EZ.F - Scala中功能全面、使用简单的服务框架

ez framework.svg

1. 设计理念

  1. 类库化 低侵入,虽然以“框架”称呼,但实际上是多个类库的集合,这里没有容器,不会与您的工程强耦合

  2. 模块化 所有功能按需选择,让您的工程尽可能轻量

  3. 高性能 核心模块提供同步与异步两种模型,高性能需求可选择异步模型

  4. 功能全面 集成了常用的服务,全栈支持

  5. 使用简单 各API都尽可能地统一与简化,降低使用成本

2. 功能列表

  1. 核心服务

    1. 支持自定义同步或异步的拦截器栈处理

    2. 支持I18N

  2. RPC服务

    1. 支持HTTP和WebSocket服务

    2. 基于注解服务发现

    3. Restful风格,支持GET POST PUT DELETE等常用方法

    4. 支持HTTPS

    5. 支持Json跨域请求

    6. 支持拦截器栈,可适配权限认证等服务

    7. 提供同步及异步HTTP客户端

    8. 提供CRUD脚手架服务

    9. 支持HTML与XML处理

  3. 存储服务

    1. 支持Mongo或关系型数据库(JDBC)

    2. 统一API

    3. 轻量级ORMapping,无Session处理

    4. 注解支持

  4. 调度服务

    1. 支持cron表达式的调度任务

    2. 支持按模块调度

    3. 支持JDBC或Mongo的持久化

  5. Redis缓存服务

    1. 支持Redis的常用操作

    2. 支持同步与异步操作

  6. 基于RBAC的基础HTTP认证

    1. 支持RBAC的认证服务

    2. 支持菜单级和资源级(action)的授权

    3. 支持JDBC或Mongo的持久化

    4. 支持自助注册及密码找回

  7. 邮件服务

    1. 支持同步或异步的邮件发送服务

    2. 支持添加附件

  8. Kakfa服务

  9. Master-Slave服务

  10. 常用分布式服务

    1. 分布式计数

    2. 分布式锁

    3. 分布式Map

    4. 分布式阻塞和非阻塞队列

    5. 分布式消息发布订阅

    6. 分布式服务监控

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>

4. 文档

5. License

Under version 2.0 of the Apache License.

6. 持久化服务

6.1. 功能

  1. 支持Mongo或关系型数据库(JDBC)

  2. 统一API

  3. 轻量级ORMapping,无Session处理

  4. 注解支持

关系型数据库目前的测试基于MySQL,其它数据库未做详细测试
数据字段应避免使用null值,字符用""、数字用0(或其它)、Bool用false 等代替

6.2. 5分钟上手

  1. 添加依赖

    <dependency>
      <groupId>com.ecfront</groupId>
      <artifactId>ezf-storage-<mongo或jdbc></artifactId>
      <version>3.0.0-beta2</version>
    <dependency>
  2. 添加配置

    // Mongo
    "storage.mongo": {
      "host": "<服务IP>",
      "port": <服务端口>,
      "db_name": "<数据库名>"
    }
    // 或 JDBC
    "storage.jdbc": {
      "driver_class": "<驱动>",
      "url": "<连接URL>",
      "user": "<用户名>",
      "password": "<密码>"
    }
  3. 在添加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]
  4. 测试服务

    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.3. 依赖

环境依赖:mongo或关系型数据库

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)
本服务基于 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. 常用方法

