EZ.F - Scala中功能全面、使用简单的服务框架
1. 设计理念
-
类库化 低侵入,虽然以“框架”称呼,但实际上是多个类库的集合,这里没有容器,不会与您的工程强耦合
-
模块化 所有功能按需选择,让您的工程尽可能轻量
-
高性能 核心模块提供同步与异步两种模型,高性能需求可选择异步模型
-
功能全面 集成了常用的服务,全栈支持
-
使用简单 各API都尽可能地统一与简化,降低使用成本
2. 功能列表
-
核心服务
-
支持自定义同步或异步的拦截器栈处理
-
支持I18N
-
-
RPC服务
-
支持HTTP和WebSocket服务
-
基于注解服务发现
-
Restful风格,支持GET POST PUT DELETE等常用方法
-
支持HTTPS
-
支持Json跨域请求
-
支持拦截器栈,可适配权限认证等服务
-
提供同步及异步HTTP客户端
-
提供CRUD脚手架服务
-
支持HTML与XML处理
-
-
存储服务
-
支持Mongo或关系型数据库(JDBC)
-
统一API
-
轻量级ORMapping,无Session处理
-
注解支持
-
-
调度服务
-
支持cron表达式的调度任务
-
支持按模块调度
-
支持JDBC或Mongo的持久化
-
-
Redis缓存服务
-
支持Redis的常用操作
-
支持同步与异步操作
-
-
基于RBAC的基础HTTP认证
-
支持RBAC的认证服务
-
支持菜单级和资源级(action)的授权
-
支持JDBC或Mongo的持久化
-
支持自助注册及密码找回
-
-
邮件服务
-
支持同步或异步的邮件发送服务
-
支持添加附件
-
-
Kakfa服务
-
Master-Slave服务
-
常用分布式服务
-
分布式计数
-
分布式锁
-
分布式Map
-
分布式阻塞和非阻塞队列
-
分布式消息发布订阅
-
分布式服务监控
-
3. 使用
通过Maven引用
<!-- 核心包 -->
<dependency>
<groupId>com.ecfront</groupId>
<artifactId>ezf-core</artifactId>
<version>3.0.0-beta2</version>
</dependency>
<!-- 各服务模块包 -->
<dependency>
<groupId>com.ecfront</groupId>
<artifactId>ezf-<服务模块></artifactId>
<version>3.0.0-beta2</version>
</dependency>
5. License
Under version 2.0 of the Apache License.
6. 持久化服务
6.1. 功能
-
支持Mongo或关系型数据库(JDBC)
-
统一API
-
轻量级ORMapping,无Session处理
-
注解支持
关系型数据库目前的测试基于MySQL,其它数据库未做详细测试 |
数据字段应避免使用null值,字符用""、数字用0(或其它)、Bool用false 等代替 |
6.2. 5分钟上手
-
添加依赖
<dependency> <groupId>com.ecfront</groupId> <artifactId>ezf-storage-<mongo或jdbc></artifactId> <version>3.0.0-beta2</version> <dependency>
-
添加配置
// Mongo "storage.mongo": { "host": "<服务IP>", "port": <服务端口>, "db_name": "<数据库名>" }
// 或 JDBC "storage.jdbc": { "driver_class": "<驱动>", "url": "<连接URL>", "user": "<用户名>", "password": "<密码>" }
-
在添加scala类
@Entity("") case class Test_Entity() extends BaseModel {
@Unique @Label("姓名") @Require @BeanProperty var name: String = _
}
// Mongo object Mongo_Test_Entity extends MongoBaseStorage[Test_Entity] //或 object JDBC_Test_Entity extends MongoBaseStorage[Test_Entity]
-
测试服务
EZManager.start()
var entity = Test_Entity() mongo.name = "name1"
// 保存并获取持久化后的id // Mongo var id = Mongo_Test_Entity.save(mongo).body.id // 或 JDBC var id = JDBC_Test_Entity.save(mongo).body.id
JDBC方式需要手工创建数据库及数据表 |
6.4. Mongo配置
"storage.mongo": { // 单个集群设置 "host" : "", (1) "port" : 27017, (2) // 多集群设置 "hosts" : [ { "host" : "cluster1", (1) "port" : (2) }, { "host" : "cluster2", (1) "port" : (2) }, ... ], db_name: "" (3) "replicaSet" : "" (4) // 连接池设置 "maxPoolSize" : 100, (5) "minPoolSize" : 0, (6) "maxIdleTimeMS" : 0, (7) "maxLifeTimeMS" : 0, (8) // 认证设置 "username" : "", (9) "password" : "", (10) ... }
1 | 服务IP或Host |
2 | 服务端口 |
3 | 数据库名 |
4 | 复制集的名字 |
5 | 最大连接数 |
6 | 最小连接数 |
7 | 一个连接的最大空闲时间 |
8 | 一个连接的最大存活时间 |
9 | 用户名 |
10 | 密码 |
本服务基于 vertx-mongo-client 实现,更多配置见:http://vertx.io/docs/vertx-mongo-client/java/#_configuring_the_client
|
6.5. JDBC配置
"storage.jdbc": { "provider_class":"io.vertx.ext.jdbc.spi.impl.C3P0DataSourceProvider" (1) // C3P0 配置 "driver_class": "", "url": "", "user": "", "password": "" ... }
1 | 连接池实现类,默认是C3P0,还支持HikariCP(io.vertx.ext.jdbc.spi.impl.HikariCPDataSourceProvider)及BoneCP(io.vertx.ext.jdbc.spi.impl.BoneCPDataSourceProvider) |
BoneCP的配置见 http://www.jolbox.com/configuration.html |
本服务基于 vertx-jdbc-client 实现,更多配置见:http://vertx.io/docs/vertx-jdbc-client/java/#_configuration
|
6.6. 使用
6.6.1. 注解及方法定义
// 使用Entity注解表示此类可以持久化(实体)
@Entity("")
// 所有实体都要直接或间接BaseModel,BaseModel添加了名为`id`的字段
// StatusModel在BaseModel的基础上添加了`enable`字段,对于表示状态的启用或禁用
// SecureModel在BaseModel的基础上添加了6个字段,对于记录操作信息:
// create_user 创建用户
// create_org 创建组织
// create_time 创建时间(yyyyMMddHHmmssSSS)
// update_user 更新用户
// update_org 更新组织
// update_time 更新时间(yyyyMMddHHmmssSSS)
case class Test_Entity() extends SecureModel with StatusModel {
// @Unique 表示唯一性,保存或更新时会做唯一性检查,可选
@Unique
// @Require 表示必填,保存或更新时会做为空检查,可选
@Require
// @Label 表示字段说明,出错时(不唯一、为空等)用于显示,可选
@Label("姓名")
// @BeanProperty 所有要持久化的字段都要加上此注解
@BeanProperty
// 字段名称及类型定义
var name: String = _
// 字段可以是复杂类型
@BeanProperty var parameters: Map[String, Any] = _
}
// 以上只是定义了实体,但没有持久化能力,要实现持久化还要添加一个间接继承自BaseStorage[E]的object
// Mongo持久化时要直接或间接继承MongoBaseStorage[E],E是要持久化的实体
// 与BaseModel一样,Storage也有StatusStorage、SecureStorage及其对应的Mongo和JDBC实现
object Mongo_Test_Entity extends MongoSecureStorage[Test_Entity] with MongoStatusStorage[Test_Entity]
// JDBC持久化时要直接或间接继承JDBCBaseStorage[E]
object JDBC_Test_Entity extends JDBCSecureStorage[Test_Entity] with JDBCStatusStorage[Test_Entity]
限制:所有实体都必须有Id字段,对JDBC而言,Id必须是自增类型(INT),对于Mongo而言则是"_id",映射到实体时Id字段统一用String类型 |
如果持久化字段是复杂类型,MySQL的版本必须是5.7及以上,建表时请用JSON 类型
|
6.6.2. 常用方法
/**
* 保存
*
* @param model 实体对象
* @param context 上下文
* @return 保存后的实体对象
*/
def save(model: M, context: EZStorageContext = EZStorageContext()): Resp[M]
/**
* 更新
*
* @param model 实体对象
* @param context 上下文
* @return 更新后的实体对象
*/
def update(model: M, context: EZStorageContext = EZStorageContext()): Resp[M]
/**
* 保存或更新
*
* @param model 实体对象
* @param context 上下文
* @return 保存或更新后的实体对象
*/
def saveOrUpdate(model: M, context: EZStorageContext = EZStorageContext()): Resp[M]
/**
* 更新
*
* @param newValues 新值,SQL (相当于SET中的条件)或Json
* @param condition 条件,SQL (相当于Where中的条件)或Json
* @param parameters 参数 ,Mongo不需要
* @param context 上下文
* @return 是否成功
*/
def updateByCond(newValues: String, condition: String, parameters: List[Any] = List(), context: EZStorageContext = EZStorageContext()): Resp[Void]
/**
* 删除
*
* @param id 主键
* @param context 上下文
* @return 是否成功
*/
def deleteById(id: Any, context: EZStorageContext = EZStorageContext()): Resp[Void]
/**
* 删除
*
* @param condition 条件,SQL (相当于Where中的条件)或Json
* @param parameters 参数 ,Mongo不需要
* @param context 上下文
* @return 是否成功
*/
def deleteByCond(condition: String, parameters: List[Any] = List(), context: EZStorageContext = EZStorageContext()): Resp[Void]
/**
* 获取一条记录
*
* @param id 主键
* @param context 上下文
* @return 获取到的记录
*/
def getById(id: Any, context: EZStorageContext = EZStorageContext()): Resp[M]
/**
* 获取一条记录
*
* @param condition 条件,SQL (相当于Where中的条件)或Json
* @param parameters 参数 ,Mongo不需要
* @param context 上下文
* @return 获取到的记录
*/
def getByCond(condition: String, parameters: List[Any] = List(), context: EZStorageContext = EZStorageContext()): Resp[M]
/**
* 判断是否存在
*
* @param id 主键
* @param context 上下文
* @return 是否存在
*/
def existById(id: Any, context: EZStorageContext = EZStorageContext()): Resp[Boolean]
/**
* 判断是否存在
*
* @param condition 条件,SQL (相当于Where中的条件)或Json
* @param parameters 参数 ,Mongo不需要
* @param context 上下文
* @return 是否存在
*/
def existByCond(condition: String, parameters: List[Any] = List(), context: EZStorageContext = EZStorageContext()): Resp[Boolean]
/**
* 查找
*
* @param condition 条件,SQL (相当于Where中的条件)或Json
* @param parameters 参数 ,Mongo不需要
* @param context 上下文
* @return 查找结果
*/
def find(condition: String, parameters: List[Any] = List(), context: EZStorageContext = EZStorageContext()): Resp[List[M]]
/**
* 分页
*
* @param condition 条件,SQL (相当于Where中的条件)或Json
* @param parameters 参数 ,Mongo不需要
* @param pageNumber 当前页,从1开始
* @param pageSize 每页条数
* @param context 上下文
* @return 分页结果
*/
def page(condition: String, parameters: List[Any] = List(), pageNumber: Long = 1, pageSize: Int = 10,
context: EZStorageContext = EZStorageContext()): Resp[Page[M]]
/**
* 计数
*
* @param condition 条件,SQL (相当于Where中的条件)或Json
* @param parameters 参数 ,Mongo不需要
* @param context 上下文
* @return 条数
*/
def count(condition: String, parameters: List[Any] = List(), context: EZStorageContext = EZStorageContext()): Resp[Long]
/**
* 获取一条启用的记录
*
* @param condition 条件,SQL (相当于Where中的条件)或Json
* @param parameters 参数 ,Mongo不需要
* @param context 上下文
* @return 获取到的记录
*/
def getEnabledByCond(condition: String, parameters: List[Any] = List(), context: EZStorageContext = null): Resp[M]
/**
* 启用记录查找
*
* @param condition 条件,SQL (相当于Where中的条件)或Json
* @param parameters 参数 ,Mongo不需要
* @param context 上下文
* @return 查找到的记录
*/
def findEnabled(condition: String, parameters: List[Any] = List(), context: EZStorageContext = null): Resp[List[M]]
/**
* 启用记录分页
*
* @param condition 条件,SQL (相当于Where中的条件)或Json
* @param parameters 参数 ,Mongo不需要
* @param pageNumber 当前页,从1开始
* @param pageSize 每页条数
* @param context 上下文
* @return 分页结果
*/
def pageEnabled(
condition: String,
parameters: List[Any] = List(),
pageNumber: Long = 1, pageSize: Int = 10, context: EZStorageContext = null): Resp[Page[M]]
/**
* 判断启用记录是否存在
*
* @param condition 条件,SQL (相当于Where中的条件)或Json
* @param parameters 参数 ,Mongo不需要
* @param context 上下文
* @return 是否存在
*/
def existEnabledByCond(condition: String, parameters: List[Any] = List(), context: EZStorageContext = null): Resp[Boolean]
/**
* 启用记录计数
*
* @param condition 条件,SQL (相当于Where中的条件)或Json
* @param parameters 参数 ,Mongo不需要
* @param context 上下文
* @return 条数
*/
def countEnabled(condition: String, parameters: List[Any] = List(), context: EZStorageContext = null): Resp[Long]
/**
* 启用一条记录
*
* @param id 主键
* @param context 上下文
* @return 启用结果
*/
def enableById(id: Any, context: EZStorageContext = null): Resp[Void]
/**
* 禁用一条记录
*
* @param id 主键
* @param context 上下文
* @return 禁用结果
*/
def disableById(id: Any, context: EZStorageContext = null): Resp[Void]
以上所有方法都有preX 和postX 方法对,可以在实现的storage中重写以用于操作前及操作后处理,系统仅对操作前返回Resp.success() 的结果做后续操作
|
Mongo特殊方法
/**
* 附加条件查找
* @param condition 过滤条件
* @param sort 排序
* @param limit 获取记录数,默认为0,表示获取所有
* @param context 上下文
* @return 查找结果
*/
def findWithOpt(condition: String = "{}", sort: Map[String, SortEnum], limit: Int = 0, context: EZStorageContext = EZStorageContext()): Resp[List[M]]
/**
* 附加条件分页
* @param condition 过滤条件
* @param pageNumber 当前页,从1开始
* @param pageSize 每页条数
* @param sort 排序
* @param context 上下文
* @return 分页结果
*/
def pageWithOpt(
condition: String = "{}", pageNumber: Long = 1, pageSize: Int = 10,
sort: Map[String, SortEnum] = Map(), context: EZStorageContext = null): Resp[Page[M]]
/**
* 聚合计算
*
* 例如:
* [{ "$$match": {<过滤条件>} },
* {
* // Group
* "$$group": {
* "_id": {
* "platform":"$$platform",
* "component":"$$component",
* "module":"$$module",
* "stage":"$$stage"
* },
* "count": { "$$sum": 1 }
* }
* }]
* @param condition 计算条件
* @param context 上下文
* @return 计算结果
*/
def aggregate(condition: JsonArray, context: EZStorageContext = null): Resp[JsonArray]
6.6.3. 适配器使用
当业务模块需要可选择支持Mongo或JDBC时可以用适配器,以减少业务编码
// 先定义一个基础持久化类,可继承BaseStorage[E]、StatusStorage[E]或SecureStorage[E]
// 此类不能混入Mongo或JDBC信息
trait EZ_Role_Base extends SecureStorage[EZ_Role] with StatusStorage[EZ_Role] {
// 这里可以重写前置或后置方法
override def preSaveOrUpdate(model: EZ_Role, context: EZStorageContext): Resp[EZ_Role] = {
if (model.flag == null || model.flag.trim.isEmpty) {
Resp.badRequest("Require【flag】")
} else {
if (model.flag.contains(BaseModel.SPLIT)) {
Resp.badRequest(s"【flag】can't contains ${BaseModel.SPLIT}")
} else {
model.code = assembleCode(model.flag, model.organization_code)
super.preSaveOrUpdate(model, context)
}
}
}
// 可以添加自定义方法
def findByOrganizationCode(organizationCode: String): Resp[List[EZ_Role]]
}
// 用Mongo实现自定义方法
object EZ_Role_Mongo extends MongoSecureStorage[EZ_Role] with MongoStatusStorage[EZ_Role] with EZ_Role_Base {
override def findByOrganizationCode(organizationCode: String): Resp[List[EZ_Role]] = {
find(s"""{"organization_code":"$organizationCode"}""")
}
}
// 用JDBC实现自定义方法
object EZ_Role_JDBC extends JDBCSecureStorage[EZ_Role] with JDBCStatusStorage[EZ_Role] with EZ_Role_Base {
override def findByOrganizationCode(organizationCode: String): Resp[List[EZ_Role]] = {
find(s"""organization_code = ?""", List(organizationCode))
}
}
// 添加适配器类,每个Storage都有对应的Adapter
// Adapter[E,T]两个泛型分别代表 实体及对应的基础持久化类
object EZ_Role extends SecureStorageAdapter[EZ_Role, EZ_Role_Base]
with StatusStorageAdapter[EZ_Role, EZ_Role_Base] with EZ_Role_Base {
// 重写storageObj属性,根据外部条件选择使用Mongo或JDBC
override protected val storageObj: EZ_Role_Base =
if (<外部条件>) EZ_Role_Mongo else EZ_Role_JDBC
// 重写基础持久化类中对应的自定义方法,应用对应的方法
override def findByOrganizationCode(organizationCode: String): Resp[List[EZ_Role]] = storageObj.findByOrganizationCode(organizationCode)
}
6.6.4. Mongo低层API使用
同步操作
/**
* 保存
*
* @param collection 集合名
* @param save 保存的Json对象
* @return 保存结果
*/
def save(collection: String, save: JsonObject): Resp[String]
/**
* 更新
*
* @param collection 集合名
* @param id 要更新的_id
* @param update 更新的Json对象
* @return 更新结果
*/
def update(collection: String, id: String, update: JsonObject): Resp[String]
/**
* 保存或更新,存在主键做保存,反之更新
*
* @param collection 集合名
* @param saveOrUpdate 保存或更新的Json对象
* @return 保存或更新结果
*/
def saveOrUpdate(collection: String, saveOrUpdate: JsonObject): Resp[String]
/**
* 更新
*
* @param collection 集合名
* @param query 更新条件,Json格式
* @param update 更新的Json对象
* @return 更新结果
*/
def updateByCond(collection: String, query: JsonObject, update: JsonObject): Resp[Void]
/**
* 删除
*
* @param collection 集合名
* @param query 删除条件,Json格式
* @return 删除结果
*/
def deleteByCond(collection: String, query: JsonObject): Resp[Void]
/**
* 删除
*
* @param collection 集合名
* @param id 删除_id
* @return 删除结果
*/
def deleteById(collection: String, id: String): Resp[Void]
/**
* 计数
*
* @param collection 集合名
* @param query 计数条件,Json格式
* @return 计数结果
*/
def count(collection: String, query: JsonObject): Resp[Long]
/**
* 获取一条记录
*
* @param collection 集合名
* @param id 记录_id
* @param resultClass 记录类型
* @tparam E 记录类型
* @return 获取到的记录
*/
def getById[E](collection: String, id: String, resultClass: Class[E]): Resp[E]
/**
* 获取一条记录
*
* @param collection 集合名
* @param query 获取条件,Json格式
* @param resultClass 记录类型
* @tparam E 记录类型
* @return 获取到的记录
*/
def getByCond[E](collection: String, query: JsonObject, resultClass: Class[E]): Resp[E]
/**
* 查找
*
* @param collection 集合名
* @param query 查找条件,Json格式
* @param sort 排序方式
* @param limit 获取条数
* @param resultClass 记录类型
* @tparam E 记录类型
* @return 获取到的记录
*/
def find[E](collection: String, query: JsonObject, sort: JsonObject, limit: Int, resultClass: Class[E]): Resp[List[E]]
/**
* 分页
*
* @param collection 集合名
* @param query 分页条件,Json格式
* @param pageNumber 当前页,从1开始
* @param pageSize 每页条数
* @param sort 排序方式
* @param resultClass 记录类型
* @tparam E 记录类型
* @return 获取到的记录
*/
def page[E](collection: String, query: JsonObject, pageNumber: Long, pageSize: Int, sort: JsonObject, resultClass: Class[E]): Resp[Page[E]]
/**
* 判断是否存在
*
* @param collection 集合名
* @param query 是否存在条件,Json格式
* @return 是否存在
*/
def exist(collection: String, query: JsonObject): Resp[Boolean]
/**
* 聚合操作
*
* @param collection 集合名
* @param query 聚合条件,Json格式
* @return 操作结果
*/
def aggregate(collection: String, query: JsonArray): Resp[JsonArray]
异步操作
/**
* 保存
*
* @param collection 集合名
* @param save 保存的Json对象
* @return 保存结果
*/
def save(collection: String, save: JsonObject): Future[Resp[String]]
/**
* 更新
*
* @param collection 集合名
* @param id 要更新的_id
* @param update 更新的Json对象
* @return 更新结果
*/
def update(collection: String, id: String, update: JsonObject): Future[Resp[String]]
/**
* 保存或更新,存在主键做保存,反之更新
*
* @param collection 集合名
* @param saveOrUpdate 保存或更新的Json对象
* @return 保存或更新结果
*/
def saveOrUpdate(collection: String, saveOrUpdate: JsonObject): Future[Resp[String]]
/**
* 更新
*
* @param collection 集合名
* @param query 更新条件,Json格式
* @param update 更新的Json对象
* @return 更新结果
*/
def updateByCond(collection: String, query: JsonObject, update: JsonObject): Future[Resp[Void]]
/**
* 删除
*
* @param collection 集合名
* @param query 删除条件,Json格式
* @return 删除结果
*/
def deleteByCond(collection: String, query: JsonObject): Future[Resp[Void]]
/**
* 删除
*
* @param collection 集合名
* @param id 删除_id
* @return 删除结果
*/
def deleteById(collection: String, id: String): Future[Resp[Void]]
/**
* 计数
*
* @param collection 集合名
* @param query 计数条件,Json格式
* @return 计数结果
*/
def count(collection: String, query: JsonObject): Future[Resp[Long]]
/**
* 获取一条记录
*
* @param collection 集合名
* @param id 记录_id
* @param resultClass 记录类型
* @tparam E 记录类型
* @return 获取到的记录
*/
def getById[E](collection: String, id: String, resultClass: Class[E]): Future[Resp[E]]
/**
* 获取一条记录
*
* @param collection 集合名
* @param query 获取条件,Json格式
* @param resultClass 记录类型
* @tparam E 记录类型
* @return 获取到的记录
*/
def getByCond[E](collection: String, query: JsonObject, resultClass: Class[E]): Future[Resp[E]]
/**
* 查找
*
* @param collection 集合名
* @param query 查找条件,Json格式
* @param sort 排序方式
* @param limit 获取条数
* @param resultClass 记录类型
* @tparam E 记录类型
* @return 获取到的记录
*/
def find[E](collection: String, query: JsonObject, sort: JsonObject, limit: Int, resultClass: Class[E]): Future[Resp[List[E]]]
/**
* 分页
*
* @param collection 集合名
* @param query 分页条件,Json格式
* @param pageNumber 当前页,从1开始
* @param pageSize 每页条数
* @param sort 排序方式
* @param resultClass 记录类型
* @tparam E 记录类型
* @return 获取到的记录
*/
def page[E](collection: String, query: JsonObject, pageNumber: Long, pageSize: Int, sort: JsonObject, resultClass: Class[E]): Future[Resp[Page[E]]]
/**
* 判断是否存在
*
* @param collection 集合名
* @param query 是否存在条件,Json格式
* @return 是否存在
*/
def exist(collection: String, query: JsonObject): Future[Resp[Boolean]]
/**
* 聚合操作
*
* @param collection 集合名
* @param query 聚合条件,Json格式
* @return 操作结果
*/
def aggregate(collection: String, query: JsonArray): Future[Resp[JsonArray]]
6.6.5. JDBC低层API使用
同步操作
/**
* update
*
* @param sql sql
* @param parameters 参数
* @param conn 已存在的connection,为空时会新建
* @return update结果
*/
def update(sql: String, parameters: List[Any] = null, conn: SQLConnection = null): Resp[Void]
/**
* 批处理
*
* @param sql sql
* @param parameterList 参数列表
* @param conn 已存在的connection,为空时会新建
* @return 处理结果
*/
def batch(sql: String, parameterList: List[List[Any]] = null, conn: SQLConnection = null): Resp[Void]
/**
* 获取一条记录
*
* @param sql sql
* @param parameters 参数
* @param resultClass 记录类型
* @param conn 已存在的connection,为空时会新建
* @tparam E 记录类型
* @return 获取到的记录
*/
def get[E](sql: String, parameters: List[Any], resultClass: Class[E], conn: SQLConnection = null): Resp[E]
/**
* 查找
*
* @param sql sql
* @param parameters 参数
* @param resultClass 记录类型
* @param conn 已存在的connection,为空时会新建
* @tparam E 记录类型
* @return 获取到的记录
*/
def find[E](sql: String, parameters: List[Any], resultClass: Class[E], conn: SQLConnection = null): Resp[List[E]]
/**
* 分页
*
* @param sql sql
* @param parameters 参数
* @param pageNumber 当前页,从1开始
* @param pageSize 每页条数
* @param resultClass 记录类型
* @param conn 已存在的connection,为空时会新建
* @tparam E 记录类型
* @return 获取到的记录
*/
def page[E](sql: String, parameters: List[Any], pageNumber: Long, pageSize: Int, resultClass: Class[E], conn: SQLConnection = null): Resp[Page[E]]
/**
* 计数
*
* @param sql sql
* @param parameters 参数
* @param conn 已存在的connection,为空时会新建
* @return 计数结果
*/
def count(sql: String, parameters: List[Any], conn: SQLConnection = null): Resp[Long]
/**
* 判断是否存在
*
* @param sql sql
* @param parameters 参数
* @param conn 已存在的connection,为空时会新建
* @return 是否存在
*/
def exist(sql: String, parameters: List[Any], conn: SQLConnection = null): Resp[Boolean]
/**
* 开始事务
*
* @return 当前事务的连接信息
*/
def openTx(): SQLConnection
/**
* 回滚事务
*
* @param conn 当前事务的连接信息
*/
def rollback(conn: SQLConnection): Unit
/**
* 提交事务
*
* @param conn 当前事务的连接信息
*/
def commit(conn: SQLConnection): Unit
异步操作
/**
* update
*
* @param sql sql
* @param parameters 参数
* @param conn 已存在的connection,为空时会新建
* @return update结果
*/
def update(sql: String, parameters: List[Any] = null, conn: SQLConnection = null): Future[Resp[Void]]
/**
* 批处理
*
* @param sql sql
* @param parameterList 参数列表
* @param conn 已存在的connection,为空时会新建
* @return 处理结果
*/
def batch(sql: String, parameterList: List[List[Any]], conn: SQLConnection = null): Future[Resp[Void]]
/**
* 获取一条记录
*
* @param sql sql
* @param parameters 参数
* @param resultClass 记录类型
* @param conn 已存在的connection,为空时会新建
* @tparam E 记录类型
* @return 获取到的记录
*/
def get[E](sql: String, parameters: List[Any], resultClass: Class[E], conn: SQLConnection = null): Future[Resp[E]]
/**
* 查找
*
* @param sql sql
* @param parameters 参数
* @param resultClass 记录类型
* @param conn 已存在的connection,为空时会新建
* @tparam E 记录类型
* @return 获取到的记录
*/
def find[E](sql: String, parameters: List[Any], resultClass: Class[E], conn: SQLConnection = null): Future[Resp[List[E]]]
/**
* 分页
*
* @param sql sql
* @param parameters 参数
* @param pageNumber 当前页,从1开始
* @param pageSize 每页条数
* @param resultClass 记录类型
* @param conn 已存在的connection,为空时会新建
* @tparam E 记录类型
* @return 获取到的记录
*/
def page[E](sql: String, parameters: List[Any], pageNumber: Long, pageSize: Int,
resultClass: Class[E], conn: SQLConnection = null): Future[Resp[Page[E]]]
/**
* 计数
*
* @param sql sql
* @param parameters 参数
* @param conn 已存在的connection,为空时会新建
* @return 计数结果
*/
def count(sql: String, parameters: List[Any], conn: SQLConnection = null): Future[Resp[Long]]
/**
* 判断是否存在
*
* @param sql sql
* @param parameters 参数
* @param conn 已存在的connection,为空时会新建
* @return 是否存在
*/
def exist(sql: String, parameters: List[Any], conn: SQLConnection = null): Future[Resp[Boolean]]
/**
* 开始事务
*
* @return 当前事务的连接信息
*/
def openTx(): Future[SQLConnection]
/**
* 回滚事务
*
* @param conn 当前事务的连接信息
* @return 是否成功
*/
def rollback(conn: SQLConnection): Future[Void]
/**
* 提交事务
*
* @param conn 当前事务的连接信息
* @return 是否成功
*/
def commit(conn: SQLConnection): Future[Void]
使用JDBCProcessor.tx
实现事务快捷操作
JDBCProcessor.tx{
// your code (1)
}
1 | 业务代码,可以跨类和方法,但必须在同一线程中(其它线程的代码不会使用当前事务) |
7. RPC服务
7.1. 功能
-
支持HTTP和WebSocket服务
-
基于注解服务发现
-
Restful风格,支持GET POST PUT DELETE等常用方法
-
支持HTTPS
-
支持Json跨域请求
-
支持拦截器栈,可适配权限认证等服务
-
提供同步及异步HTTP客户端
-
提供CRUD脚手架服务
-
支持HTML与XML处理
-
支持HTTP慢请求筛选
-
支持HTTP DDoS过滤
7.2. 5分钟上手
-
添加依赖
<dependency> <groupId>com.ecfront</groupId> <artifactId>ezf-rpc-<http或websocket></artifactId> <version>3.0.0-beta2</version> <dependency>
-
添加配置
// HTTP "rpc.http": { "servicePath": "com.foo.api" }
// 或 WebSocket "rpc.websocket": { "servicePath": "com.foo.api" }
-
在
com.foo.api
包下添加scala类@RPC("/test/") @HTTP 或 @WebSocket object TestService { @POST("") 或 @REQUEST("") def getTest(parameter: Map[String, String],body:String, context: EZRPCContext): Resp[String] = { Resp.success(String) } }
-
启动服务
EZManager.start()
-
访问
// HTTP POST http://127.0.0.1/test/" body: "Hello World" 返回 "Hello World"
// 或 WebSocket 写个HTML页面 <html> <head></head> <script> var socket_resource; if (window.WebSocket) { socket= new WebSocket("ws://localhost/test/"); socket.onmessage = function (event) { console.log("received : " + JSON.parse(event.data)); }; socket.onopen = function (event) { console.log("opened!"); }; socket.onclose = function (event) { console.log("closed."); }; } else { alert("Your browser does not support Websockets. (Use Chrome)"); } function send() { socket.send(JSON.stringify({ f1: "1111" })); } </script> <body> <input id="btnSend" type="button" value="POST" onclick="send()"/> </body> </html> 打开测试,OK
7.4. HTTP配置
"rpc.http": { "servicePath": "", (1) "host": "127.0.0.1", (2) "port": 80/443, (3) "publicUrl": "", (4) "webUrl": "", (5) "resourcePath": "/tmp/", (6) "accessControlAllowOrigin": "*", (7) "ssl": { (8) "keyPath": "", (9) "keyPassword": "" (10) }, monitor": { (11) "slow": {(12) "time": 10000, (13) "includes": [], (14) "excludes": [] (15) } }, "antiDDoS":{ (16) "reqRatePerMinute":0, (17) "illegalReqRatePerMinute":0 (18) } }
1 | HTTP服务发现的根包,系统会以此包为基础,扫描子孙路径下所有带@RPC及@HTTP的类,并将其类中带@GET @POST @PUT @DELETE注解的方法加入到路由表中 |
2 | IP或主机名 |
3 | 端口号,HTTP默认80,HTTPS默认443 |
4 | 对外开放的URL,默认是 http(s)://host:port/ ,服务需要对外发布、代理转发等场景下可以配置此项向外暴露服务,在用户注册激活及找回密码等功能时会发送以此URL开头的链接 |
5 | Web页面的URL,默认同publicUrl ,此配置用于用户注册激活及找回密码等功能时跳转到登录页面时做为登录登录URL的前缀路径 |
6 | 资源目录,上传文件的基础目录 |
7 | 跨域请求允许的来源域,*表示允许所有域 |
8 | SSL配置 |
9 | SSL证书路径,可以是绝对路径,也可以是相对路径 |
10 | SSL证书密码 |
11 | 是否使用请求监控 |
12 | 是否使用慢请求监控 |
13 | 慢请求时间定义,请求大于此时间会记录到日志,单位毫秒 |
14 | 包含的请求URI,如果此字段存在,那么只会记录此URI中的请求,格式:method:uri |
15 | 排除的请求URI,如果此字段存在,那么在此URI中的请求都不会记录,格式:method:uri |
16 | DDoS过滤 |
17 | 同一IP每分钟最大请求数 |
18 | 同一IP每分钟最大非法请求数(如查找不存在的方法) |
资源目录(resourcePath),需要禁用此目录及子孙目录的执行权限 |
SSL证书路径为相对路径时,它的根路径查找顺序为 1)启动参数中conf指定的路径,2)当前的classPath,3)./config/ |
includes 和excludes 是排他的,当includes 不为空时excludes 将失效
|
7.5. WebSocket配置
"rpc.websocket": { "servicePath": "", (1) "host": "127.0.0.1", (2) "port": 80 (3) }
1 | WebSocket服务发现的根包,系统会以此包为基础,扫描子孙路径下所有带@RPC及@WebSocket的类,并将其类中带@REQUEST注解的方法加入到路由表中 |
2 | IP或主机名 |
3 | 端口号 |
7.6. 使用
7.6.1. HTTP服务注解及方法定义
@RPC("/test/") // 指定RPC注解,参数`/test/`表示uri的根路径
@HTTP // 指定HTTP注解,表示此类可提供HTTP服务
object DemoService { // 服务可以是object或class
// 此注解表示此方法对应于HTTP的GET请求
// 参数为空表示请求的路径是就是根据路径,可接收的请求为 GET /test/
// 如果参数是 @GET("a/b/c/") 则可接收的请求为 GET /test/a/b/c/
// 如果要重写根据路径参数以'/'开始即可,如 @GET("/a/b/c/") 则可接收的请求为 GET /a/b/c/
// 如果参数中带有变量使用':'指定,如 @GET("a/:b/:c/") 则可接收的请求为 GET /test/a/111/222/,方法参数parameter会映射b与c的值
@GET("")
// 对于 @GET 和 @DELETE 请求而言,方法的行参有两个
// 第一个类型是Map[String,String],用于保存URL中的变量及search值,如 @GET(":id/"),请求为 GET /test/111/?token=sss,则此参数的值为 Map("id" -> "111","token" -> "sss")
// 第二个类型是EZRPCContext及其子类,保存了请求上下文
// 请求上下文的参数有:
// 请求方法 method
// 请求对应的模块URI(可能带通配符) templateUri
// 请求的真实URI realUri
// 请求URL中的参数 parameters
// 远程IP remoteIP
// 请求的Accept accept
// 请求的ContentType contentType
// 返回值必须为Resp封装的类型
// Resp中有code,message及body三个属性:
// code是返回的业务状态码,与HTTP类似,200表示成功
// message是错误的消息描述
// body是成功返回的真实对象,在context-Type 是 `*/json`时会将对象转成Json字符串格式返回
// 几个特殊返回类型:
// Resp[File] 返回下载文件流
// Resp[RespRedirect] 页面重定向
// Resp[Raw] 返回未封装的原始对象
// Resp[Document] 当context-Type 是 `text/xml` 时用于返回xml信息(去除Resp封装),xml可由Jsoup处理
def getTest(parameter: Map[String, String],context: EZRPCContext): Resp[String] = {
//Resp有多个方法,success表示成功,还有诸如notFound、badRequest、unAuthorized等预定义的错误
Resp.success("Hello World")
}
// 此注解表示此方法对应于HTTP的POST请求
@POST("")
// 对于 @POST 和 @PUT 请求而言,方法的行参有三个
// 第一个和第三个同 @GET 或 @DELETE 请求
// 第二是任意类型*,系统会将请求body解析成对应的对象
def getTest(parameter: Map[String, String],body:String, context: EZRPCContext): Resp[String] = {
Resp.success(String)
}
}
目前POST及PUT请求体不支持直接解析自定义泛型对象,内置类型泛型如Map[String,String]没有问题,但如List[EZ_Resource]这些自定义类型做为泛型时请先使用string(body:String)接收,然后使用JsonHelper.toObject[List[EZ_Resource]](body)转换 |
URI必须严格对应,如 /test 与 /test/ 不是同一个路径
|
URI变量以 : 开头,变量名只能是字母或数字
|
7.6.2. WebSocket服务注解及方法定义
@RPC("/test/") // 指定RPC注解,参数`/test/`表示uri的根路径
@WebSocket // 指定WebSocket注解,表示此类可提供WebSocket服务
object DemoService {
// 此注解表示此方法支持WebSocket请求,方法形参与HTTP的@POST类似
@REQUEST("")
def getTest(parameter: Map[String, String],body:String,context: EZRPCContext): Resp[String] = {
Resp.success("Hello World")
}
}
7.6.3. SimpleHttpService脚手架
为简化常规的CRUD应用,可继承SimpleHttpService,此类中已定义了基本的CRUD、上传、下载、导出等操作
@RPC("/resource/")
@HTTP
// 继承SimpleHttpService以实现基础服务
// SimpleHttpService第一个泛型指定操作的实体类型,第二个泛型指定上下文类型
object ResourceService extends SimpleHttpService[EZ_Resource, EZRPCContext] {
// 此属性指定持久化的实现,这里使用EZ_Resource的伴生类
override protected val storageObj: BaseStorage[EZ_Resource] = EZ_Resource
}
// 这里一个实体,带有两个属性,使用详见`storage`服务
@Entity("Resource")
case class EZ_Resource() extends BaseModel with StatusModel{
@BeanProperty var method: String = _
@BeanProperty var uri: String = _
}
// 此伴生类表明此实体可以持久化到Mongo,使用详见`storage`服务
object EZ_Resource extends MongoBaseStorage[EZ_Resource] with MongoStatusStorage[EZ_Resource]
上述操作后实现的HTTP服务有:
添加新资源
POST /resource/ body {"method":"","uri":""}
更新已有资源
PUT /resource/<资源id>/ body {"id":"","method":"","uri":""}
获取一个资源
GET /resource/<资源id>/
删除一个资源
DELETE /resource/<资源id>/
查找资源列表
GET /resource/?condition=<查找条件,sql或mongo json> condition可选
查找启用资源列表
GET /resource/enable/?condition=<查找条件,sql或mongo json> condition可选
分页查找资源列表
GET /resource/page/<当前页,从1开始>/<每页显示条数>/?condition=<查找条件,sql或mongo json> condition可选
启用一个资源
GET /resource/<资源id>/enable/
仅在实体继承StatusModel时有效 |
禁用一个资源
GET /resource/<资源id>/disable/
仅在实体继承StatusModel时有效 |
导出资源列表
GET /resource/export/
默认会导出所有字段,可能会引发数据安全问题,重写 override protected def allowExportFields = List(<可以导出的字段>) 可以选择导出字段 |
重写 override protected def allowExport = false 可以禁用导出功能 |
上传文件
POST /resource/res/
重写 override protected def allowUpload = false 可以禁用上传功能 |
重写 override protected def allowUploadTypes 可以选择允许上传的类型,如 allowUploadTypes=List(FileType.TYPE_COMPRESS, FileType.TYPE_IMAGE, FileType.TYPE_OFFICE)表示可以上传压缩、图片、Office文档类型 |
上传文件到:配置文件中的resourcePath + 当前实体的名称 + File.separator + 当前日期(yyyyMMdd) + File.separator |
下载文件
GET <`上传文件`中返回的uri>
7.6.4. HTTP客户端
同步操作
/**
* GET 请求
*
* @param url 请求URL
* @param contentType 请求类型,默认为 application/json; charset=utf-8
* @return 请求结果,string类型
*/
def get(url: String, contentType: String = "application/json; charset=utf-8"): String
/**
* POST 请求
*
* @param url 请求URL
* @param body 请求体
* @param contentType 请求类型,默认为 application/json; charset=utf-8
* @return 请求结果,string类型
*/
def post(url: String, body: Any, contentType: String = "application/json; charset=utf-8"): String
/**
* PUT 请求
*
* @param url 请求URL
* @param body 请求体
* @param contentType 请求类型,默认为 application/json; charset=utf-8
* @return 请求结果,string类型
*/
def put(url: String, body: Any, contentType: String = "application/json; charset=utf-8"): String
/**
* DELETE 请求
*
* @param url 请求URL
* @param contentType 请求类型,默认为 application/json; charset=utf-8
* @return 请求结果,string类型
*/
def delete(url: String, contentType: String = "application/json; charset=utf-8"): String
异步操作
/**
* GET 请求
*
* @param url 请求URL
* @param contentType 请求类型,默认为 application/json; charset=utf-8
* @return 请求结果,string类型
*/
def get(url: String, contentType: String = "application/json; charset=utf-8"): Future[String]
/**
* POST 请求
*
* @param url 请求URL
* @param body 请求体
* @param contentType 请求类型,默认为 application/json; charset=utf-8
* @return 请求结果,string类型
*/
def post(url: String, body: Any, contentType: String = "application/json; charset=utf-8"): Future[String]
/**
* PUT 请求
*
* @param url 请求URL
* @param body 请求体
* @param contentType 请求类型,默认为 application/json; charset=utf-8
* @return 请求结果,string类型
*/
def put(url: String, body: Any, contentType: String = "application/json; charset=utf-8"): Future[String]
/**
* DELETE 请求
*
* @param url 请求URL
* @param contentType 请求类型,默认为 application/json; charset=utf-8
* @return 请求结果,string类型
*/
def delete(url: String, contentType: String = "application/json; charset=utf-8"): Future[String]
7.6.5. WebSocket消息推送管理
/**
* 向所有客户端推送消息
*
* @param method 连接方法,目前只限于 `REQUEST` 方法
* @param path 连接路径
* @param data 消息
*/
def ws(method: String, path: String, data: Any): Unit
/**
* 移除推送消息
*
* @param method 连接方法,目前只限于 `REQUEST` 方法
* @param path 连接路径
* @param matchAll 是否匹配全路径,为false时只按前缀匹配
*/
def remove(method: String, path: String, matchAll: Boolean = true): Unit
8. 认证服务
8.2. 5分钟上手
-
添加依赖
<dependency> <groupId>com.ecfront</groupId> <artifactId>ezf-auth</artifactId> <version>3.0.0-beta2</version> <dependency>
-
添加配置
"auth": {}, "storage.mongo": { "host": "<服务IP>", "port": 27017, "db_name": "test" }, "rpc.http": { "host": "0.0.0.0", "port": 8080 }, "distributed":{}, "redis": { "host": "<服务IP>", "port": 6379 }
-
启动服务
EZManager.start()
-
登录
POST http://127.0.0.1:8080/public/auth/login/" body: {"id":"admin","password":"admin"} 返回登录信息
8.3. 依赖
服务依赖:rpc.http、redis、distributed、storage.mongo或storage.jdbc、mail(可选,允许注册或开放找回密码功能时必选)
环境依赖:redis、mongo或mysql
8.4. 配置
"auth": {
"allowRegister": false, (1)
"customLogin": false, (2)
"selfActive": true, (3)
"defaultRoleFlag": "user", (4)
"defaultOrganizationCode": "", (5)
"publicUriPrefix": "/public/", (6)
"loginUrl": "#/auth/login", (7)
"loginKeepSeconds": 0, (8)
"activeKeepSeconds": 86400, (9)
"extAccountStorage": "", (10)
"useRelTable": false, (11)
"customTables": { (12)
"organization": "", (13)
"account": "", (14)
"resource": "", (15)
"role": "", (16)
"menu": "", (17)
"rel_account_role": "", (18)
"rel_role_resource": "", (19)
"rel_menu_role": "" (20)
},
"storage":"mongo", (21)
"loginLimit": { (22)
"showCaptcha":2147483647 (23)
},
}
1 | 是否允许注册,注册后账户处于禁用状态,需要激活 |
2 | 是否启用自定义登录,如果为true的话默认登录URL会被禁用 |
3 | 是否允许自助激活,true:会发送激活邮件自助激活,false:需要管理员手工激活 |
4 | 默认角色标识,注册时账户默认的角色 |
5 | 默认组织编码,注册时账户默认的组织 |
6 | 公开URI的前缀,以此前缀开头的URI不需要认证 |
7 | 登录URL,注册或找回密码激活成功后会跳转到登录URL,如果此配置值不带http前缀则使用rpc-http 中webUrl 做为基础路径 |
8 | 登录保持时间,单位秒,指定登录会话多少秒后过期,0表示不过期 |
9 | 注册或找回密码激活有效期,单位秒 |
10 | 扩展账号持久化类路径,可选,默认对于扩展的账号信息存储在ez_account 表中的ext_info 字段中,如果需要将扩展信息存储在独立表中时可以定义一个继承自BaseModel 的实体及对应的持久化实现,然后指定其类路径 |
11 | 是否使用关联表(多对多关联中间表),多用于JDBC持久化,建议在原生不支持json的关系型数据启用此项 |
12 | 是否使用自定义表名 |
13 | 自定义组织表名 |
14 | 自定义账户表名 |
15 | 自定义资源表名 |
16 | 自定义角色表名 |
17 | 自定义菜单表名 |
18 | 自定义账户角色关联表名 |
19 | 自定义角色资源关联表名 |
20 | 自定义菜单角色关联表名 |
21 | 持久化实现,支持mongo 或jdbc |
22 | 登录限制 |
23 | 在连续多次登录失败后显示验证码 |
storage:jdbc & useRelTable不存在或等于false 时请执行ez_ddl.sql 以创建基础表,storage:jdbc & useRelTable=true 时请执行ez_ddl_with_rel.sql 以创建带关联表的基础表
|
8.5. 认证服务接入
在原有HTTP服务方法中修改 EZRPCContext
为 EZAuthContext
即可,如
@POST("")
def testAuth(parameter: Map[String, String], body: Account_VO, context: EZAuthContext): Resp[Void]
EZAuthContext在EZRPCContext基础上添加了两个字段:
-
token 认证的Token值
-
loginInfo 登录信息
8.6. 事件
8.6.1. 初始化新组织事件
ServiceAdapter.ezEvent_organizationInit.<subscribe | subscribeOneNode>({
orgCode:String =>
// your code
})
8.7. 预定义认证接口调用
8.7.1. 说明
-
无特殊说明的情况下所有请求的header Content-Type为application/json
-
无特殊说明的情况下所有返回值均为Json,由以下格式构成:
{ "code":"<状态码,200表示成功,其它表示失败>", "message":"<消息,多在出现失败时显示失败原因>", "body":"<返回主体内容,不同接口内容不同>" }
-
如果是分页查询,则返回格式构成如下:
{ "code":"<状态码,200表示成功,其它表示失败>", "message":"<消息,多在出现失败时显示失败原因>", "body": { "pageNumber": <当前页,从1开始>, "pageSize": <每页条数>, "pageTotal": <总页数>, "recordTotal": <总记录数>, "objects": [ 返回主体内容,不同接口内容不同 ] } }
8.7.2. 登录
请求
POST /public/auth/login/
body:
{
"id":String, // 登录Id或email
"password":String, // 密码
"organizationCode":String, // 所属组织,不传时使用配置中的`defaultOrganizationCode`
"captcha":String // 验证码
}
响应内容主体
{
"token": String, // token,前端需要保存此值,用于后续获取登录信息
"login_id": String, // 登录id
"name": String, // 姓名
"email": String, // email
"image": String, // 头像URL
"organization_code": String, // 组织编码
"organization_name": String, // 组织名称
"role_codes": List[String], // 角色编码列表
"ext_id": String, // 扩展Id
"ext_info": Map[String, Any] // 扩展信息
}
ext_id 只在使用extAccountStorage 时有意义
|
状态码
200 成功 |
8.7.6. 获取菜单(带权限过滤)
请求
GET /public/menu/?__ez_token__=<token> `__ez_token__`可选,不加时显示公共(不需要认证)的菜单
响应内容主体
[
{
"code": String, // 菜单编码
"uri": String, // 菜单点击的URI
"name": String, // 菜单名称
"icon": String, // 菜单图标名称
"translate": String, // 菜单翻译(i18n用)
"role_codes": List[String], // 所属角色编码列表
"parent_code": String, // 父菜单编码,用于多级菜单
"sort": Int, // 排序,倒序
"organization_code": String // 所属组织编码
},
...
]
8.7.7. 注册
请求
POST /public/register/
body
{
"login_id": String, // 登录id
"name": String, // 姓名
"image": String, // 头像
"email": String, // Email
"new_password": String // 密码
}
响应内容主体
null,允许自助激活时会发送激活邮件
8.7.9. 找回(重置)密码
请求
PUT /public/findpassword/<email>/
body
{
"new_password": String // 新的密码
}
响应内容主体
null,发送激活邮件
8.7.11. 获取登录账号信息
-
此操作直接从数据中获取数据,上文
获取账号信息
从缓存中获取
请求
GET /auth/manage/account/bylogin/?__ez_token__=<token>
响应内容主体
{
"id": String, // 数据库id
"login_id": String, // 登录id
"name": String, // 姓名
"image": String, // 头像
"email": String, // Email
"ext_id": String, // 扩展id
"ext_info": Map[String, Any] // 扩展信息
}
8.7.12. 更新登录账号信息
请求
PUT /auth/manage/account/bylogin/?__ez_token__=<token>
body
{
"name": String, // 姓名
"image": String, // 头像
"email": String, // Email
"current_password": String, // 当前密码
"new_password": String // 新密码,如果要修改密码此字段必填
}
current_password 必须填写正确,否则无法修改
|
响应内容主体
null
8.7.13. (管理接口)添加资源
请求
POST /auth/manage/resource/?__ez_token__=<token>
body
{
"method": String, // Http方法,大写
"uri": String, // 资源URI
"name": String // 资源名称
}
响应内容主体
{
"id": String, // 数据库id
"code": String, // 资源编码
"method": String, // Http方法,大写
"uri": String, // 资源uri
"name": String, // 资源名称
"enable": Boolean, // 是否启用
"create_user": String, // 创建用户login_id
"create_org": String, // 创建组织编码
"create_time": Long, // 创建时间(yyyyMMddHHmmssSSS)
"update_user": String, // 更新用户login_id
"update_org": String, // 更新组织编码
"update_time": Long // 更新时间(yyyyMMddHHmmssSSS)
}
8.7.14. (管理接口)更新资源
请求
PUT /auth/manage/resource/<资源id>/?__ez_token__=<token>
body
{
"name": String // 资源名称
}
只能修改name
|
响应内容主体
同(管理接口)添加资源
的响应内容主体
8.7.15. (管理接口)查找资源列表
请求
GET /auth/manage/resource/?__ez_token__=<token>&condition=<查找条件,sql或mongo json> condition可选
响应内容主体
[
{
同`(管理接口)添加资源`的响应内容主体
},
...
]
8.7.16. (管理接口)查找启用资源列表
请求
GET /auth/manage/resource/enable/?__ez_token__=<token>&condition=<查找条件,sql或mongo json> condition可选
响应内容主体
同(管理接口)查找启用资源列表
的响应内容主体
8.7.17. (管理接口)分页查找资源列表
请求
GET /auth/manage/resource/page/<当前页,从1开始>/<每页显示条数>/?__ez_token__=<token>&condition=<查找条件,sql或mongo json> condition可选
响应内容主体
{
"pageNumber":Long, // 当前页,从1开始
"pageSize":Int, // 每页显示条数
"pageTotal":Long, // 总共页数
"recordTotal":Long, // 总共记录数
// 当前页的实体列表
"objects":[
{
同`(管理接口)添加资源`的响应内容主体
},
...
]
}
8.7.18. (管理接口)获取一个资源
请求
GET /auth/manage/resource/<资源id>/?__ez_token__=<token>
响应内容主体
同(管理接口)添加资源
的响应内容主体
8.7.22. (管理接口)导出资源列表
请求
GET /auth/manage/resource/export/?__ez_token__=<token>
响应内容主体
资源中可导出字段的列表,格式为逗号分割符
8.7.23. (管理接口)添加组织
请求
POST /auth/manage/organization/?__ez_token__=<token>
body
{
"code": String, // 编码编码
"name": String, // 组织名称
"image": String, // 组织图标
"category": String // 组织类别
}
响应内容主体
{
"id": String, // 数据库id
"code": String, // 编码编码
"name": String, // 组织名称
"image": String, // 组织图标
"category": String, // 组织类别
"enable": Boolean, // 是否启用
"create_user": String, // 创建用户login_id
"create_org": String, // 创建组织编码
"create_time": Long, // 创建时间(yyyyMMddHHmmssSSS)
"update_user": String, // 更新用户login_id
"update_org": String, // 更新组织编码
"update_time": Long // 更新时间(yyyyMMddHHmmssSSS)
}
8.7.24. (管理接口)更新组织
请求
PUT /auth/manage/organization/<组织id>/?__ez_token__=<token>
body
{
"name": String, // 组织名称
"image": String, // 组织图标
"category": String // 组织类别
}
只能修改name 、image 和category
|
响应内容主体
同(管理接口)添加组织
的响应内容主体
8.7.25. (管理接口)查找组织列表
请求
GET /auth/manage/organization/?__ez_token__=<token>&condition=<查找条件,sql或mongo json> condition可选
响应内容主体
[
{
同`(管理接口)添加组织`的响应内容主体
},
...
]
8.7.26. (管理接口)查找启用组织列表
请求
GET /auth/manage/organization/enable/?__ez_token__=<token>&condition=<查找条件,sql或mongo json> condition可选
响应内容主体
同(管理接口)查找启用组织列表
的响应内容主体
8.7.27. (管理接口)分页查找组织列表
请求
GET /auth/manage/organization/page/<当前页,从1开始>/<每页显示条数>/?__ez_token__=<token>&condition=<查找条件,sql或mongo json> condition可选
响应内容主体
{
"pageNumber":Long, // 当前页,从1开始
"pageSize":Int, // 每页显示条数
"pageTotal":Long, // 总共页数
"recordTotal":Long, // 总共记录数
// 当前页的实体列表
"objects":[
{
同`(管理接口)添加组织`的响应内容主体
},
...
]
}
8.7.28. (管理接口)获取一个组织
请求
GET /auth/manage/organization/<组织id>/?__ez_token__=<token>
响应内容主体
同(管理接口)添加组织
的响应内容主体
8.7.30. (管理接口)启用一个组织
请求
GET /auth/manage/organization/<组织id>/enable/?__ez_token__=<token>
响应内容主体
null
8.7.31. (管理接口)禁用一个组织
请求
GET /auth/manage/organization/<组织id>/disable/?__ez_token__=<token>
响应内容主体
null
8.7.32. (管理接口)导出组织列表
请求
GET /auth/manage/organization/export/?__ez_token__=<token>
响应内容主体
组织中可导出字段的列表,格式为逗号分割符
8.7.33. (管理接口)上传组织图标
请求
POST /auth/manage/organization/res/?__ez_token__=<token>
body 上传的图标
响应内容主体
上传图标的uri
8.7.35. (管理接口)添加角色
请求
POST /auth/manage/role/?__ez_token__=<token>
body
{
"flag": String, // 角色标识
"name": String, // 角色名称
"resource_codes": List[String], // 所属资源编码列表
"organization_code": String // 所属组织编码
}
响应内容主体
{
"id": String, // 数据库id
"code": String, // 编码编码
"flag": String, // 角色标识
"name": String, // 角色名称
"resource_codes": List[String], // 所属资源编码列表
"organization_code": String, // 所属组织编码
"enable": Boolean, // 是否启用
"create_user": String, // 创建用户login_id
"create_org": String, // 创建角色编码
"create_time": Long, // 创建时间(yyyyMMddHHmmssSSS)
"update_user": String, // 更新用户login_id
"update_org": String, // 更新角色编码
"update_time": Long // 更新时间(yyyyMMddHHmmssSSS)
}
8.7.36. (管理接口)更新角色
请求
PUT /auth/manage/role/<角色id>/?__ez_token__=<token>
body
{
"name": String, // 角色名称
"resource_codes": List[String], // 所属资源编码列表
}
只能修改name 和resource_codes
|
响应内容主体
同(管理接口)添加角色
的响应内容主体
8.7.37. (管理接口)查找角色列表
请求
GET /auth/manage/role/?__ez_token__=<token>&condition=<查找条件,sql或mongo json> condition可选
响应内容主体
[
{
同`(管理接口)添加角色`的响应内容主体
},
...
]
8.7.38. (管理接口)查找启用角色列表
请求
GET /auth/manage/role/enable/?__ez_token__=<token>&condition=<查找条件,sql或mongo json> condition可选
响应内容主体
同(管理接口)查找启用角色列表
的响应内容主体
8.7.39. (管理接口)分页查找角色列表
请求
GET /auth/manage/role/page/<当前页,从1开始>/<每页显示条数>/?__ez_token__=<token>&condition=<查找条件,sql或mongo json> condition可选
响应内容主体
{
"pageNumber":Long, // 当前页,从1开始
"pageSize":Int, // 每页显示条数
"pageTotal":Long, // 总共页数
"recordTotal":Long, // 总共记录数
// 当前页的实体列表
"objects":[
{
同`(管理接口)添加角色`的响应内容主体
},
...
]
}
8.7.40. (管理接口)获取一个角色
请求
GET /auth/manage/role/<角色id>/?__ez_token__=<token>
响应内容主体
同(管理接口)添加角色
的响应内容主体
8.7.44. (管理接口)导出角色列表
请求
GET /auth/manage/role/export/?__ez_token__=<token>
响应内容主体
角色中可导出字段的列表,格式为逗号分割符
8.7.45. (管理接口)添加账户
请求
POST /auth/manage/account/?__ez_token__=<token>
body
{
"login_id": String, // 登录id
"name": String, // 姓名
"email": String, // email
"image": String, // 头像URL
"password": String, // 密码
"role_codes": List[String], // 角色编码列表
"organization_code": String, // 所属组织编码
"ext_id": String, // 扩展Id
"ext_info": Map[String, Any] // 扩展信息
}
响应内容主体
{
"id": String, // 数据库id
"code": String, // 账户编码
"login_id": String, // 登录id
"name": String, // 姓名
"email": String, // email
"image": String, // 头像URL
"password": String, // 密码
"role_codes": List[String], // 角色编码列表
"organization_code": String, // 所属组织编码
"oauth": Map[String, String], // oauth信息
"ext_id": String, // 扩展Id
"ext_info": Map[String, Any] // 扩展信息
"enable": Boolean, // 是否启用
"create_user": String, // 创建用户login_id
"create_org": String, // 创建账户编码
"create_time": Long, // 创建时间(yyyyMMddHHmmssSSS)
"update_user": String, // 更新用户login_id
"update_org": String, // 更新账户编码
"update_time": Long // 更新时间(yyyyMMddHHmmssSSS)
}
8.7.46. (管理接口)更新账户
请求
PUT /auth/manage/account/<账户id>/?__ez_token__=<token>
body
{
"name": String, // 姓名
"email": String, // email
"image": String, // 头像URL
"password": String, // 密码
"role_codes": List[String], // 角色编码列表
"ext_info": Map[String, Any] // 扩展信息
}
只能修改name 、email 、image 、password 、role_codes 和ext_info
|
响应内容主体
同(管理接口)添加账户
的响应内容主体
8.7.47. (管理接口)查找账户列表
请求
GET /auth/manage/account/?__ez_token__=<token>&condition=<查找条件,sql或mongo json> condition可选
响应内容主体
[
{
同`(管理接口)添加账户`的响应内容主体
},
...
]
8.7.48. (管理接口)查找启用账户列表
请求
GET /auth/manage/account/enable/?__ez_token__=<token>&condition=<查找条件,sql或mongo json> condition可选
响应内容主体
同(管理接口)查找启用账户列表
的响应内容主体
8.7.49. (管理接口)分页查找账户列表
请求
GET /auth/manage/account/page/<当前页,从1开始>/<每页显示条数>/?__ez_token__=<token>&condition=<查找条件,sql或mongo json> condition可选
响应内容主体
{
"pageNumber":Long, // 当前页,从1开始
"pageSize":Int, // 每页显示条数
"pageTotal":Long, // 总共页数
"recordTotal":Long, // 总共记录数
// 当前页的实体列表
"objects":[
{
同`(管理接口)添加账户`的响应内容主体
},
...
]
}
8.7.50. (管理接口)获取一个账户
请求
GET /auth/manage/account/<账户id>/?__ez_token__=<token>
响应内容主体
同(管理接口)添加账户
的响应内容主体
8.7.54. (管理接口)导出账户列表
请求
GET /auth/manage/account/export/?__ez_token__=<token>
响应内容主体
账户中可导出字段的列表,格式为逗号分割符
8.7.57. (管理接口)添加菜单
请求
POST /auth/manage/menu/?__ez_token__=<token>
body
{
"uri": String, // 菜单点击的URI
"name": String, // 菜单名称
"icon": String, // 菜单图标名称
"translate": String, // 菜单翻译(i18n用)
"role_codes": List[String], // 所属角色编码列表
"parent_code": String, // 父菜单编码,用于多级菜单
"sort": Int, // 排序,倒序
"organization_code": String // 所属组织编码
}
响应内容主体
{
"id": String, // 数据库id
"code": String, // 菜单编码
"uri": String, // 菜单点击的URI
"name": String, // 菜单名称
"icon": String, // 菜单图标名称
"translate": String, // 菜单翻译(i18n用)
"role_codes": List[String], // 所属角色编码列表
"parent_code": String, // 父菜单编码,用于多级菜单
"sort": Int, // 排序,倒序
"organization_code": String, // 所属组织编码
"enable": Boolean, // 是否启用
"create_user": String, // 创建用户login_id
"create_org": String, // 创建菜单编码
"create_time": Long, // 创建时间(yyyyMMddHHmmssSSS)
"update_user": String, // 更新用户login_id
"update_org": String, // 更新菜单编码
"update_time": Long // 更新时间(yyyyMMddHHmmssSSS)
}
8.7.58. (管理接口)更新菜单
请求
PUT /auth/manage/menu/<菜单id>/?__ez_token__=<token>
body
{
"name": String, // 菜单名称
"icon": String, // 菜单图标名称
"translate": String, // 菜单翻译(i18n用)
"role_codes": List[String], // 所属角色编码列表
"parent_code": String, // 父菜单编码,用于多级菜单
"sort": Int // 排序,倒序
}
只能修改name 、icon 、translate 、parent_code 、role_codes 和sort
|
响应内容主体
同(管理接口)添加菜单
的响应内容主体
8.7.59. (管理接口)查找菜单列表
请求
GET /auth/manage/menu/?__ez_token__=<token>&condition=<查找条件,sql或mongo json> condition可选
响应内容主体
[
{
同`(管理接口)添加菜单`的响应内容主体
},
...
]
8.7.60. (管理接口)查找启用菜单列表
请求
GET /auth/manage/menu/enable/?__ez_token__=<token>&condition=<查找条件,sql或mongo json> condition可选
响应内容主体
同(管理接口)查找启用菜单列表
的响应内容主体
8.7.61. (管理接口)分页查找菜单列表
请求
GET /auth/manage/menu/page/<当前页,从1开始>/<每页显示条数>/?__ez_token__=<token>&condition=<查找条件,sql或mongo json> condition可选
响应内容主体
{
"pageNumber":Long, // 当前页,从1开始
"pageSize":Int, // 每页显示条数
"pageTotal":Long, // 总共页数
"recordTotal":Long, // 总共记录数
// 当前页的实体列表
"objects":[
{
同`(管理接口)添加菜单`的响应内容主体
},
...
]
}
9. 消息服务
9.2. 5分钟上手
-
添加依赖
<dependency> <groupId>com.ecfront</groupId> <artifactId>ezf-message</artifactId> <version>3.0.0-beta2</version> <dependency>
-
添加配置
"message": { "customTables": { "message": "biz_message", "message_log": "biz_message_log" }, "storage": "jdbc" }, "auth": { "useRelTable": true, "storage": "jdbc" }, "redis": { "host": "192.168.99.100", "port": 6379, }, "rpc.http": {}, "storage.jdbc": { "driver_class": "com.mysql.jdbc.Driver", "url": "jdbc:mysql://192.168.99.100:3306/ez_test?characterEncoding=UTF-8&autoReconnect=true", "user": "root", "password": "123456" }
-
启动服务
EZManager.start()
-
添加一条个人消息
val startTime = TimeHelper.sf.parse("20160503000000") val endTime = TimeHelper.sf.parse("20160504000000") MessageService.sendToAccount("@sysadmin", "cate1", "0", "msgSysAdmin1", "", startTime, endTime, EZStorageContext())
9.4. 配置
"auth": {
"customTables": { (1)
"message": "", (2)
"message_log": "" (3)
},
"storage":"mongo" (4)
}
1 | 是否使用自定义表名 |
2 | 自定义消息表名 |
3 | 自定义消息日志表名 |
4 | 持久化实现,支持mongo 或jdbc |
9.5. HTTP消息接口调用
9.5.2. 根据登录信息获取未读消息
请求
GET /message/unRead/?<markRead=true&>__ez_token__=<token>
markRead=true 时表示获取并标记为已读
|
响应内容主体
[
{
"id": "", // 消息Id
"to_account": "", // 个人消息
"to_role": "", // 角色消息
"category": "", // 类型
"level": "", // 级别
"template_code": "", // 模板Code
"content": "", // 内容
"title": "", // 标题
"start_time": 0, // 开始时间,yyyyMMddHHmmss
"end_time": 0 // 结束时间,yyyyMMddHHmmss
},
// ...
]
9.6. 编码消息接口调用
/**
* 使用模板发送公共消息
*
* @param category 类型
* @param level 级别
* @param templateCode 模板编码
* @param variable 模板变量
* @param startTime 开始时间
* @param endTime 结束时间
* @param context 上下文
* @return 是否成功
*/
def sendToPublic(category: String, level: String, templateCode: String, variable: Map[String, String],
startTime: Date, endTime: Date, context: EZStorageContext): Resp[Void]
/**
* 使用模板发送个人消息
*
* @param accountCode 账号编码
* @param category 类型
* @param level 级别
* @param templateCode 模板编码
* @param variable 模板变量
* @param startTime 开始时间
* @param endTime 结束时间
* @param context 上下文
* @return 是否成功
*/
def sendToAccount(accountCode: String, category: String, level: String, templateCode: String, variable: Map[String, String],
startTime: Date, endTime: Date, context: EZStorageContext): Resp[Void]
/**
* 使用模板发送角色消息
*
* @param roleCode 角色编码
* @param category 类型
* @param level 级别
* @param templateCode 模板编码
* @param variable 模板变量
* @param startTime 开始时间
* @param endTime 结束时间
* @param context 上下文
* @return 是否成功
*/
def sendToRole(roleCode: String, category: String, level: String, templateCode: String, variable: Map[String, String],
startTime: Date, endTime: Date, context: EZStorageContext): Resp[Void]
/**
* 发送公共消息
*
* @param category 类型
* @param level 级别
* @param content 内容
* @param title 标题
* @param startTime 开始时间
* @param endTime 结束时间
* @param context 上下文
* @return 是否成功
*/
def sendToPublic(category: String, level: String, content: String, title: String,
startTime: Date, endTime: Date, context: EZStorageContext): Resp[Void]
/**
* 发送个人消息
*
* @param accountCode 账号编码
* @param category 类型
* @param level 级别
* @param content 内容
* @param title 标题
* @param startTime 开始时间
* @param endTime 结束时间
* @param context 上下文
* @return 是否成功
*/
def sendToAccount(accountCode: String, category: String, level: String, content: String, title: String,
startTime: Date, endTime: Date, context: EZStorageContext): Resp[Void]
/**
* 发送角色消息
*
* @param roleCode 角色编码
* @param category 类型
* @param level 级别
* @param content 内容
* @param title 标题
* @param startTime 开始时间
* @param endTime 结束时间
* @param context 上下文
* @return 是否成功
*/
def sendToRole(roleCode: String, category: String, level: String, content: String, title: String,
startTime: Date, endTime: Date, context: EZStorageContext): Resp[Void]
10. 邮件服务
10.2. 5分钟上手
-
添加依赖
<dependency> <groupId>com.ecfront</groupId> <artifactId>ezf-email</artifactId> <version>3.0.0-beta2</version> <dependency>
-
添加配置
"email": { "hostname": "<SMTP服务IP>", "username": "<用户名或完整email 地址>", "password": "<登录密码>" }
-
测试服务
EZManager.start() EmailProcessor.send( "<收件人email地址>", "test 1", "<h1>h1</h1><br/>1\r\n2\r\n" )
10.4. 配置
"email": { "hostname": "", (1) "port": 25, (2) "starttls": "OPTIONAL", (3) "login": "NONE", (4) "username": "", (5) "password": "", (6) "ssl": false, (7) "authMethods": "LOGIN", (8) "keepAlive": true, (9) "maxPoolSize": 10, (10) "trustAll": false, (11) "keyStore ": "<可选>", (12) "keyStorePassword": "<可选>", (13) "allowRcptErrors": "<可选>" (14) }
1 | SMTP服务IP |
2 | SMTP服务端口号 |
3 | 通信协议扩展,支持 DISABLED, OPTIONAL or REQUIRED |
4 | 登录认证,支持 DISABLED, NONE or REQUIRED |
5 | 登录用户名或完整email 地址 |
6 | 登录密码 |
7 | 是否使用SSL加密连接 |
8 | 登录认证方法,可选 或填写 LOGIN |
9 | 是否使用连接池 |
10 | 最大连接数量 |
11 | 是否信任所有证书 |
12 | 加密证书绝对路径 |
13 | 证书密码 |
10.5. 使用
10.5.1. 同步操作
/**
* 发送email
*
* @param to to
* @param title 标题
* @param content 正文
* @return 发送结果
*/
def send(to: String, title: String, content: String): Resp[Void]
/**
* 发送email
*
* @param to to
* @param title 标题
* @param content 正文
* @return 发送结果
*/
def send(to: List[String], title: String, content: String): Resp[Void] = {
send(mailConfig.getUsername, to, null, null, title, content, List())
}
/**
* 发送email
*
* @param from from
* @param to to
* @param title 标题
* @param content 正文
* @return 发送结果
*/
def send(from: String, to: String, title: String, content: String): Resp[Void]
/**
* 发送email
*
* @param from from
* @param to to
* @param title 标题
* @param content 正文
* @return 发送结果
*/
def send(from: String, to: List[String], title: String, content: String): Resp[Void]
/**
* 发送email
*
* @param from from
* @param to to
* @param cc cc
* @param bcc bcc
* @param title 标题
* @param content 正文
* @param attachments 附件,格式:Name - ContentType - Data
* @return 发送结果
*/
def send(from: String, to: List[String], cc: List[String], bcc: List[String],
title: String, content: String, attachments: List[(String, String, Buffer)]): Resp[Void]
异步操作
/**
* 发送email
*
* @param to to
* @param title 标题
* @param content 正文
* @return 发送结果
*/
def send(to: String, title: String, content: String): Future[Resp[Void]]
/**
* 发送email
*
* @param to to
* @param title 标题
* @param content 正文
* @return 发送结果
*/
def send(to: List[String], title: String, content: String): Future[Resp[Void]]
/**
* 发送email
*
* @param from from
* @param to to
* @param title 标题
* @param content 正文
* @return 发送结果
*/
def send(from: String, to: String, title: String, content: String): Future[Resp[Void]]
/**
* 发送email
*
* @param from from
* @param to to
* @param title 标题
* @param content 正文
* @return 发送结果
*/
def send(from: String, to: List[String], title: String, content: String): Future[Resp[Void]]
/**
* 发送email
*
* @param from from
* @param to to
* @param cc cc
* @param bcc bcc
* @param title 标题
* @param content 正文
* @param attachments 附件,格式:Name - ContentType - Data
* @return 发送结果
*/
def send(from: String, to: List[String], cc: List[String], bcc: List[String],
title: String, content: String, attachments: List[(String, String, Buffer)]): Future[Resp[Void]]
11. Redis服务
11.2. 5分钟上手
-
添加依赖
<dependency> <groupId>com.ecfront</groupId> <artifactId>ezf-redis</artifactId> <version>3.0.0-beta2</version> <dependency>
-
添加配置
"distributed": {}, "redis": { "host": "<redis IP>" }
-
测试服务
EZManager.start() // 删除n_test RedisProcessor.del("n_test") // 判断是否存在n_test RedisProcessor.exists("n_test") // 添加n_test值 4秒后过期 RedisProcessor.set("n_test", s"""{"name":"jzy"}""", 4) // 获取n_test值 RedisProcessor.get("n_test")
11.4. 配置
"redis": { "host": "127.0.0.1", (1) "port": 6379, (2) "auth": null, (3) "db": 0, (4) }
1 | Redis服务IP |
2 | Redis服务端口号 |
3 | Redis认证密码 |
4 | 连接的db库名 |
11.5. 使用
11.5.1. 同步操作
/**
* 暴露redis client ,用于自定义操作
*
* @return redis client
*/
def custom(): RedisClient
/**
* key是否存在
*
* @param key key
* @return 是否存在
*/
def exists(key: String): Resp[Boolean]
/**
* 获取字符串值
*
* @param key key
* @return 字符串值
*/
def get(key: String): Resp[String]
/**
* 设置字符串
*
* @param key key
* @param value value
* @param expire 过期时间(seconds),0表示永不过期
* @return 是否成功
*/
def set(key: String, value: String, expire: Long = 0): Resp[Void]
/**
* 删除key
*
* @param key key
* @return 是否成功
*/
def del(key: String): Resp[Void]
/**
* 设置列表
*
* @param key key
* @param values values
* @param expire 过期时间(seconds),0表示永不过期
* @return 是否成功
*/
def lmset(key: String, values: List[String], expire: Long = 0): Resp[Void]
/**
* 添加列表值
*
* @param key key
* @param value value
* @return 是否成功
*/
def lpush(key: String, value: String): Resp[Void]
/**
* 修改列表中索引对应的值
*
* @param key key
* @param value value
* @param index 索引
* @return 是否成功
*/
def lset(key: String, value: String, index: Long): Resp[Void]
/**
* 弹出栈顶的列表值
* 注意,Redis的列表是栈结构,先进后出
*
* @param key key
* @return 栈顶的列表值
*/
def lpop(key: String): Resp[String]
/**
* 获取列表中索引对应的值
* 注意,Redis的列表是栈结构,先进后出
*
* @param key key
* @param index 索引
* @return 索引对应的值
*/
def lindex(key: String, index: Long): Resp[String]
/**
* 获取列表值的长度
*
* @param key key
* @return 长度
*/
def llen(key: String): Resp[Long]
/**
* 获取列表中的所有值
*
* @param key key
* @return 值列表
*/
def lget(key: String): Resp[List[String]]
/**
* 设置Hash集合
*
* @param key key
* @param values values
* @param expire 过期时间(seconds),0表示永不过期
* @return 是否成功
*/
def hmset(key: String, values: Map[String, String], expire: Long = 0): Resp[Void]
/**
* 修改Hash集合field对应的值
*
* @param key key
* @param field field
* @param value value
* @return 是否成功
*/
def hset(key: String, field: String, value: String): Resp[Void]
/**
* 获取Hash集合field对应的值
*
* @param key key
* @param field field
* @return field对应的值
*/
def hget(key: String, field: String): Resp[String]
/**
* 判断Hash集合field是否存在
*
* @param key key
* @param field field
* @return 是否存在
*/
def hexists(key: String, field: String): Resp[Boolean]
/**
* 获取Hash集合的所有值
*
* @param key key
* @return 所有值
*/
def hgetall(key: String): Resp[Map[String, String]]
/**
* 删除Hash集合是对应的field
*
* @param key key
* @param field field
* @return 是否成功
*/
def hdel(key: String, field: String): Resp[Void]
/**
* 原子加操作
*
* @param key key,key不存在时会自动创建值为0的对象
* @param incrValue 要增加的值,必须是Long Int Float 或 Double
* @return 操作后的值
*/
def incr(key: String, incrValue: Long=1): Resp[Long]
/**
* 原子减操作
*
* @param key key key,key不存在时会自动创建值为0的对象
* @param decrValue 要减少的值,必须是Long 或 Int
* @return 操作后的值
*/
def decr(key: String, decrValue: Long=1): Resp[Long]
异步操作
/**
* key是否存在
*
* @param key key
* @return 是否存在
*/
def exists(key: String): Future[Resp[Boolean]]
/**
* 获取字符串值
*
* @param key key
* @return 字符串值
*/
def get(key: String): Future[Resp[String]]
/**
* 设置字符串
*
* @param key key
* @param value value
* @param expire 过期时间(seconds),0表示永不过期
* @return 是否成功
*/
def set(key: String, value: String, expire: Long = 0): Future[Resp[Void]]
/**
* 删除key
*
* @param key key
* @return 是否成功
*/
def del(key: String): Future[Resp[Void]]
/**
* 设置列表
*
* @param key key
* @param values values
* @param expire 过期时间(seconds),0表示永不过期
* @return 是否成功
*/
def lmset(key: String, values: List[String], expire: Long = 0): Future[Resp[Void]]
/**
* 添加列表值
*
* @param key key
* @param value value
* @return 是否成功
*/
def lpush(key: String, value: String): Future[Resp[Void]]
/**
* 修改列表中索引对应的值
*
* @param key key
* @param value value
* @param index 索引
* @return 是否成功
*/
def lset(key: String, value: String, index: Long): Future[Resp[Void]]
/**
* 弹出栈顶的列表值
* 注意,Redis的列表是栈结构,先进后出
*
* @param key key
* @return 栈顶的列表值
*/
def lpop(key: String): Future[Resp[String]]
/**
* 获取列表中索引对应的值
* 注意,Redis的列表是栈结构,先进后出
*
* @param key key
* @param index 索引
* @return 索引对应的值
*/
def lindex(key: String, index: Long): Future[Resp[String]]
/**
* 获取列表值的长度
*
* @param key key
* @return 长度
*/
def llen(key: String): Future[Resp[Long]]
/**
* 获取列表中的所有值
*
* @param key key
* @return 值列表
*/
def lget(key: String): Future[Resp[List[String]]]
/**
* 设置Hash集合
*
* @param key key
* @param values values
* @param expire 过期时间(seconds),0表示永不过期
* @return 是否成功
*/
def hmset(key: String, values: Map[String, String], expire: Long = 0): Future[Resp[Void]]
/**
* 修改Hash集合field对应的值
*
* @param key key
* @param field field
* @param value value
* @return 是否成功
*/
def hset(key: String, field: String, value: String): Future[Resp[Void]]
/**
* 获取Hash集合field对应的值
*
* @param key key
* @param field field
* @return field对应的值
*/
def hget(key: String, field: String): Future[Resp[String]]
/**
* 判断Hash集合field是否存在
*
* @param key key
* @param field field
* @return 是否存在
*/
def hexists(key: String, field: String): Future[Resp[Boolean]]
/**
* 获取Hash集合的所有值
*
* @param key key
* @return 所有值
*/
def hgetall(key: String): Future[Resp[Map[String, String]]]
/**
* 删除Hash集合是对应的field
*
* @param key key
* @param field field
* @return 是否成功
*/
def hdel(key: String, field: String): Future[Resp[Void]]
/**
* 原子加操作
*
* @param key key,key不存在时会自动创建值为0的对象
* @param incrValue 要增加的值,必须是Long Int Float 或 Double
* @return 是否成功
*/
def incr(key: String, incrValue: Long=1): Future[Resp[Long]]
/**
* 原子减操作
*
* @param key key key,key不存在时会自动创建值为0的对象
* @param decrValue 要减少的值,必须是Long 或 Int
* @return 是否成功
*/
def decr(key: String, decrValue: Long=1): Future[Resp[Long]]
12. 常用分布式服务
12.2. 5分钟上手
-
添加依赖
<dependency> <groupId>com.ecfront</groupId> <artifactId>ezf-distributed</artifactId> <version>3.0.0-beta2</version> <dependency>
-
添加配置
"distributed": {}, "redis": {}
-
测试服务
EZManager.start() // 创建test_counter计数器 val counter = DCounterService("test_counter") // 设置成10 counter.set(10) // 加1 counter.inc() // 减1 counter.dec() // 删除 counter.delete()
12.5. 使用
12.5.1. 分布式AtomicLong
def set(value: Long)
def get: Long
1 | key AtomicLong名 |
12.5.2. 分布式阻塞队列
def put(value: M)
def peek(): M
def take(): M
def size(): Int
def delete()
1 | key 队列名,M 队列项的类型 |
12.5.3. 分布式CountDownLatch
def set(value: Long)
def await(time: Long = -1, unit: TimeUnit = null)
def get: Long
def countDown()
def delete()
1 | key CountDownLatch名 |
12.5.4. 分布式原子计数器
def set(value: Long)
def get: Long
def inc(): Long
def dec(): Long
/**
* 有上限的inc
*
* @param maxValue 最大值
* @return 当前值
*/
def inc(maxValue: Long): Long
/**
* 有上限的inc并返回是否增加了
*
* @param maxValue 最大值
* @return 是否增加了,已达上限返回false
*/
def incWithStatus(maxValue: Long): Boolean
/**
* 有下限的dec
*
* @param minValue 最小值
* @return 当前值
*/
def dec(minValue: Long): Long
/**
* 有下限的inc并返回是否减少了
*
* @param minValue 最小值
* @return 是否减少了,已达下限返回false
*/
def decWithStatus(minValue: Long): Boolean
def delete()
1 | key 计数器名 |
12.5.5. 分布式锁
def lock(leaseTime: Long = -1, unit: TimeUnit = null)
def tryLock(waitTime: Long = 0, leaseTime: Long = -1, unit: TimeUnit = TimeUnit.MILLISECONDS): Boolean
def unLock(): Boolean
def isLock: Boolean
def delete()
1 | key 锁名 |
12.5.6. 分布式Map,key为string , value为自定义类型
def put(key: String, value: M)
def putIfAbsent(key: String, value: M)
def contains(key: String): Boolean
def foreach(fun: (String, M) => Unit)
def get(key: String): M
def remove(key: String)
def clear()
1 | key Map名,M Map项的类型 |
12.5.7. 分布式队列(不阻塞)
def add(value: M)
def peek(): M
def poll(): M
def size(): Int
def delete()
1 | key 队列名,M 队列项的类型 |
12.5.8. 分布式消息队列
def publish(message: M) // 发布消息
def subscribe(fun: => M => Unit) // 订阅消息
def subscribeOneNode(fun: => M => Unit) // 订阅消息,一条消息只由一个节点处理
def send(message: M) // 发送消息(point to point)
def receive(fun: => M => Unit) // 接收消息(point to point)
1 | key 消息队列名,M 消息队列项的类型 |
12.5.9. 分布式服务监控
此功能用监控服务状态,在需要被监控的服务中调用DMonitorService.start()
即可开启监控
心跳默认时间间隔是60秒
|
服务状态管理:
/**
* 获取所有服务
*
* @return 所有服务
*/
def fetchAllServices: Map[String, DService]
/**
* 删除一个服务
*
* @param key 要删除的服务key
*/
def removeAService(key: String): Unit
/**
* 删除所有服务
*/
def removeAllServices(): Unit
/**
* 获取服务状态报告
*
* @return 服务状态报告
*/
def fetchLiveReport: Map[String, Long]
13. 调度服务
13.2. 5分钟上手
-
添加依赖
<dependency> <groupId>com.ecfront</groupId> <artifactId>ezf-scheduler</artifactId> <version>3.0.0-beta2</version> <dependency>
-
添加配置
"scheduler":{ "storage":"mongo" }
-
添加调度代码
// 添加调度任务 val scheduler = EZ_Scheduler() scheduler.name = "测试" // 任务名称 scheduler.cron = "* * * * * ?" // 调度周期 scheduler.module = EZContext.module // 要调度的模块 scheduler.clazz = TestScheduleJob.getClass.getName // 任务回调对应的类 scheduler.parameters = Map("p1" -> 1, "p2" -> "1") // 任务参数 SchedulerProcessor.save(scheduler) // 保存
object TestScheduleJob extends ScheduleJob { override def execute(scheduler: EZ_Scheduler): Resp[Void] = { println("Scheduler callback:" + scheduler.name) Resp.success(null) } }
-
启动服务
EZManager.start()
13.4. 配置
"scheduler": {
"storage": "", (1)
"customTables": {
"scheduler": "ez_scheduler", (2)
"scheduler_Log": "ez_scheduler_Log" (3)
}
}
1 | 持久化实现,支持mongo 或jdbc |
2 | 自定义调度表名 |
3 | 自定义调度日志表名 |
storage:jdbc 时请执行ez_ddl.sql 以创建基础表
|
13.5. 使用
=== 保存调度任务 ===
// 调度任务是EZ_Scheduler对象
val scheduler = EZ_Scheduler()
scheduler.name = "" // 任务名称
scheduler.cron = "" // 调度周期
scheduler.module = "" // 要调度的模块
scheduler.clazz = "" // 任务回调对应的类,要实现ScheduleJob接口
scheduler.parameters = Map() // 任务参数
SchedulerProcessor.save(scheduler) // 保存
=== 更新调度任务 ===
...
SchedulerProcessor.update(scheduler)
...
=== 删除调度任务 ===
...
SchedulerProcessor.delete("<调度任务名称>")
...
=== 根据调度名称分页获取日志 ===
...
SchedulerProcessor.pageLogsByName("<调度任务名称>", <当前页,从1开始>, <每页条数>)
...
=== 调度任务回调 ===
/**
* 调度器回调基类,所有调度作业回调处理都要继承此类
*/
trait ScheduleJob {
/**
* 调度器回调时的执行方法
*
* @param scheduler 调度信息
* @return 执行结果
*/
def execute(scheduler: EZ_Scheduler): Resp[Void]
}
14. Kafka服务
14.2. 5分钟上手
-
添加依赖
<dependency> <groupId>com.ecfront</groupId> <artifactId>ezf-kafka</artifactId> <version>3.0.0-beta2</version> <dependency>
-
添加配置
"kafka": { "brokerList": "<broker ip:port>" }
-
测试服务
EZManager.start() // 定义第一个消费者,属于group1 val consumer1 = KafkaProcessor.Consumer("group1", "topic1") consumer1.receive(new ReceivedCallback { override def callback(message: String): Resp[Void] = { // 收到消息 println("consumer1 -> " + message) // 关闭此消费者,这时第二个消费者可以收到消息 consumer1.close() Resp.success(null) } }) // 定义第二个消费者,也属于group1,第一个与第二消费者是竞争关系,同一消息只会被其中一个消 费 KafkaProcessor.Consumer("group1", "topic1").receive(new ReceivedCallback { override def callback(message: String): Resp[Void] = { // 收到消息 println("consumer2 -> " + message) Resp.success(null) } }) // 定义第三个消费者,属于group2,这个消费者可以收到所有消息 KafkaProcessor.Consumer("group2", "topic1").receive(new ReceivedCallback { override def callback(message: String): Resp[Void] = { // 收到消息 println("consumer3 -> " + message) Resp.success(null) } }) // 定义一个生产者 val producer = KafkaProcessor.Producer("topic1", "client1") for (i <- 0 to 5) { producer.send(s"【$i】haha...") }
14.6. API
/**
* 生产者类
*
* @param topic 主题
* @param clientId 客户端ID
*/
case class Producer(topic: String, clientId: String) {
/**
* 发送消息
*
* @param message 消息
* @param messageId 消息ID,默认自动生成UUID
*/
def send(message: String, messageId: String = UUID.randomUUID().toString): Unit
/**
* 发送消息
*
* @param message 消息
* @param messageId 消息ID,默认自动生成UUID
* @return 是否成功 ,返回对应的消息ID(自动生成)和message
*/
def sendFuture(message: String, messageId: String = UUID.randomUUID().toString): Future[Resp[(String, String)]]
/**
* 发送需要回复的消息
*
* @param message 消息
* @param ackTopic 回复主题
* @param timeout 等待超时时间(ms)
* @return 回复的消息
*/
def ack(message: String, ackTopic: String, timeout: Long = 30 * 1000): Resp[String]
/**
* 关闭当前生产者
*/
def close(): Unit
}
/**
* 消费者类
*
* @param topic 主题
* @param groupId 组ID,用于发布-订阅模式
* @param autoCommit 是否自动提交
* @param autoCommitInterval 自动提供间隔
*/
case class Consumer(topic: String,groupId: String, autoCommit: Boolean = false, autoCommitInterval: Long = DEFAULT_AUTO_COMMIT_INTERVAL) {
/**
* 接收消息
*
* @param fun 收到消息后的回调方法
* @param ackTopic 回复主题,如果存在会回复消息
*/
def receive(fun: (String, String) => Resp[String], ackTopic: String = null): Unit
/**
* 关闭当前消费者
*/
def close(): Unit
}
/**
* 关闭所有实例
*/
def close(): Unit
15. Master-Slave服务
15.2. 5分钟上手
-
添加依赖
<dependency> <groupId>com.ecfront</groupId> <artifactId>ezf-master-slave</artifactId> <version>3.0.0-beta2</version> <dependency>
-
添加配置
"masterslave": { "clusterId": "ez.test" }, "kafka": { "brokerList": "<broker ip:port>" }
-
测试服务
EZManager.start()
15.4. 配置
"masterslave": { "clusterId": "", (1) "ha":false, (2) "category": { (3) "": { (4) "pool": 5, (5) "newThread": true (6) } }
1 | 集群ID,同一ID的节点会被视为同一集群 |
2 | 是否启用HA,启用HA时需要Redis支持 |
3 | Worker执行器列表 |
4 | Worker执行器名 |
5 | 当前执行器并发数 |
6 | 当前执行器是否在新线程中执行 |
15.6. API
/**
* 生产者类
*
* @param topic 主题
* @param clientId 客户端ID
*/
case class Producer(topic: String, clientId: String) {
/**
* 发送消息
*
* @param message 消息
*/
def send(message: String): Unit
/**
* 发送消息
*
* @param message 消息
* @return 是否成功 ,返回对应的消息ID(自动生成)和message
*/
def sendFuture(message: String): Future[Resp[(String, String)]]
/**
* 关闭当前生产者
*/
def close(): Unit
}
/**
* 消费者类
*
* @param groupId 组ID,用于发布-订阅模式
* @param topic 主题
* @param autoCommit 是否自动提交
* @param autoCommitInterval 自动提供间隔
*/
case class Consumer(groupId: String, topic: String, autoCommit: Boolean = false, autoCommitInterval: Long = DEFAULT_AUTO_COMMIT_INTERVAL) {
/**
* 接收消息
*
* @param fun 收到消息后的回调方法
*/
def receive(fun: ReceivedCallback): Unit
/**
* 关闭当前消费者
*/
def close(): Unit
}
/**
* 关闭所有实例
*/
def close(): Unit