继承自(Mongo或JDBC)BaseStorage[E]的方法
/**
  * 保存
  *
  * @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]
继承自(Mongo或JDBC)StatusStorage[E]的方法
/**
  * 获取一条启用的记录
  *
  * @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]
以上所有方法都有preXpostX方法对,可以在实现的storage中重写以用于操作前及操作后处理,系统仅对操作前返回Resp.success()的结果做后续操作
Mongo特殊方法
继承自MongoBaseStorage[E]的方法
 /**
   * 附加条件查找
   * @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使用

同步操作
MongoProcessor
/**
  * 保存
  *
  * @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]
异步操作
MongoProcessor.Async
/**
  * 保存
  *
  * @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使用

同步操作
JDBCProcessor
  /**
    * 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
异步操作
JDBCProcessor.Async
/**
      * 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. 功能

  1. 支持HTTP和WebSocket服务

  2. 基于注解服务发现

  3. Restful风格,支持GET POST PUT DELETE等常用方法

  4. 支持HTTPS

  5. 支持Json跨域请求

  6. 支持拦截器栈,可适配权限认证等服务

  7. 提供同步及异步HTTP客户端

  8. 提供CRUD脚手架服务

  9. 支持HTML与XML处理

  10. 支持HTTP慢请求筛选

  11. 支持HTTP DDoS过滤

7.2. 5分钟上手

  1. 添加依赖

    <dependency>
      <groupId>com.ecfront</groupId>
      <artifactId>ezf-rpc-<http或websocket></artifactId>
      <version>3.0.0-beta2</version>
    <dependency>
  2. 添加配置

    // HTTP
    "rpc.http": {
      "servicePath": "com.foo.api"
    }
    // 或 WebSocket
    "rpc.websocket": {
      "servicePath": "com.foo.api"
    }
  3. 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)
       }
    }
  4. 启动服务

    EZManager.start()
  5. 访问

    // 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.3. 依赖

服务依赖:storage.mongo或storage.jdbc(可选,使用CRUD脚手架服务时必选)

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/
includesexcludes是排他的,当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客户端

同步操作
HttpClientProcessor
/**
  * 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
异步操作
HttpClientProcessor.Async
/**
  * 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消息推送管理

WebSocketMessagePushManager
/**
  * 向所有客户端推送消息
  *
  * @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.1. 功能

  1. 支持RBAC的认证服务

  2. 支持菜单级和资源级(action)的授权

  3. 支持JDBC或Mongo的持久化

  4. 支持自助注册及密码找回

8.2. 5分钟上手

  1. 添加依赖

    <dependency>
      <groupId>com.ecfront</groupId>
      <artifactId>ezf-auth</artifactId>
      <version>3.0.0-beta2</version>
    <dependency>
  2. 添加配置

    "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
    }
  3. 启动服务

    EZManager.start()
  4. 登录

    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-httpwebUrl做为基础路径
8 登录保持时间,单位秒,指定登录会话多少秒后过期,0表示不过期
9 注册或找回密码激活有效期,单位秒
10 扩展账号持久化类路径,可选,默认对于扩展的账号信息存储在ez_account表中的ext_info字段中,如果需要将扩展信息存储在独立表中时可以定义一个继承自BaseModel的实体及对应的持久化实现,然后指定其类路径
11 是否使用关联表(多对多关联中间表),多用于JDBC持久化,建议在原生不支持json的关系型数据启用此项
12 是否使用自定义表名
13 自定义组织表名
14 自定义账户表名
15 自定义资源表名
16 自定义角色表名
17 自定义菜单表名
18 自定义账户角色关联表名
19 自定义角色资源关联表名
20 自定义菜单角色关联表名
21 持久化实现,支持mongojdbc
22 登录限制
23 在连续多次登录失败后显示验证码
storage:jdbc & useRelTable不存在或等于false时请执行ez_ddl.sql以创建基础表,storage:jdbc & useRelTable=true时请执行ez_ddl_with_rel.sql以创建带关联表的基础表

8.5. 认证服务接入

在原有HTTP服务方法中修改 EZRPCContextEZAuthContext 即可,如

@POST("")
def testAuth(parameter: Map[String, String], body: Account_VO, context: EZAuthContext): Resp[Void]

EZAuthContext在EZRPCContext基础上添加了两个字段:

  1. token 认证的Token值

  2. loginInfo 登录信息

8.6. 事件

8.6.1. 初始化新组织事件

ServiceAdapter.ezEvent_organizationInit.<subscribe | subscribeOneNode>({
  orgCode:String =>
    // your code
})

8.6.2. 登录成功事件

ServiceAdapter.ezEvent_loginSuccess.<subscribe | subscribeOneNode>({
  tokenInfo:Token_Info_VO =>
    // your code
})

8.6.3. 注销事件

ServiceAdapter.ezEvent_logout.<subscribe | subscribeOneNode>({
  tokenInfo:Token_Info_VO =>
    // your code
})

8.7. 预定义认证接口调用

8.7.1. 说明

  1. 无特殊说明的情况下所有请求的header Content-Type为application/json

  2. 无特殊说明的情况下所有返回值均为Json,由以下格式构成:

    {
      "code":"<状态码,200表示成功,其它表示失败>",
      "message":"<消息,多在出现失败时显示失败原因>",
      "body":"<返回主体内容,不同接口内容不同>"
    }
  3. 如果是分页查询,则返回格式构成如下:

    {
      "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 成功
400 传入参数错误
403 验证码错误
409 账号或密码错误
404 账号不存在 423 账号或所属机构被锁定

8.7.3. 获取验证码

请求

GET /public/auth/captcha/<所属组织编码>/<登录Id或email>/

响应内容主体

验证码图片

8.7.4. 注销

请求

GET /auth/logout/?__ez_token__=<token>

响应内容主体

null

8.7.5. 获取登录信息

请求

GET /auth/logininfo/?__ez_token__=<token>

响应内容主体

登录的响应内容主体

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.8. 激活账号

请求

GET /public/active/account/<加密字符串>/ 来自邮件中的链接

响应内容主体

跳转到登录URL 或 返回错误信息

8.7.9. 找回(重置)密码

请求

PUT /public/findpassword/<email>/

body

{
  "new_password": String  // 新的密码
}

响应内容主体

null,发送激活邮件

8.7.10. 激活新密码

请求

GET /public/active/password/<加密字符串>/ 来自邮件中的链接

响应内容主体

跳转到登录URL 或 返回错误信息

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.19. (管理接口)删除一个资源

请求

DELETE /auth/manage/resource/<资源id>/?__ez_token__=<token>

响应内容主体

null

8.7.20. (管理接口)启用一个资源

请求

GET /auth/manage/resource/<资源id>/enable/?__ez_token__=<token>

响应内容主体

null

8.7.21. (管理接口)禁用一个资源

请求

GET /auth/manage/resource/<资源id>/disable/?__ez_token__=<token>

响应内容主体

null

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 // 组织类别
}
只能修改nameimagecategory

响应内容主体

(管理接口)添加组织的响应内容主体

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.29. (管理接口)删除一个组织

请求

DELETE /auth/manage/organization/<组织id>/?__ez_token__=<token>

响应内容主体

null

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.34. (管理接口)获取组织图标

请求

GET <`(管理接口)上传组织图标`中返回的uri>?__ez_token__=<token>

响应内容主体

显示上传的图标

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], // 所属资源编码列表
}
只能修改nameresource_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.41. (管理接口)删除一个角色

请求

DELETE /auth/manage/role/<角色id>/?__ez_token__=<token>

响应内容主体

null

8.7.42. (管理接口)启用一个角色

请求

GET /auth/manage/role/<角色id>/enable/?__ez_token__=<token>

响应内容主体

null

8.7.43. (管理接口)禁用一个角色

请求

GET /auth/manage/role/<角色id>/disable/?__ez_token__=<token>

响应内容主体

null

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] // 扩展信息
}
只能修改nameemailimagepasswordrole_codesext_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.51. (管理接口)删除一个账户

请求

DELETE /auth/manage/account/<账户id>/?__ez_token__=<token>

响应内容主体

null

8.7.52. (管理接口)启用一个账户

请求

GET /auth/manage/account/<账户id>/enable/?__ez_token__=<token>

响应内容主体

null

8.7.53. (管理接口)禁用一个账户

请求

GET /auth/manage/account/<账户id>/disable/?__ez_token__=<token>

响应内容主体

null

8.7.54. (管理接口)导出账户列表

请求

GET /auth/manage/account/export/?__ez_token__=<token>

响应内容主体

账户中可导出字段的列表,格式为逗号分割符

8.7.55. (管理接口)上传账户头像

请求

POST /auth/manage/role/res/?__ez_token__=<token>

body 上传的头像

响应内容主体

上传头像的uri

8.7.56. (管理接口)获取账户头像

请求

GET <`(管理接口)上传账户头像`中返回的uri>?__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 // 排序,倒序
}
只能修改nameicontranslateparent_coderole_codessort

响应内容主体

(管理接口)添加菜单的响应内容主体

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":[
    {
     同`(管理接口)添加菜单`的响应内容主体
    },
    ...
  ]
}

8.7.62. (管理接口)获取一个菜单

请求

GET /auth/manage/menu/<菜单id>/?__ez_token__=<token>

响应内容主体

(管理接口)添加菜单的响应内容主体

8.7.63. (管理接口)删除一个菜单

请求

DELETE /auth/manage/menu/<菜单id>/?__ez_token__=<token>

响应内容主体

null

8.7.64. (管理接口)启用一个菜单

请求

GET /auth/manage/menu/<菜单id>/enable/?__ez_token__=<token>

响应内容主体

null

8.7.65. (管理接口)禁用一个菜单

请求

GET /auth/manage/menu/<菜单id>/disable/?__ez_token__=<token>

响应内容主体

null

8.7.66. (管理接口)导出菜单列表

请求

GET /auth/manage/menu/export/?__ez_token__=<token>

响应内容主体

菜单中可导出字段的列表,格式为逗号分割符

9. 消息服务

9.1. 功能

  1. 支持消息模板

  2. 支持个人、角色和公共消息

9.2. 5分钟上手

  1. 添加依赖

    <dependency>
      <groupId>com.ecfront</groupId>
      <artifactId>ezf-message</artifactId>
      <version>3.0.0-beta2</version>
    <dependency>
  2. 添加配置

    "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"
    }
  3. 启动服务

    EZManager.start()
  4. 添加一条个人消息

    val startTime = TimeHelper.sf.parse("20160503000000")
    val endTime = TimeHelper.sf.parse("20160504000000")
    MessageService.sendToAccount("@sysadmin", "cate1", "0", "msgSysAdmin1", "", startTime, endTime, EZStorageContext())

9.3. 依赖

服务依赖:auth、storage.mongo或storage.jdbc

环境依赖:redis、mongo或mysql

9.4. 配置

"auth": {
  "customTables": {  (1)
     "message": "", (2)
     "message_log": "" (3)
  },
  "storage":"mongo" (4)
}
1 是否使用自定义表名
2 自定义消息表名
3 自定义消息日志表名
4 持久化实现,支持mongojdbc

9.5. HTTP消息接口调用

9.5.1. 根据登录信息获取未读消息条数

请求

GET /message/unRead/number/?__ez_token__=<token>

响应内容主体

未读消息条数

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.5.3. 根据登录信息分页获取已读消息

请求

GET /message/read/<当前页,从1开始>/<每页显示条数>/?__ez_token__=<token>

响应内容主体

{
  "pageNumber":0, // 当前页,从1开始
  "pageSize":0, // 每页显示条数
  "pageTotal":0, // 总共页数
  "recordTotal":0, // 总共记录数
  "objects":[] // 当前页的实体列表,同`根据登录信息获取未读消息`的响应内容主体
}

9.5.4. 标记消息已读

请求

GET /message/<消息ID>/markRead/?__ez_token__=<token>

响应内容主体

是否成功

9.5.5. 保存消息

请求

POST /message/?__ez_token__=<token>

body

根据登录信息获取未读消息的响应内容主体

响应内容主体

是否成功

9.5.6. 更新消息

请求

PUT /message/<消息ID>/?__ez_token__=<token>

body

根据登录信息获取未读消息的响应内容主体

响应内容主体

是否成功

9.5.7. 删除消息

请求

DELETE /message/<消息ID>/?__ez_token__=<token>

响应内容主体

是否成功

9.6. 编码消息接口调用

MessageService
/**
  * 使用模板发送公共消息
  *
  * @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.1. 功能

  1. 支持同步或异步的邮件发送服务

  2. 支持添加附件

10.2. 5分钟上手

  1. 添加依赖

    <dependency>
      <groupId>com.ecfront</groupId>
      <artifactId>ezf-email</artifactId>
      <version>3.0.0-beta2</version>
    <dependency>
  2. 添加配置

    "email": {
      "hostname": "<SMTP服务IP>",
      "username": "<用户名或完整email 地址>",
      "password": "<登录密码>"
    }
  3. 测试服务

    EZManager.start()
    EmailProcessor.send(
      "<收件人email地址>",
      "test 1",
      "<h1>h1</h1><br/>1\r\n2\r\n"
    )

10.3. 依赖

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.4.1. QQ企业邮箱配置示例

"email": {
  "hostname": "smtp.exmail.qq.com",
  "port": 465,
  "starttls": "REQUIRED",
  "login": "REQUIRED",
  "ssl": true,
  "authMethods": "LOGIN",
  "username": "admin@ecfront.com",
  "password": "<密码>"
}

10.5. 使用

10.5.1. 同步操作

EmailProcessor
/**
    * 发送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]
异步操作
EmailProcessor.Async
   /**
      * 发送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.1. 功能

  1. 支持Redis的常用操作

  2. 支持同步与异步操作

11.2. 5分钟上手

  1. 添加依赖

    <dependency>
      <groupId>com.ecfront</groupId>
      <artifactId>ezf-redis</artifactId>
      <version>3.0.0-beta2</version>
    <dependency>
  2. 添加配置

    "distributed": {},
    "redis": {
      "host": "<redis IP>"
    }
  3. 测试服务

    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.3. 依赖

环境依赖:redis 3.x

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. 同步操作

RedisProcessor
   /**
    * 暴露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]
异步操作
RedisProcessor.Async
    /**
      * 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.1. 功能

  1. 分布式计数

  2. 分布式锁

  3. 分布式Map

  4. 分布式阻塞和非阻塞队列

  5. 分布式消息发布订阅

12.2. 5分钟上手

  1. 添加依赖

    <dependency>
      <groupId>com.ecfront</groupId>
      <artifactId>ezf-distributed</artifactId>
      <version>3.0.0-beta2</version>
    <dependency>
  2. 添加配置

    "distributed": {},
    "redis": {}
  3. 测试服务

    EZManager.start()
    // 创建test_counter计数器
    val counter = DCounterService("test_counter")
    // 设置成10
    counter.set(10)
    // 加1
    counter.inc()
    // 减1
    counter.dec()
    // 删除
    counter.delete()

12.3. 依赖

服务依赖:redis

12.4. 配置

"distributed": {}

分布式服务本身没有配置项

12.5. 使用

12.5.1. 分布式AtomicLong

DAtomicLongService(key: String) <1>
  def set(value: Long)
  def get: Long
1 key AtomicLong名

12.5.2. 分布式阻塞队列

DBlockingQueueService[M](key: String) <1>
  def put(value: M)
  def peek(): M
  def take(): M
  def size(): Int
  def delete()
1 key 队列名,M 队列项的类型

12.5.3. 分布式CountDownLatch

DCountDownLatchService(key: String) <1>
  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. 分布式原子计数器

DCounterService(key: String) <1>
  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. 分布式锁

DLockService(key: String) <1>
  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为自定义类型

DMapService[M](key: String) <1>
  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. 分布式队列(不阻塞)

DQueueService[M](key: String) <1>
  def add(value: M)
  def peek(): M
  def poll(): M
  def size(): Int
  def delete()
1 key 队列名,M 队列项的类型

12.5.8. 分布式消息队列

DMQService[M](key: String) <1>
  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秒

服务状态管理:

DMonitorService.Manager
/**
  * 获取所有服务
  *
  * @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.1. 功能

  1. 支持cron表达式的调度任务

  2. 支持按模块调度

  3. 支持JDBC或Mongo的持久化

13.2. 5分钟上手

  1. 添加依赖

    <dependency>
      <groupId>com.ecfront</groupId>
      <artifactId>ezf-scheduler</artifactId>
      <version>3.0.0-beta2</version>
    <dependency>
  2. 添加配置

    "scheduler":{
       "storage":"mongo"
    }
  3. 添加调度代码

    // 添加调度任务
    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)
      }
    }
  4. 启动服务

    EZManager.start()

13.3. 依赖

服务依赖:storage.mongo或storage.jdbc

环境依赖:mongo或mysql

13.4. 配置

"scheduler": {
  "storage": "", (1)
  "customTables": {
     "scheduler": "ez_scheduler", (2)
     "scheduler_Log": "ez_scheduler_Log" (3)
  }
}
1 持久化实现,支持mongojdbc
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.1. 功能

  1. 实现Kafka服务封装

14.2. 5分钟上手

  1. 添加依赖

    <dependency>
      <groupId>com.ecfront</groupId>
      <artifactId>ezf-kafka</artifactId>
      <version>3.0.0-beta2</version>
    <dependency>
  2. 添加配置

    "kafka": {
      "brokerList": "<broker ip:port>"
    }
  3. 测试服务

    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.3. 依赖

环境依赖:kafka 0.9.x

14.4. 配置

"kafka": {
  "brokerList": "" (1)
}
1 Kafka broker地址列表,格式为: ip:port,…​

14.5. 使用

见示例com.ecfront.ez.framework.examples.kafka

14.6. API

KafkaProcessor
  /**
  * 生产者类
  *
  * @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.1. 功能

  1. master-slave模型封装

15.2. 5分钟上手

  1. 添加依赖

    <dependency>
      <groupId>com.ecfront</groupId>
      <artifactId>ezf-master-slave</artifactId>
      <version>3.0.0-beta2</version>
    <dependency>
  2. 添加配置

    "masterslave": {
      "clusterId": "ez.test"
    },
    "kafka": {
      "brokerList": "<broker ip:port>"
    }
  3. 测试服务

    EZManager.start()

15.3. 依赖

服务依赖:kafka、redis(如果需要HA支持) 环境依赖:kafka 0.9.x、redis 3.x

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.5. 使用

见示例com.ecfront.ez.framework.examples.masterslave

15.6. API

KafkaProcessor
  /**
  * 生产者类
  *
  * @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

16. Contributing

16.1. Environment

  1. JDK 1.8

  2. Scala 2.11

  3. Mongo 3.x(可选)

  4. Redis 3.x(可选)

  5. Kafka 0.8.x(可选)

16.2. Build

  1. git clone https://github.com/gudaoxuri/ez-framework.git

  2. 使用IDE导入Maven工程,各组件都是独立Maven工程

16.3. Submit

  1. 提交前请确保:

    已完成变更代码的单元测试
    代码风格检查(mvn scalastyle:check)通过:0 errors 0 warnings

17. Roadmap

17.1. 3.1.0

  1. 动态多数据源 支持

  2. SimpleRPC 添加排序、过滤等功能

  3. i18n支持

17.2. 3.0.0

  1. 添加示例

  2. 更多的边界测试

17.3. 3.0.0-SNAPSHOT

  1. 重写核心架构

  2. 规范代码组织

  3. 完善注释注解

  4. 添加示例代码

18. ChangeLog

18.1. 3.0.0

18.1.1. Features

  1. 核心架构重写

18.1.2. Bug Fixes