Merge branch 'release-0.0.1'
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..538cf2e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+/**/.DS_Store
+
+/**/pom.xml.versionsBackup
+
+/**/.project
+/**/.classpath
+/**/.settings
+/**/target/**/*
+
+/**/etc/cas/logs/*
diff --git a/Jenkinsfile b/Jenkinsfile
new file mode 100644
index 0000000..57c5f87
--- /dev/null
+++ b/Jenkinsfile
@@ -0,0 +1,102 @@
+pipeline {
+  agent any
+
+  triggers {
+    // 每30分钟检查一下GIT仓库
+    pollSCM '*/30 * * * *'
+  }
+
+  options {
+    // 禁止因Multibranch pipeline index动作触发构建
+    overrideIndexTriggers false
+    // 禁止并发构建
+    disableConcurrentBuilds()
+    // 构建结果保留天数10天,最多保留10个
+    buildDiscarder logRotator(daysToKeepStr: '30', numToKeepStr: '10')
+    // 控制台打出时间戳
+    // timestamps()
+    // 构建超时设置:15分钟
+    timeout(30)
+  }
+
+  stages {
+
+    stage('Build & Test') {
+
+      steps {
+        withMaven(
+          // Jenkins全局工具设置的Maven的名字
+          maven: 'Maven3',
+          // Jenkins全局工具设置的JDK的名字,有JDK6,JDK7,JDK8可选
+          jdk: 'JDK8',
+          // 指定maven本地仓库路径为项目working dir下的.local-m2-reop,可以避免因并发构建导致本地仓库混乱的问题
+          mavenLocalRepo: '.local-m2-repo'
+        ) {
+          sh 'mvn clean package -DskipTests -DskipITs'
+        }
+      }
+
+    }
+
+    stage('Push nexus') {
+
+      when {
+        // 只有当前分支是develop时才执行本Stage
+        branch 'develop'
+        beforeAgent true
+      }
+
+      steps {
+        withMaven(
+          maven: 'Maven3',
+          jdk: 'JDK8',
+          mavenLocalRepo: '.local-m2-repo',
+          // 只是Deploy,因此把所有options都关闭掉(默认是都开启的)
+          publisherStrategy: 'EXPLICIT'
+        ) {
+          sh 'mvn clean package deploy -DskipTests -DskipITs'
+        }
+      }
+
+    }
+
+    stage ('Push harbor') {
+
+      when {
+        branch 'develop'
+        beforeAgent true
+      }
+
+      steps {
+        withMaven(
+          maven: 'Maven3',
+          jdk: 'JDK8',
+          mavenLocalRepo: '.local-m2-repo',
+          publisherStrategy: 'EXPLICIT'
+        ) {
+          sh 'mvn clean package -DskipTests -DskipITs dockerfile:build dockerfile:push'
+        }
+      }
+
+    }
+
+  }
+
+  post {
+    always {
+      // 发送邮件到本次change所包含的committer
+      emailext recipientProviders: [developers()],
+        subject: "Pipeline [${currentBuild.fullDisplayName}] built ${currentBuild.currentResult}",
+        body: "More details: ${currentBuild.absoluteUrl}"
+
+    }
+    cleanup {
+      // 清空workspace,节省Jenkins服务器磁盘空间
+      echo 'Cleanup workspace'
+      withMaven(maven: 'Maven3', jdk: 'JDK8', mavenLocalRepo: '.local-m2-repo') {
+        sh 'mvn clean'
+      }
+
+    }
+  }
+}
diff --git a/base/api/.gitignore b/base/api/.gitignore
new file mode 100644
index 0000000..b83d222
--- /dev/null
+++ b/base/api/.gitignore
@@ -0,0 +1 @@
+/target/
diff --git a/base/api/pom.xml b/base/api/pom.xml
new file mode 100644
index 0000000..8505604
--- /dev/null
+++ b/base/api/pom.xml
@@ -0,0 +1,110 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.supwisdom.institute</groupId>
+    <artifactId>sw-backend-parent</artifactId>
+    <version>0.0.1</version>
+    <relativePath>../../</relativePath>
+  </parent>
+
+  <groupId>com.supwisdom.institute</groupId>
+  <artifactId>sw-backend-base-api</artifactId>
+  <version>0.0.1</version>
+  <packaging>jar</packaging>
+
+  <name>Supwisdom Backend Framework Base API</name>
+  <description>Supwisdom Backend Framework Base API project</description>
+
+  <dependencies>
+
+    <dependency>
+      <groupId>org.projectlombok</groupId>
+      <artifactId>lombok</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>javax.servlet-api</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-web</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>com.supwisdom.institute</groupId>
+      <artifactId>sw-backend-common-core</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.supwisdom.institute</groupId>
+      <artifactId>sw-backend-common-utils</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.supwisdom.institute</groupId>
+      <artifactId>sw-backend-common-framework</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>com.supwisdom.institute</groupId>
+      <artifactId>sw-backend-base-domain</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-release-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-failsafe-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/admin/AdminAccountController.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/admin/AdminAccountController.java
new file mode 100644
index 0000000..2b2efb3
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/admin/AdminAccountController.java
@@ -0,0 +1,583 @@
+package com.supwisdom.institute.backend.base.api.v1.controller.admin;
+
+import java.util.HashMap;
+import java.util.List;
+
+import io.swagger.annotations.Api;
+import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.http.HttpStatus;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.AccountCreateRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.AccountDeleteBatchRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.AccountQueryRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.AccountRelateGroupsRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.AccountRelateRolesRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.AccountRelatedGroupsRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.AccountRelatedRolesRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.AccountUpdateRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.AccountCreateResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.AccountDeleteBatchResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.AccountLoadResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.AccountQueryResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.AccountRelateGroupsResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.AccountRelateRolesResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.AccountRelatedGroupsResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.AccountRelatedRolesResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.AccountRemoveResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.AccountUpdateResponseData;
+import com.supwisdom.institute.backend.base.domain.entity.Account;
+import com.supwisdom.institute.backend.base.domain.entity.AccountGroup;
+import com.supwisdom.institute.backend.base.domain.entity.AccountRole;
+import com.supwisdom.institute.backend.base.domain.service.AccountService;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.DefaultApiResponse;
+
+@Api(value = "BaseAdminAccount", tags = { "BaseAdminAccount" }, description = "帐号的操作接口")
+@Slf4j
+@RestController
+@RequestMapping("/v1/admin/accounts")
+public class AdminAccountController {
+  
+  @Autowired
+  private AccountService accountService;
+
+  /**
+   * 
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:8081/api/v1/admin/accounts'
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:8081/api/v1/admin/accounts?pageIndex=2&pageSize=50'
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:8081/api/v1/admin/accounts?pageIndex=0&pageSize=20&mapBean[username]=username&mapBean[name]=name&mapBean[status]=1'
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:8081/api/v1/admin/accounts?pageIndex=0&pageSize=20&mapBean[username]=username&mapBean[name]=name&mapBean[status]=0'
+   * 
+   * response success:
+   * 
+   * <pre>
+   * {
+   *   "pageIndex":0,
+   *   "pageSize":20,
+   *   "mapBean":null,
+   *   "pageCount":1,
+   *   "recordCount":1,
+   *   "items":[
+   *     {
+   *       "id":"ff80808164feb8990164feba0de50000",
+   *       "companyId":"1",
+   *       "deleted":false,
+   *       "addAccount":"account","addTime":"2018-08-03T07:39:23.000+0000",
+   *       "editAccount":null,"editTime":null,
+   *       "deleteAccount":null,"deleteTime":null,
+   *       "accountname":"test001",
+   *       "password":"test001",
+   *       "enabled":true,
+   *       "accountNonExpired":true,
+   *       "accountNonLocked":true,
+   *       "credentialsNonExpired":true,
+   *       "name":"测试001",
+   *       "status":"1",
+   *       "mobile":null,
+   *       "email":null
+   *     }
+   *   ]
+   * }
+   * </pre>
+   * 
+   * response error 401:
+   * 
+   * <pre>
+   * {
+   *   "timestamp":"2018-08-03T08:48:25.777+0000",
+   *   "status":401,
+   *   "error":"Http Status 401",
+   *   "message":"Unauthorized",
+   *   "path":"/api/v1/admin/accounts"
+   * }
+   * </pre>
+   * 
+   * @param pagerRequestModel
+   * @return
+   */
+  @GetMapping(produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<AccountQueryResponseData> query(AccountQueryRequest queryRequest) {
+
+    Page<Account> page = accountService.selectPageList(
+        queryRequest.isLoadAll(), 
+        queryRequest.getPageIndex(), 
+        queryRequest.getPageSize(),
+        queryRequest.getMapBean(),
+        queryRequest.getOrderBy());
+
+    AccountQueryResponseData data = AccountQueryResponseData.of(queryRequest).build(page);
+
+    return new DefaultApiResponse<AccountQueryResponseData>(data);
+  }
+
+  /**
+   * 
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:8081/api/v1/admin/accounts/1'
+   * 
+   * response success:
+   * 
+   * <pre>
+   * {
+   *   "id":"ff80808164feb8990164feba0de50000",
+   *   "companyId":"1",
+   *   "deleted":false,
+   *   "addAccount":"account","addTime":"2018-08-03T07:39:23.000+0000",
+   *   "editAccount":null,"editTime":null,
+   *   "deleteAccount":null,"deleteTime":null,
+   *   "username":"test001",
+   *   "password":"test001",
+   *   "enabled":true,
+   *   "accountNonExpired":true,
+   *   "accountNonLocked":true,
+   *   "credentialsNonExpired":true,
+   *   "name":"测试001",
+   *   "status":"1",
+   *   "mobile":null,
+   *   "email":null
+   * }
+   * </pre>
+   * 
+   * response error 401:
+   * 
+   * <pre>
+   * {
+   *   "timestamp":"2018-08-03T08:43:26.080+0000",
+   *   "status":401,
+   *   "error":"Http Status 401",
+   *   "message":"Unauthorized",
+   *   "path":"/api/v1/admin/accounts/ff80808164fecf640164fed269480000"
+   * }
+   * </pre>
+   * 
+   * response error 500:
+   * 
+   * <pre>
+   * {
+   *   "timestamp":"2018-08-03T07:44:07.963+0000",
+   *   "status":500,
+   *   "error":"Internal Server Error",
+   *   "exception":"java.lang.RuntimeException",
+   *   "message":"exception.get.domain.not.exist",
+   *   "path":"/api/v1/admin/accounts/1"
+   * }
+   * </pre>
+   * 
+   * @param id
+   * @return
+   */
+  @GetMapping(path = "/{id}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<AccountLoadResponseData> load(@PathVariable("id") String id) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Account account = accountService.selectById(id);
+
+    if (account == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+    
+    AccountLoadResponseData data = AccountLoadResponseData.of(account);
+
+    return new DefaultApiResponse<AccountLoadResponseData>(data);
+  }
+
+  /**
+   * 
+   * curl -i -s -X POST -H 'Content-Type:application/json' -H 'Accept:application/json' 'http://localhost:8081/api/v1/admin/accounts' \
+   * -d '{"accountname":"test001","password":"test001","enabled":true,"accountNonExpired":true,"accountNonLocked":true,"credentialsNonExpired":true,"name":"测试001","status":"1"}'
+   * 
+   * response success:
+   * 
+   * <pre>
+   * {
+   *   "success":"info.create.success"
+   * }
+   * </pre>
+   * 
+   * response error 401:
+   * 
+   * <pre>
+   * {
+   *   "timestamp":"2018-08-03T08:48:25.777+0000",
+   *   "status":401,
+   *   "error":"Http Status 401",
+   *   "message":"Unauthorized",
+   *   "path":"/api/v1/admin/accounts"
+   * }
+   * </pre>
+   * 
+   * response error: // FIXME: save error
+   * 
+   * <pre>
+   * {
+   *   "timestamp":"2018-08-03T07:45:43.436+0000",
+   *   "status":500,
+   *   "error":"Internal Server Error",
+   *   "exception":"org.springframework.dao.DataIntegrityViolationException",
+   *   "message":"could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement",
+   *   "path":"/api/v1/admin/accounts"
+   * }
+   * </pre>
+   * 
+   * @param account
+   * @return
+   */
+  @PostMapping(consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<AccountCreateResponseData> create(
+      @RequestBody AccountCreateRequest createRequest) {
+    
+    // FIXME: 验证数据有效性
+    
+    Account account = createRequest.getEntity();
+    
+    if (account.getPassword() !=null && account.getPassword().length() > 0 && !account.getPassword().startsWith("{")) {
+      //account.setPassword(passwordEncoder.encode(account.getPassword()));
+    }
+
+    Account ret = accountService.insert(account);
+    
+    AccountCreateResponseData data = AccountCreateResponseData.build(ret);
+
+    return new DefaultApiResponse<AccountCreateResponseData>(data);
+  }
+
+  /**
+   * 
+   * curl -i -s -X PUT -H 'Content-Type:application/json' -H 'Accept:application/json' 'http://localhost:8081/api/v1/admin/accounts' \
+   * -d '{"id":"1","status":"0"}'
+   * 
+   * response success:
+   * 
+   * <pre>
+   * {
+   *   "success":"info.update.success"
+   * }
+   * </pre>
+   * 
+   * response error 401:
+   * 
+   * <pre>
+   * {
+   *   "timestamp":"2018-08-03T08:48:25.777+0000",
+   *   "status":401,
+   *   "error":"Http Status 401",
+   *   "message":"Unauthorized",
+   *   "path":"/api/v1/admin/accounts"
+   * }
+   * </pre>
+   * 
+   * curl -i -s -X PUT -H 'Content-Type:application/json' -H 'Accept:application/json' 'http://localhost:8081/api/v1/admin/accounts' \
+   * -d '{"status":"0"}'
+   * 
+   * response error:
+   * 
+   * <pre>
+   * {
+   *   "timestamp":"2018-08-03T07:50:52.327+0000",
+   *   "status":500,
+   *   "error":"Internal Server Error",
+   *   "exception":"java.lang.RuntimeException",
+   *   "message":"exception.update.id.must.not.empty",
+   *   "path":"/api/v1/admin/accounts"
+   * }
+   * </pre>
+   * 
+   * curl -i -s -X PUT -H 'Content-Type:application/json' -H 'Accept:application/json' 'http://localhost:8081/api/v1/admin/accounts' \
+   * -d '{"id":"1","status":"0"}'
+   * 
+   * response error:
+   * 
+   * <pre>
+   * {
+   *   "timestamp":"2018-08-03T07:48:24.774+0000",
+   *   "status":500,
+   *   "error":"Internal Server Error",
+   *   "exception":"java.lang.RuntimeException",
+   *   "message":"exception.update.domain.not.exist",
+   *   "path":"/api/v1/admin/accounts"
+   * }
+   * </pre>
+   * 
+   * @param account
+   * @return
+   */
+  @PutMapping(path = "/{id}", consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<AccountUpdateResponseData> update(
+      @PathVariable("id") String id, 
+      @RequestBody AccountUpdateRequest updateRequest) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.update.id.must.not.empty");
+    }
+
+    Account tmp = accountService.selectById(id);
+    if (tmp == null) {
+      throw new RuntimeException("exception.update.domain.not.exist");
+    }
+
+    Account account = updateRequest.getEntity();
+    account.setId(id);
+
+    if (account.getPassword() !=null && account.getPassword().length() > 0 && !account.getPassword().startsWith("{")) {
+      //account.setPassword(passwordEncoder.encode(account.getPassword()));
+    }
+
+    account = EntityUtils.merge(tmp, account);
+
+    Account ret = accountService.update(account);
+
+    AccountUpdateResponseData data = AccountUpdateResponseData.build(ret);
+    
+    return new DefaultApiResponse<AccountUpdateResponseData>(data);
+  }
+  
+
+  /**
+   * 
+   * curl -i -s -X DELETE -H 'Accept:application/json' 'http://localhost:8081/api/v1/admin/accounts/1'
+   * 
+   * response success:
+   * 
+   * <pre>
+   * {
+   *   "success":"info.delete.success"
+   * }
+   * </pre>
+   * 
+   * response error 401:
+   * 
+   * <pre>
+   * {
+   *   "timestamp":"2018-08-03T08:48:25.777+0000",
+   *   "status":401,
+   *   "error":"Http Status 401",
+   *   "message":"Unauthorized",
+   *   "path":"/api/v1/admin/accounts/1"
+   * }
+   * </pre>
+   * 
+   * response error 500:
+   * 
+   * <pre>
+   * {
+   *   "timestamp":"2018-08-03T08:03:16.364+0000",
+   *   "status":500,
+   *   "error":"Internal Server Error",
+   *   "exception":"java.lang.RuntimeException",
+   *   "message":"exception.delete.domain.not.exist",
+   *   "path":"/api/v1/admin/accounts/1"
+   * }
+   * </pre>
+   * 
+   * @param id
+   * @return
+   */
+  @DeleteMapping(path = "/{id}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<AccountRemoveResponseData> delete(
+      @PathVariable("id") String id) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.delete.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Account tmp = accountService.selectById(id);
+    if (tmp == null) {
+      throw new RuntimeException("exception.delete.domain.not.exist"); // FIXME: RestException
+    }
+
+    accountService.deleteById(id);
+
+    AccountRemoveResponseData data = AccountRemoveResponseData.build(tmp);
+    return new DefaultApiResponse<AccountRemoveResponseData>(data);
+  }
+  
+  @DeleteMapping(path = "/batch", consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<AccountDeleteBatchResponseData> deleteBatch(
+      @RequestBody AccountDeleteBatchRequest deleteBatchRequest) {
+    
+    List<String> ids = deleteBatchRequest.getIds();
+    
+    accountService.deleteBatch(ids);
+    
+    AccountDeleteBatchResponseData data = AccountDeleteBatchResponseData.build(ids);
+    return new DefaultApiResponse<AccountDeleteBatchResponseData>(data);
+  }
+
+  /**
+   * 
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:8081/api/v1/admin/accounts/1/groups'
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:8081/api/v1/admin/accounts/1/groups?pageIndex=2&pageSize=50'
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:8081/api/v1/admin/accounts/1/groups?pageIndex=0&pageSize=20&mapBean[groupCode]=groupCode&mapBean[groupName]=groupName'
+   * 
+   * 
+   * 
+   * @param id
+   * @param pagerRequestModel
+   * @return
+   */
+  @RequestMapping(method = RequestMethod.GET, path = "/{id}/groups", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public DefaultApiResponse<AccountRelatedGroupsResponseData> accountGroups(
+      @PathVariable("id") String id, 
+      AccountRelatedGroupsRequest request) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Account account = accountService.selectById(id);
+
+    if (account == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    if (request.getMapBean() == null) {
+      request.setMapBean(new HashMap<String, Object>());
+    }
+    request.getMapBean().put("accountId", account.getId());
+
+    Page<AccountGroup> page = accountService.selectAccountGroups(request.getPageIndex(),
+        request.getPageSize(), request.getMapBean());
+
+    AccountRelatedGroupsResponseData data = AccountRelatedGroupsResponseData.of(request).build(page);
+
+    return new DefaultApiResponse<AccountRelatedGroupsResponseData>(data);
+  }
+
+  /**
+   * 
+   * curl -i -s -X POST -H 'Content-Type:application/json' -H 'Accept:application/json' 'http://localhost:8081/api/v1/admin/accounts/1/groups' \
+   * -d '{"groupAccounts":[{"groupId":"1"},{"groupId":"2"}]}'
+   * 
+   * 
+   * @param id
+   * @param groupAccounts
+   * @return
+   */
+  @RequestMapping(method = RequestMethod.POST, path = "/{id}/groups", consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public DefaultApiResponse<AccountRelateGroupsResponseData> relateGroups(
+      @PathVariable("id") String id, 
+      @RequestBody AccountRelateGroupsRequest accountGroups) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Account tmp = accountService.selectById(id);
+
+    if (tmp == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    accountService.relateAccountGroups(tmp, accountGroups.getAccountGroups());
+
+    AccountRelateGroupsResponseData data = AccountRelateGroupsResponseData.of("info.relate.success");
+
+    return new DefaultApiResponse<AccountRelateGroupsResponseData>(data);
+  }
+
+  /**
+   * 
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:8081/api/v1/admin/accounts/1/roles'
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:8081/api/v1/admin/accounts/1/roles?pageIndex=2&pageSize=50'
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:8081/api/v1/admin/accounts/1/roles?pageIndex=0&pageSize=20&mapBean[roleCode]=roleCode&mapBean[roleName]=roleName'
+   * 
+   * 
+   * 
+   * @param id
+   * @param pagerRequestModel
+   * @return
+   */
+  @RequestMapping(method = RequestMethod.GET, path = "/{id}/roles", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public DefaultApiResponse<AccountRelatedRolesResponseData> accountRoles(
+      @PathVariable("id") String id, 
+      AccountRelatedRolesRequest request) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Account account = accountService.selectById(id);
+
+    if (account == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    if (request.getMapBean() == null) {
+      request.setMapBean(new HashMap<String, Object>());
+    }
+    request.getMapBean().put("accountId", account.getId());
+
+    Page<AccountRole> page = accountService.selectAccountRoles(request.getPageIndex(),
+        request.getPageSize(), request.getMapBean());
+
+    AccountRelatedRolesResponseData data = AccountRelatedRolesResponseData.of(request).build(page);
+
+    return new DefaultApiResponse<AccountRelatedRolesResponseData>(data);
+  }
+
+  /**
+   * 
+   * curl -i -s -X POST -H 'Content-Type:application/json' -H 'Accept:application/json' 'http://localhost:8081/api/v1/admin/accounts/1/roles' \
+   * -d '{"accountRoles":[{"roleId":"1"},{"roleId":"2"}]}'
+   * 
+   * 
+   * @param id
+   * @param accountRoles
+   * @return
+   */
+  @RequestMapping(method = RequestMethod.POST, path = "/{id}/roles", consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public DefaultApiResponse<AccountRelateRolesResponseData> relateRoles(
+      @PathVariable("id") String id, 
+      @RequestBody AccountRelateRolesRequest accountRoles) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Account account = accountService.selectById(id);
+
+    if (account == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    accountService.relateAccountRoles(account, accountRoles.getAccountRoles());
+
+    AccountRelateRolesResponseData data = AccountRelateRolesResponseData.of("info.relate.success");
+
+    return new DefaultApiResponse<AccountRelateRolesResponseData>(data);
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/admin/AdminApplicationController.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/admin/AdminApplicationController.java
new file mode 100644
index 0000000..02fb24b
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/admin/AdminApplicationController.java
@@ -0,0 +1,165 @@
+package com.supwisdom.institute.backend.base.api.v1.controller.admin;
+
+import java.util.List;
+
+import io.swagger.annotations.Api;
+import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.http.HttpStatus;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.ApplicationCreateRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.ApplicationDeleteBatchRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.ApplicationQueryRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.ApplicationUpdateRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.ApplicationCreateResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.ApplicationDeleteBatchResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.ApplicationLoadResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.ApplicationQueryResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.ApplicationRemoveResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.ApplicationUpdateResponseData;
+import com.supwisdom.institute.backend.base.domain.entity.Application;
+import com.supwisdom.institute.backend.base.domain.service.ApplicationService;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.DefaultApiResponse;
+
+@Api(value = "BaseAdminApplication", tags = { "BaseAdminApplication" }, description = "应用的操作接口")
+@Slf4j
+@RestController
+@RequestMapping("/v1/admin/applications")
+public class AdminApplicationController {
+
+  @Autowired
+  private ApplicationService applicationService;
+  
+
+  @GetMapping(produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<ApplicationQueryResponseData> query(ApplicationQueryRequest queryRequest) {
+
+    Page<Application> page = applicationService.selectPageList(
+        queryRequest.isLoadAll(), 
+        queryRequest.getPageIndex(), 
+        queryRequest.getPageSize(),
+        queryRequest.getMapBean(),
+        queryRequest.getOrderBy());
+
+    ApplicationQueryResponseData data = ApplicationQueryResponseData.of(queryRequest).build(page);
+
+    return new DefaultApiResponse<ApplicationQueryResponseData>(data);
+  }
+  
+  @GetMapping(path = "/{id}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<ApplicationLoadResponseData> load(@PathVariable("id") String id) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Application application = applicationService.selectById(id);
+
+    if (application == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+    
+    ApplicationLoadResponseData data = ApplicationLoadResponseData.of(application);
+
+    return new DefaultApiResponse<ApplicationLoadResponseData>(data);
+  }
+
+  @PostMapping(consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<ApplicationCreateResponseData> create(
+      @RequestBody ApplicationCreateRequest createRequest) {
+    
+    // FIXME: 验证数据有效性
+    
+    Application application = createRequest.getEntity();
+    
+    Application ret = applicationService.insert(application);
+    
+    ApplicationCreateResponseData data = ApplicationCreateResponseData.build(ret);
+
+    return new DefaultApiResponse<ApplicationCreateResponseData>(data);
+  }
+  
+  @PutMapping(path = "/{id}", consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<ApplicationUpdateResponseData> update(
+      @PathVariable("id") String id, 
+      @RequestBody ApplicationUpdateRequest updateRequest) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.update.id.must.not.empty");
+    }
+
+    Application tmp = applicationService.selectById(id);
+    if (tmp == null) {
+      throw new RuntimeException("exception.update.domain.not.exist");
+    }
+
+    Application application = updateRequest.getEntity();
+    application.setId(id);
+
+    application = EntityUtils.merge(tmp, application);
+
+    Application ret = applicationService.update(application);
+
+    ApplicationUpdateResponseData data = ApplicationUpdateResponseData.build(ret);
+    
+    return new DefaultApiResponse<ApplicationUpdateResponseData>(data);
+  }
+
+  @DeleteMapping(path = "/{id}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<ApplicationRemoveResponseData> delete(
+      @PathVariable("id") String id) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.delete.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Application tmp = applicationService.selectById(id);
+    if (tmp == null) {
+      throw new RuntimeException("exception.delete.domain.not.exist"); // FIXME: RestException
+    }
+
+    applicationService.deleteById(id);
+
+    ApplicationRemoveResponseData data = ApplicationRemoveResponseData.build(tmp);
+    return new DefaultApiResponse<ApplicationRemoveResponseData>(data);
+  }
+
+  @DeleteMapping(path = "/batch", consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<ApplicationDeleteBatchResponseData> deleteBatch(
+      @RequestBody ApplicationDeleteBatchRequest deleteBatchRequest) {
+    
+    List<String> ids = deleteBatchRequest.getIds();
+    
+    applicationService.deleteBatch(ids);
+    
+    ApplicationDeleteBatchResponseData data = ApplicationDeleteBatchResponseData.build(ids);
+    return new DefaultApiResponse<ApplicationDeleteBatchResponseData>(data);
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/admin/AdminConfigController.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/admin/AdminConfigController.java
new file mode 100644
index 0000000..4b38209
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/admin/AdminConfigController.java
@@ -0,0 +1,198 @@
+package com.supwisdom.institute.backend.base.api.v1.controller.admin;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.http.HttpStatus;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.ConfigCreateRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.ConfigQueryRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.ConfigUpdateRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.ConfigCreateResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.ConfigLoadResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.ConfigQueryResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.ConfigUpdateResponseData;
+import com.supwisdom.institute.backend.base.domain.entity.Config;
+import com.supwisdom.institute.backend.base.domain.exception.ConfigException;
+import com.supwisdom.institute.backend.base.domain.service.ConfigService;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.DefaultApiResponse;
+
+@Api(value = "BaseAdminConfig", tags = { "BaseAdminConfig" }, description = "配置项的操作接口")
+@Slf4j
+@RestController
+@RequestMapping("/v1/admin/configs")
+public class AdminConfigController {
+  
+  @Autowired
+  private ConfigService configService;
+
+
+  /**
+   * @param configQueryRequest
+   * @return
+   */
+  @ApiOperation(value = "查询配置列表", notes = "查询配置列表", nickname = "systemAdminConfigQuery")
+  @ApiImplicitParams({
+    @ApiImplicitParam(name = "loadAll", value = "是否加载全部", required = true, dataType = "boolean", paramType = "query", defaultValue = "false"),
+    @ApiImplicitParam(name = "pageIndex", value = "分页 - 页码", required = true, dataType = "int", paramType = "query", defaultValue = "0", example = "0"),
+    @ApiImplicitParam(name = "pageSize", value = "分页 - 每页记录数", required = true, dataType = "int", paramType = "query", defaultValue = "20", example = "20"),
+    @ApiImplicitParam(name = "mapBean[deleted]", value = "查询条件 - 删除状态 (精确)", required = false, dataType = "boolean", paramType = "query"),
+    @ApiImplicitParam(name = "mapBean[categoryCode]", value = "查询条件 - 分类代码 (精确)", required = false, dataType = "string", paramType = "query"),
+    @ApiImplicitParam(name = "mapBean[categoryName]", value = "查询条件 - 分类名称 (模糊)", required = false, dataType = "string", paramType = "query"),
+    @ApiImplicitParam(name = "mapBean[name]", value = "查询条件 - 名称 (模糊)", required = false, dataType = "string", paramType = "query"),
+    @ApiImplicitParam(name = "mapBean[description]", value = "查询条件 - 描述 (模糊)", required = false, dataType = "string", paramType = "query"),
+    @ApiImplicitParam(name = "mapBean[configKey]", value = "查询条件 - 配置Key (精确)", required = false, dataType = "string", paramType = "query"),
+  })
+  @RequestMapping(method = RequestMethod.GET, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<ConfigQueryResponseData> query(ConfigQueryRequest configQueryRequest) {
+    
+    Page<Config> page = configService.selectPageList(
+        configQueryRequest.isLoadAll(),
+        configQueryRequest.getPageIndex(),
+        configQueryRequest.getPageSize(),
+        configQueryRequest.getMapBean(),
+        configQueryRequest.getOrderBy());
+
+    ConfigQueryResponseData resp = ConfigQueryResponseData.of(configQueryRequest).build(page);
+    
+    return new DefaultApiResponse<ConfigQueryResponseData>(resp);
+  }
+
+  /**
+   * @param id
+   * @return
+   */
+  @ApiOperation(value = "根据ID获取配置项", notes = "根据ID获取配置项", nickname="systemAdminConfigLoad")
+  @RequestMapping(method = RequestMethod.GET, path = "/{id}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<ConfigLoadResponseData> load(
+      @PathVariable("id") String id) {
+    
+    if (id == null || id.length() == 0) {
+      throw new ConfigException().newInstance("exception.get.id.must.not.empty");
+    }
+    
+    Config config = configService.selectById(id);
+    
+    if (config == null) {
+      throw new ConfigException().newInstance("exception.get.domain.not.exist");
+    }
+
+    ConfigLoadResponseData resp = ConfigLoadResponseData.build(config);
+    
+    return new DefaultApiResponse<ConfigLoadResponseData>(resp);
+  }
+  
+  /**
+   * @param configCreateRequest
+   * @return
+   */
+  @ApiOperation(value = "创建配置项", notes = "创建配置项", nickname = "systemAdminConfigCreate")
+  @RequestMapping(method = RequestMethod.POST, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.CREATED)
+  @ResponseBody
+  public DefaultApiResponse<ConfigCreateResponseData> create(
+      @RequestBody ConfigCreateRequest configCreateRequest) {
+
+    // FIXME: 验证数据有效性
+
+    Config entity = configCreateRequest.getEntity();
+
+    Config ret = configService.insert(entity);
+
+    ConfigCreateResponseData resp = ConfigCreateResponseData.build(ret);
+
+    return new DefaultApiResponse<ConfigCreateResponseData>(resp);
+  }
+
+  @ApiOperation(value = "更新配置项", notes = "更新配置项", nickname = "systemAdminConfigUpdate")
+  @RequestMapping(method = RequestMethod.PUT, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<ConfigUpdateResponseData> update(
+      @PathVariable("id") String id,
+      @RequestBody ConfigUpdateRequest configUpdateRequest) {
+
+    if (id == null || id.length() == 0) {
+      throw new ConfigException().newInstance("exception.update.id.must.not.empty");
+    }
+
+    Config tmp = configService.selectById(id);
+    if (tmp == null) {
+      throw new ConfigException().newInstance("exception.update.domain.not.exist");
+    }
+
+    if (!tmp.getEditable().booleanValue()) {
+      throw new ConfigException().newInstance("exception.editable.can.not.update");
+    }
+
+    Config entity = configUpdateRequest.getEntity();
+    entity.setId(id);
+
+    entity = EntityUtils.merge(tmp, entity);
+    
+//    if (tmp.getEditable().booleanValue() != entity.getEditable().booleanValue()) {
+//      throw new ConfigException().newInstance("exception.editable.can.not.update");
+//    }
+
+    entity.setEditable(true);  // 防止 可修改记录的 editable 被置为false
+
+    Config ret = configService.update(entity);
+
+    ConfigUpdateResponseData resp = ConfigUpdateResponseData.build(ret);
+    
+    return new DefaultApiResponse<ConfigUpdateResponseData>(resp);
+  }
+
+  
+  /**
+   * @param categoryCode
+   * @param configKey
+   * @return
+   */
+  @ApiOperation(value = "根据 categoryCode、configKey 获取配置项", notes = "根据 categoryCode、configKey 获取配置项", nickname = "systemAdminConfigLoadByCategoryKey")
+  @RequestMapping(method = RequestMethod.GET, path = "/loadByCategoryKey", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<ConfigLoadResponseData> loadByCategoryKey(
+      @RequestParam("categoryCode") String categoryCode, 
+      @RequestParam("configKey") String configKey
+      ) {
+    
+    if (categoryCode == null || categoryCode.length() == 0) {
+      throw new ConfigException().newInstance("exception.load.params.must.not.empty");
+    }
+    if (configKey == null || configKey.length() == 0) {
+      throw new ConfigException().newInstance("exception.load.params.must.not.empty");
+    }
+    
+    Config config = configService.selectByCategoryKey(categoryCode, configKey);
+    
+    if (config == null) {
+      throw new ConfigException().newInstance("exception.load.domain.not.exist");
+    }
+
+    ConfigLoadResponseData resp = ConfigLoadResponseData.build(config);
+    
+    return new DefaultApiResponse<ConfigLoadResponseData>(resp);
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/admin/AdminGroupController.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/admin/AdminGroupController.java
new file mode 100644
index 0000000..c9a28d7
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/admin/AdminGroupController.java
@@ -0,0 +1,270 @@
+package com.supwisdom.institute.backend.base.api.v1.controller.admin;
+
+import java.util.HashMap;
+
+import io.swagger.annotations.Api;
+import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.http.HttpStatus;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.GroupCreateRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.GroupQueryRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.GroupRelateAccountsRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.GroupRelateRolesRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.GroupRelatedAccountsRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.GroupRelatedRolesRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.GroupUpdateRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.GroupCreateResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.GroupLoadResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.GroupQueryResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.GroupRelateAccountsResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.GroupRelateRolesResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.GroupRelatedAccountsResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.GroupRelatedRolesResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.GroupRemoveResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.GroupUpdateResponseData;
+import com.supwisdom.institute.backend.base.domain.entity.AccountGroup;
+import com.supwisdom.institute.backend.base.domain.entity.Group;
+import com.supwisdom.institute.backend.base.domain.entity.GroupRole;
+import com.supwisdom.institute.backend.base.domain.service.GroupService;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.DefaultApiResponse;
+
+@Api(value = "BaseAdminGroup", tags = { "BaseAdminGroup" }, description = "用户组的操作接口")
+@Slf4j
+@RestController
+@RequestMapping("/v1/admin/groups")
+public class AdminGroupController {
+
+  @Autowired
+  private GroupService groupService;
+
+  @GetMapping(produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<GroupQueryResponseData> query(GroupQueryRequest queryRequest) {
+
+    Page<Group> page = groupService.selectPageList(
+        queryRequest.isLoadAll(), 
+        queryRequest.getPageIndex(), 
+        queryRequest.getPageSize(),
+        queryRequest.getMapBean(),
+        queryRequest.getOrderBy());
+
+    GroupQueryResponseData data = GroupQueryResponseData.of(queryRequest).build(page);
+
+    return new DefaultApiResponse<GroupQueryResponseData>(data);
+  }
+  
+  @GetMapping(path = "/{id}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<GroupLoadResponseData> load(@PathVariable("id") String id) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Group group = groupService.selectById(id);
+
+    if (group == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+    
+    GroupLoadResponseData data = GroupLoadResponseData.of(group);
+
+    return new DefaultApiResponse<GroupLoadResponseData>(data);
+  }
+
+  @PostMapping(consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<GroupCreateResponseData> create(
+      @RequestBody GroupCreateRequest createRequest) {
+    
+    // FIXME: 验证数据有效性
+    
+    Group group = createRequest.getEntity();
+    
+    Group ret = groupService.insert(group);
+    
+    GroupCreateResponseData data = GroupCreateResponseData.build(ret);
+
+    return new DefaultApiResponse<GroupCreateResponseData>(data);
+  }
+  
+  @PutMapping(path = "/{id}", consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<GroupUpdateResponseData> update(
+      @PathVariable("id") String id, 
+      @RequestBody GroupUpdateRequest updateRequest) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.update.id.must.not.empty");
+    }
+
+    Group tmp = groupService.selectById(id);
+    if (tmp == null) {
+      throw new RuntimeException("exception.update.domain.not.exist");
+    }
+
+    Group group = updateRequest.getEntity();
+    group.setId(id);
+
+    group = EntityUtils.merge(tmp, group);
+
+    Group ret = groupService.update(group);
+
+    GroupUpdateResponseData data = GroupUpdateResponseData.build(ret);
+    
+    return new DefaultApiResponse<GroupUpdateResponseData>(data);
+  }
+
+  @DeleteMapping(path = "/{id}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<GroupRemoveResponseData> delete(
+      @PathVariable("id") String id) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.delete.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Group tmp = groupService.selectById(id);
+    if (tmp == null) {
+      throw new RuntimeException("exception.delete.domain.not.exist"); // FIXME: RestException
+    }
+
+    groupService.deleteById(id);
+
+    GroupRemoveResponseData data = GroupRemoveResponseData.build(tmp);
+    return new DefaultApiResponse<GroupRemoveResponseData>(data);
+  }
+
+  
+  
+  @RequestMapping(method = RequestMethod.GET, path = "/{id}/accounts", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public DefaultApiResponse<GroupRelatedAccountsResponseData> groupAccounts(
+      @PathVariable("id") String id, 
+      GroupRelatedAccountsRequest request) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Group group = groupService.selectById(id);
+
+    if (group == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    if (request.getMapBean() == null) {
+      request.setMapBean(new HashMap<String, Object>());
+    }
+    request.getMapBean().put("groupId", group.getId());
+
+    Page<AccountGroup> page = groupService.selectGroupAccounts(
+        request.getPageIndex(),
+        request.getPageSize(), 
+        request.getMapBean());
+
+    GroupRelatedAccountsResponseData data = GroupRelatedAccountsResponseData.of(request).build(page);
+
+    return new DefaultApiResponse<GroupRelatedAccountsResponseData>(data);
+  }
+
+  @RequestMapping(method = RequestMethod.POST, path = "/{id}/accounts", consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public DefaultApiResponse<GroupRelateAccountsResponseData> relateAccounts(
+      @PathVariable("id") String id, 
+      @RequestBody GroupRelateAccountsRequest groupAccounts) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Group group = groupService.selectById(id);
+
+    if (group == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    groupService.relateGroupAccounts(group, groupAccounts.getGroupAccounts());
+
+    GroupRelateAccountsResponseData data = GroupRelateAccountsResponseData.of("info.relate.success");
+
+    return new DefaultApiResponse<GroupRelateAccountsResponseData>(data);
+  }
+
+  
+  @RequestMapping(method = RequestMethod.GET, path = "/{id}/roles", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public DefaultApiResponse<GroupRelatedRolesResponseData> groupRoles(
+      @PathVariable("id") String id, 
+      GroupRelatedRolesRequest request) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Group group = groupService.selectById(id);
+
+    if (group == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    if (request.getMapBean() == null) {
+      request.setMapBean(new HashMap<String, Object>());
+    }
+    request.getMapBean().put("groupId", group.getId());
+
+    Page<GroupRole> page = groupService.selectGroupRoles(
+        request.getPageIndex(),
+        request.getPageSize(), 
+        request.getMapBean());
+
+    GroupRelatedRolesResponseData data = GroupRelatedRolesResponseData.of(request).build(page);
+
+    return new DefaultApiResponse<GroupRelatedRolesResponseData>(data);
+  }
+
+  @RequestMapping(method = RequestMethod.POST, path = "/{id}/roles", consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public DefaultApiResponse<GroupRelateRolesResponseData> relateRoles(
+      @PathVariable("id") String id, 
+      @RequestBody GroupRelateRolesRequest groupRoles) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Group group = groupService.selectById(id);
+
+    if (group == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    groupService.relateGroupRoles(group, groupRoles.getGroupRoles());
+
+    GroupRelateRolesResponseData data = GroupRelateRolesResponseData.of("info.relate.success");
+
+    return new DefaultApiResponse<GroupRelateRolesResponseData>(data);
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/admin/AdminMenuController.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/admin/AdminMenuController.java
new file mode 100644
index 0000000..d4ebef0
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/admin/AdminMenuController.java
@@ -0,0 +1,209 @@
+package com.supwisdom.institute.backend.base.api.v1.controller.admin;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import io.swagger.annotations.Api;
+import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.http.HttpStatus;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.PermissionCreateRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.PermissionDeleteBatchRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.PermissionQueryRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.PermissionUpdateRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.PermissionCreateResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.PermissionDeleteBatchResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.PermissionLoadResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.PermissionQueryResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.PermissionRemoveResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.PermissionTreeResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.PermissionUpdateResponseData;
+import com.supwisdom.institute.backend.base.domain.entity.Permission;
+import com.supwisdom.institute.backend.base.domain.entity.PermissionResource;
+import com.supwisdom.institute.backend.base.domain.model.PermissionTreeNode;
+import com.supwisdom.institute.backend.base.domain.service.PermissionService;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.DefaultApiResponse;
+
+@Api(value = "BaseAdminMenu", tags = { "BaseAdminMenu" }, description = "菜单的操作接口")
+@Slf4j
+@RestController
+@RequestMapping("/v1/admin/menus")
+public class AdminMenuController {
+
+  @Autowired
+  private PermissionService permissionService;
+
+  @GetMapping(produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<PermissionQueryResponseData> query(PermissionQueryRequest queryRequest) {
+    
+    if (queryRequest.getMapBean() == null) {
+      queryRequest.setMapBean(new HashMap<String, Object>());
+    }
+    queryRequest.getMapBean().put("type", Permission.TYPE_MENU);
+
+    Page<Permission> page = permissionService.selectPageList(
+        queryRequest.isLoadAll(), 
+        queryRequest.getPageIndex(), 
+        queryRequest.getPageSize(),
+        queryRequest.getMapBean(),
+        queryRequest.getOrderBy());
+
+    PermissionQueryResponseData data = PermissionQueryResponseData.of(queryRequest).build(page);
+
+    return new DefaultApiResponse<PermissionQueryResponseData>(data);
+  }
+  
+  @GetMapping(path = "/{id}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<PermissionLoadResponseData> load(@PathVariable("id") String id) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Permission permission = permissionService.selectById(id);
+
+    if (permission == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+    if (!Permission.TYPE_MENU.equals(permission.getType())) {
+      throw new RuntimeException("exception.get.domain.type.error");
+    }
+    
+    List<String> resourceIds = new ArrayList<>();
+    
+    Map<String, Object> mapBean = new HashMap<>();
+    mapBean.put("permissionId", id);
+    Page<PermissionResource> permissionResources = permissionService.selectPermissionResources(true, 0, 0, mapBean);
+    for (PermissionResource permissionResource : permissionResources) {
+      resourceIds.add(permissionResource.getResourceId());
+    }
+    
+    PermissionLoadResponseData data = PermissionLoadResponseData.of(permission, resourceIds);
+
+    return new DefaultApiResponse<PermissionLoadResponseData>(data);
+  }
+
+  @PostMapping(consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<PermissionCreateResponseData> create(
+      @RequestBody PermissionCreateRequest createRequest) {
+    
+    // FIXME: 验证数据有效性
+    
+    Permission permission = createRequest.getEntity();
+    permission.setType(Permission.TYPE_MENU);
+    
+    Permission ret = permissionService.insert(permission);
+    
+    PermissionCreateResponseData data = PermissionCreateResponseData.build(ret);
+
+    return new DefaultApiResponse<PermissionCreateResponseData>(data);
+  }
+  
+  @PutMapping(path = "/{id}", consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<PermissionUpdateResponseData> update(
+      @PathVariable("id") String id, 
+      @RequestBody PermissionUpdateRequest updateRequest) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.update.id.must.not.empty");
+    }
+
+    Permission tmp = permissionService.selectById(id);
+    if (tmp == null) {
+      throw new RuntimeException("exception.update.domain.not.exist");
+    }
+    if (!Permission.TYPE_MENU.equals(tmp.getType())) {
+      throw new RuntimeException("exception.update.domain.type.error");
+    }
+
+    Permission permission = updateRequest.getEntity();
+    permission.setId(id);
+
+    permission = EntityUtils.merge(tmp, permission);
+
+    Permission ret = permissionService.update(permission);
+
+    PermissionUpdateResponseData data = PermissionUpdateResponseData.build(ret);
+    
+    return new DefaultApiResponse<PermissionUpdateResponseData>(data);
+  }
+
+  @DeleteMapping(path = "/{id}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<PermissionRemoveResponseData> delete(
+      @PathVariable("id") String id) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.delete.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Permission tmp = permissionService.selectById(id);
+    if (tmp == null) {
+      throw new RuntimeException("exception.delete.domain.not.exist"); // FIXME: RestException
+    }
+    if (!Permission.TYPE_MENU.equals(tmp.getType())) {
+      throw new RuntimeException("exception.delete.domain.type.error");
+    }
+
+    permissionService.deleteById(id);
+
+    PermissionRemoveResponseData data = PermissionRemoveResponseData.build(tmp);
+    return new DefaultApiResponse<PermissionRemoveResponseData>(data);
+  }
+
+
+  @DeleteMapping(path = "/batch", consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<PermissionDeleteBatchResponseData> deleteBatch(
+      @RequestBody PermissionDeleteBatchRequest deleteBatchRequest) {
+    
+    List<String> ids = deleteBatchRequest.getIds();
+    
+    permissionService.deleteBatch(ids);
+    
+    PermissionDeleteBatchResponseData data = PermissionDeleteBatchResponseData.build(ids);
+    return new DefaultApiResponse<PermissionDeleteBatchResponseData>(data);
+  }
+  
+  @GetMapping(path = "/tree", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<PermissionTreeResponseData> tree(
+      @RequestParam(name = "applicationId", required = false, defaultValue = Permission.APPLICATION_ID) String applicationId) {
+    
+    PermissionTreeNode tree = permissionService.selectPermissionTree(applicationId, Permission.TYPE_MENU);
+
+    PermissionTreeResponseData data = PermissionTreeResponseData.of(tree);
+
+    return new DefaultApiResponse<PermissionTreeResponseData>(data);
+  }
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/admin/AdminOperationController.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/admin/AdminOperationController.java
new file mode 100644
index 0000000..e84e2e0
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/admin/AdminOperationController.java
@@ -0,0 +1,177 @@
+package com.supwisdom.institute.backend.base.api.v1.controller.admin;
+
+import io.swagger.annotations.Api;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.http.HttpStatus;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.PermissionCreateRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.PermissionQueryRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.PermissionUpdateRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.PermissionCreateResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.PermissionLoadResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.PermissionQueryResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.PermissionRemoveResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.PermissionUpdateResponseData;
+import com.supwisdom.institute.backend.base.domain.entity.Permission;
+import com.supwisdom.institute.backend.base.domain.entity.PermissionResource;
+import com.supwisdom.institute.backend.base.domain.service.PermissionService;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.DefaultApiResponse;
+
+@Api(value = "BaseAdminOperation", tags = { "BaseAdminOperation" }, description = "操作的操作接口")
+@Slf4j
+@RestController
+@RequestMapping("/v1/admin/operations")
+public class AdminOperationController {
+
+  @Autowired
+  private PermissionService permissionService;
+
+  @GetMapping(produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<PermissionQueryResponseData> query(PermissionQueryRequest queryRequest) {
+    
+    if (queryRequest.getMapBean() == null) {
+      queryRequest.setMapBean(new HashMap<String, Object>());
+    }
+    queryRequest.getMapBean().put("type", Permission.TYPE_OPERATION);
+
+    Page<Permission> page = permissionService.selectPageList(
+        queryRequest.isLoadAll(), 
+        queryRequest.getPageIndex(), 
+        queryRequest.getPageSize(),
+        queryRequest.getMapBean(),
+        queryRequest.getOrderBy());
+
+    PermissionQueryResponseData data = PermissionQueryResponseData.of(queryRequest).build(page);
+
+    return new DefaultApiResponse<PermissionQueryResponseData>(data);
+  }
+  
+  @GetMapping(path = "/{id}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<PermissionLoadResponseData> load(@PathVariable("id") String id) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Permission permission = permissionService.selectById(id);
+
+    if (permission == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+    if (!Permission.TYPE_OPERATION.equals(permission.getType())) {
+      throw new RuntimeException("exception.get.domain.type.error");
+    }
+
+    List<String> resourceIds = new ArrayList<>();
+    
+    Map<String, Object> mapBean = new HashMap<>();
+    mapBean.put("permissionId", id);
+    Page<PermissionResource> permissionResources = permissionService.selectPermissionResources(true, 0, 0, mapBean);
+    for (PermissionResource permissionResource : permissionResources) {
+      resourceIds.add(permissionResource.getResourceId());
+    }
+    
+    PermissionLoadResponseData data = PermissionLoadResponseData.of(permission, resourceIds);
+
+    return new DefaultApiResponse<PermissionLoadResponseData>(data);
+  }
+
+  @PostMapping(consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<PermissionCreateResponseData> create(
+      @RequestBody PermissionCreateRequest createRequest) {
+    
+    // FIXME: 验证数据有效性
+    
+    Permission permission = createRequest.getEntity();
+    permission.setType(Permission.TYPE_OPERATION);
+    
+    Permission ret = permissionService.insert(permission);
+    
+    PermissionCreateResponseData data = PermissionCreateResponseData.build(ret);
+
+    return new DefaultApiResponse<PermissionCreateResponseData>(data);
+  }
+  
+  @PutMapping(path = "/{id}", consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<PermissionUpdateResponseData> update(
+      @PathVariable("id") String id, 
+      @RequestBody PermissionUpdateRequest updateRequest) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.update.id.must.not.empty");
+    }
+
+    Permission tmp = permissionService.selectById(id);
+    if (tmp == null) {
+      throw new RuntimeException("exception.update.domain.not.exist");
+    }
+    if (!Permission.TYPE_OPERATION.equals(tmp.getType())) {
+      throw new RuntimeException("exception.update.domain.type.error");
+    }
+
+    Permission permission = updateRequest.getEntity();
+    permission.setId(id);
+
+    permission = EntityUtils.merge(tmp, permission);
+
+    Permission ret = permissionService.update(permission);
+
+    PermissionUpdateResponseData data = PermissionUpdateResponseData.build(ret);
+    
+    return new DefaultApiResponse<PermissionUpdateResponseData>(data);
+  }
+
+  @DeleteMapping(path = "/{id}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<PermissionRemoveResponseData> delete(
+      @PathVariable("id") String id) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.delete.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Permission tmp = permissionService.selectById(id);
+    if (tmp == null) {
+      throw new RuntimeException("exception.delete.domain.not.exist"); // FIXME: RestException
+    }
+    if (!Permission.TYPE_OPERATION.equals(tmp.getType())) {
+      throw new RuntimeException("exception.delete.domain.type.error");
+    }
+
+    permissionService.deleteById(id);
+
+    PermissionRemoveResponseData data = PermissionRemoveResponseData.build(tmp);
+    return new DefaultApiResponse<PermissionRemoveResponseData>(data);
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/admin/AdminPermissionController.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/admin/AdminPermissionController.java
new file mode 100644
index 0000000..a415095
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/admin/AdminPermissionController.java
@@ -0,0 +1,108 @@
+package com.supwisdom.institute.backend.base.api.v1.controller.admin;
+
+import java.util.HashMap;
+
+import io.swagger.annotations.Api;
+import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.http.HttpStatus;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.PermissionRelateResourcesRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.PermissionRelatedResourcesRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.PermissionRelateResourcesResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.PermissionRelatedResourcesResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.PermissionTreeResponseData;
+import com.supwisdom.institute.backend.base.domain.entity.Permission;
+import com.supwisdom.institute.backend.base.domain.entity.PermissionResource;
+import com.supwisdom.institute.backend.base.domain.model.PermissionTreeNode;
+import com.supwisdom.institute.backend.base.domain.service.PermissionService;
+import com.supwisdom.institute.backend.common.framework.vo.response.DefaultApiResponse;
+
+@Api(value = "BaseAdminPermission", tags = { "BaseAdminPermission" }, description = "权限的操作接口")
+@Slf4j
+@RestController
+@RequestMapping("/v1/admin/permissions")
+public class AdminPermissionController {
+
+  @Autowired
+  private PermissionService permissionService;
+
+  @GetMapping(path = "/tree", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<PermissionTreeResponseData> tree() {
+    
+    PermissionTreeNode tree = permissionService.selectPermissionTree(Permission.APPLICATION_ID, null);
+
+    PermissionTreeResponseData data = PermissionTreeResponseData.of(tree);
+
+    return new DefaultApiResponse<PermissionTreeResponseData>(data);
+  }
+
+  @RequestMapping(method = RequestMethod.GET, path = "/{id}/resources", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public DefaultApiResponse<PermissionRelatedResourcesResponseData> permissionResources(
+      @PathVariable("id") String id, 
+      PermissionRelatedResourcesRequest request) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Permission permission = permissionService.selectById(id);
+
+    if (permission == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    if (request.getMapBean() == null) {
+      request.setMapBean(new HashMap<String, Object>());
+    }
+    request.getMapBean().put("permissionId", permission.getId());
+
+    Page<PermissionResource> page = permissionService.selectPermissionResources(
+        request.isLoadAll(),
+        request.getPageIndex(),
+        request.getPageSize(), 
+        request.getMapBean());
+
+    PermissionRelatedResourcesResponseData data = PermissionRelatedResourcesResponseData.of(request).build(page);
+
+    return new DefaultApiResponse<PermissionRelatedResourcesResponseData>(data);
+  }
+
+  @RequestMapping(method = RequestMethod.POST, path = "/{id}/resources", consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public DefaultApiResponse<PermissionRelateResourcesResponseData> relateResources(
+      @PathVariable("id") String id, 
+      @RequestBody PermissionRelateResourcesRequest permissionResources) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Permission permission = permissionService.selectById(id);
+
+    if (permission == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    permissionService.relatePermissionResources(permission, permissionResources.getPermissionResources());
+
+    PermissionRelateResourcesResponseData data = PermissionRelateResourcesResponseData.of("info.relate.success");
+
+    return new DefaultApiResponse<PermissionRelateResourcesResponseData>(data);
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/admin/AdminResourceController.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/admin/AdminResourceController.java
new file mode 100644
index 0000000..5594269
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/admin/AdminResourceController.java
@@ -0,0 +1,146 @@
+package com.supwisdom.institute.backend.base.api.v1.controller.admin;
+
+import io.swagger.annotations.Api;
+import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.http.HttpStatus;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.ResourceCreateRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.ResourceQueryRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.ResourceUpdateRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.ResourceCreateResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.ResourceLoadResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.ResourceQueryResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.ResourceRemoveResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.ResourceUpdateResponseData;
+import com.supwisdom.institute.backend.base.domain.entity.Resource;
+import com.supwisdom.institute.backend.base.domain.service.ResourceService;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.DefaultApiResponse;
+
+@Api(value = "BaseAdminResource", tags = { "BaseAdminResource" }, description = "资源(API)的操作接口")
+@Slf4j
+@RestController
+@RequestMapping("/v1/admin/resources")
+public class AdminResourceController {
+
+  @Autowired
+  private ResourceService resourceService;
+
+  @GetMapping(produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<ResourceQueryResponseData> query(ResourceQueryRequest queryRequest) {
+
+    Page<Resource> page = resourceService.selectPageList(
+        queryRequest.isLoadAll(), 
+        queryRequest.getPageIndex(), 
+        queryRequest.getPageSize(),
+        queryRequest.getMapBean(),
+        queryRequest.getOrderBy());
+
+    ResourceQueryResponseData data = ResourceQueryResponseData.of(queryRequest).build(page);
+
+    return new DefaultApiResponse<ResourceQueryResponseData>(data);
+  }
+  
+  @GetMapping(path = "/{id}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<ResourceLoadResponseData> load(@PathVariable("id") String id) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Resource resource = resourceService.selectById(id);
+
+    if (resource == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+    
+    ResourceLoadResponseData data = ResourceLoadResponseData.of(resource);
+
+    return new DefaultApiResponse<ResourceLoadResponseData>(data);
+  }
+
+  @PostMapping(consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<ResourceCreateResponseData> create(
+      @RequestBody ResourceCreateRequest createRequest) {
+    
+    // FIXME: 验证数据有效性
+    
+    Resource resource = createRequest.getEntity();
+    
+    Resource ret = resourceService.insert(resource);
+    
+    ResourceCreateResponseData data = ResourceCreateResponseData.build(ret);
+
+    return new DefaultApiResponse<ResourceCreateResponseData>(data);
+  }
+  
+  @PutMapping(path = "/{id}", consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<ResourceUpdateResponseData> update(
+      @PathVariable("id") String id, 
+      @RequestBody ResourceUpdateRequest updateRequest) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.update.id.must.not.empty");
+    }
+
+    Resource tmp = resourceService.selectById(id);
+    if (tmp == null) {
+      throw new RuntimeException("exception.update.domain.not.exist");
+    }
+
+    Resource resource = updateRequest.getEntity();
+    resource.setId(id);
+
+    resource = EntityUtils.merge(tmp, resource);
+
+    Resource ret = resourceService.update(resource);
+
+    ResourceUpdateResponseData data = ResourceUpdateResponseData.build(ret);
+    
+    return new DefaultApiResponse<ResourceUpdateResponseData>(data);
+  }
+
+  @DeleteMapping(path = "/{id}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<ResourceRemoveResponseData> delete(
+      @PathVariable("id") String id) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.delete.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Resource tmp = resourceService.selectById(id);
+    if (tmp == null) {
+      throw new RuntimeException("exception.delete.domain.not.exist"); // FIXME: RestException
+    }
+
+    resourceService.deleteById(id);
+
+    ResourceRemoveResponseData data = ResourceRemoveResponseData.build(tmp);
+    return new DefaultApiResponse<ResourceRemoveResponseData>(data);
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/admin/AdminRoleController.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/admin/AdminRoleController.java
new file mode 100644
index 0000000..b45bc25
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/admin/AdminRoleController.java
@@ -0,0 +1,346 @@
+package com.supwisdom.institute.backend.base.api.v1.controller.admin;
+
+import java.util.HashMap;
+import java.util.List;
+
+import io.swagger.annotations.Api;
+import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.http.HttpStatus;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.RoleCreateRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.RoleDeleteBatchRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.RoleQueryRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.RoleRelateAccountsRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.RoleRelateGroupsRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.RoleRelatePermissionsRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.RoleRelatedAccountsRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.RoleRelatedGroupsRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.RoleRelatedPermissionsRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.RoleUpdateRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.RoleCreateResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.RoleDeleteBatchResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.RoleLoadResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.RoleQueryResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.RoleRelateAccountsResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.RoleRelateGroupsResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.RoleRelatePermissionsResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.RoleRelatedAccountsResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.RoleRelatedGroupsResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.RoleRelatedPermissionsResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.RoleRemoveResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.RoleUpdateResponseData;
+import com.supwisdom.institute.backend.base.domain.entity.AccountRole;
+import com.supwisdom.institute.backend.base.domain.entity.GroupRole;
+import com.supwisdom.institute.backend.base.domain.entity.Role;
+import com.supwisdom.institute.backend.base.domain.entity.RolePermission;
+import com.supwisdom.institute.backend.base.domain.service.RoleService;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.DefaultApiResponse;
+
+@Api(value = "BaseAdminRole", tags = { "BaseAdminRole" }, description = "角色的操作接口")
+@Slf4j
+@RestController
+@RequestMapping("/v1/admin/roles")
+public class AdminRoleController {
+
+  @Autowired
+  private RoleService roleService;
+
+  @GetMapping(produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<RoleQueryResponseData> query(RoleQueryRequest queryRequest) {
+
+    Page<Role> page = roleService.selectPageList(
+        queryRequest.isLoadAll(), 
+        queryRequest.getPageIndex(), 
+        queryRequest.getPageSize(),
+        queryRequest.getMapBean(),
+        queryRequest.getOrderBy());
+
+    RoleQueryResponseData data = RoleQueryResponseData.of(queryRequest).build(page);
+
+    return new DefaultApiResponse<RoleQueryResponseData>(data);
+  }
+  
+  @GetMapping(path = "/{id}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<RoleLoadResponseData> load(@PathVariable("id") String id) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Role role = roleService.selectById(id);
+
+    if (role == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+    
+    RoleLoadResponseData data = RoleLoadResponseData.of(role);
+
+    return new DefaultApiResponse<RoleLoadResponseData>(data);
+  }
+
+  @PostMapping(consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<RoleCreateResponseData> create(
+      @RequestBody RoleCreateRequest createRequest) {
+    
+    // FIXME: 验证数据有效性
+    
+    Role role = createRequest.getEntity();
+    
+    Role ret = roleService.insert(role);
+    
+    RoleCreateResponseData data = RoleCreateResponseData.build(ret);
+
+    return new DefaultApiResponse<RoleCreateResponseData>(data);
+  }
+  
+  @PutMapping(path = "/{id}", consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<RoleUpdateResponseData> update(
+      @PathVariable("id") String id, 
+      @RequestBody RoleUpdateRequest updateRequest) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.update.id.must.not.empty");
+    }
+
+    Role tmp = roleService.selectById(id);
+    if (tmp == null) {
+      throw new RuntimeException("exception.update.domain.not.exist");
+    }
+
+    Role role = updateRequest.getEntity();
+    role.setId(id);
+
+    role = EntityUtils.merge(tmp, role);
+
+    Role ret = roleService.update(role);
+
+    RoleUpdateResponseData data = RoleUpdateResponseData.build(ret);
+    
+    return new DefaultApiResponse<RoleUpdateResponseData>(data);
+  }
+
+  @DeleteMapping(path = "/{id}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<RoleRemoveResponseData> delete(
+      @PathVariable("id") String id) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.delete.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Role tmp = roleService.selectById(id);
+    if (tmp == null) {
+      throw new RuntimeException("exception.delete.domain.not.exist"); // FIXME: RestException
+    }
+
+    roleService.deleteById(id);
+
+    RoleRemoveResponseData data = RoleRemoveResponseData.build(tmp);
+    return new DefaultApiResponse<RoleRemoveResponseData>(data);
+  }
+
+  @DeleteMapping(path = "/batch", consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<RoleDeleteBatchResponseData> deleteBatch(
+      @RequestBody RoleDeleteBatchRequest deleteBatchRequest) {
+    
+    List<String> ids = deleteBatchRequest.getIds();
+    
+    roleService.deleteBatch(ids);
+    
+    RoleDeleteBatchResponseData data = RoleDeleteBatchResponseData.build(ids);
+    return new DefaultApiResponse<RoleDeleteBatchResponseData>(data);
+  }
+
+
+  @RequestMapping(method = RequestMethod.GET, path = "/{id}/accounts", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public DefaultApiResponse<RoleRelatedAccountsResponseData> roleAccounts(
+      @PathVariable("id") String id, 
+      RoleRelatedAccountsRequest request) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Role role = roleService.selectById(id);
+
+    if (role == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    if (request.getMapBean() == null) {
+      request.setMapBean(new HashMap<String, Object>());
+    }
+    request.getMapBean().put("roleId", role.getId());
+
+    Page<AccountRole> page = roleService.selectRoleAccounts(
+        request.getPageIndex(),
+        request.getPageSize(), 
+        request.getMapBean());
+
+    RoleRelatedAccountsResponseData data = RoleRelatedAccountsResponseData.of(request).build(page);
+
+    return new DefaultApiResponse<RoleRelatedAccountsResponseData>(data);
+  }
+
+  @RequestMapping(method = RequestMethod.POST, path = "/{id}/accounts", consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public DefaultApiResponse<RoleRelateAccountsResponseData> relateAccounts(
+      @PathVariable("id") String id, 
+      @RequestBody RoleRelateAccountsRequest roleAccounts) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Role role = roleService.selectById(id);
+
+    if (role == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    roleService.relateRoleAccounts(role, roleAccounts.getRoleAccounts());
+
+    RoleRelateAccountsResponseData data = RoleRelateAccountsResponseData.of("info.relate.success");
+
+    return new DefaultApiResponse<RoleRelateAccountsResponseData>(data);
+  }
+
+
+  @RequestMapping(method = RequestMethod.GET, path = "/{id}/groups", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public DefaultApiResponse<RoleRelatedGroupsResponseData> roleGroups(
+      @PathVariable("id") String id, 
+      RoleRelatedGroupsRequest request) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Role role = roleService.selectById(id);
+
+    if (role == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    if (request.getMapBean() == null) {
+      request.setMapBean(new HashMap<String, Object>());
+    }
+    request.getMapBean().put("roleId", role.getId());
+
+    Page<GroupRole> page = roleService.selectRoleGroups(
+        request.getPageIndex(),
+        request.getPageSize(), 
+        request.getMapBean());
+
+    RoleRelatedGroupsResponseData data = RoleRelatedGroupsResponseData.of(request).build(page);
+
+    return new DefaultApiResponse<RoleRelatedGroupsResponseData>(data);
+  }
+
+  @RequestMapping(method = RequestMethod.POST, path = "/{id}/groups", consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public DefaultApiResponse<RoleRelateGroupsResponseData> relateGroups(
+      @PathVariable("id") String id, 
+      @RequestBody RoleRelateGroupsRequest roleGroups) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Role role = roleService.selectById(id);
+
+    if (role == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    roleService.relateRoleGroups(role, roleGroups.getRoleGroups());
+
+    RoleRelateGroupsResponseData data = RoleRelateGroupsResponseData.of("info.relate.success");
+
+    return new DefaultApiResponse<RoleRelateGroupsResponseData>(data);
+  }
+
+
+  @RequestMapping(method = RequestMethod.GET, path = "/{id}/permissions", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public DefaultApiResponse<RoleRelatedPermissionsResponseData> rolePermissions(
+      @PathVariable("id") String id, 
+      RoleRelatedPermissionsRequest request) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Role role = roleService.selectById(id);
+
+    if (role == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    if (request.getMapBean() == null) {
+      request.setMapBean(new HashMap<String, Object>());
+    }
+    request.getMapBean().put("roleId", role.getId());
+
+    Page<RolePermission> page = roleService.selectRolePermissions(
+        request.getPageIndex(),
+        request.getPageSize(), 
+        request.getMapBean());
+
+    RoleRelatedPermissionsResponseData data = RoleRelatedPermissionsResponseData.of(request).build(page);
+
+    return new DefaultApiResponse<RoleRelatedPermissionsResponseData>(data);
+  }
+
+  @RequestMapping(method = RequestMethod.POST, path = "/{id}/permissions", consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public DefaultApiResponse<RoleRelatePermissionsResponseData> relatePermissions(
+      @PathVariable("id") String id, 
+      @RequestBody RoleRelatePermissionsRequest rolePermissions) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Role role = roleService.selectById(id);
+
+    if (role == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    roleService.relateRolePermissions(role, rolePermissions.getRolePermissions());
+
+    RoleRelatePermissionsResponseData data = RoleRelatePermissionsResponseData.of("info.relate.success");
+
+    return new DefaultApiResponse<RoleRelatePermissionsResponseData>(data);
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/admin/AdminRouteController.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/admin/AdminRouteController.java
new file mode 100644
index 0000000..a52334d
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/admin/AdminRouteController.java
@@ -0,0 +1,165 @@
+package com.supwisdom.institute.backend.base.api.v1.controller.admin;
+
+import java.util.List;
+
+import io.swagger.annotations.Api;
+import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.http.HttpStatus;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.RouteCreateRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.RouteDeleteBatchRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.RouteQueryRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.RouteUpdateRequest;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.RouteCreateResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.RouteDeleteBatchResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.RouteLoadResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.RouteQueryResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.RouteRemoveResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.response.RouteUpdateResponseData;
+import com.supwisdom.institute.backend.base.domain.entity.Route;
+import com.supwisdom.institute.backend.base.domain.service.RouteService;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.DefaultApiResponse;
+
+@Api(value = "BaseAdminRoute", tags = { "BaseAdminRoute" }, description = "路由信息的操作接口")
+@Slf4j
+@RestController
+@RequestMapping("/v1/admin/routes")
+public class AdminRouteController {
+
+  @Autowired
+  private RouteService routeService;
+  
+
+  @GetMapping(produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<RouteQueryResponseData> query(RouteQueryRequest queryRequest) {
+
+    Page<Route> page = routeService.selectPageList(
+        queryRequest.isLoadAll(), 
+        queryRequest.getPageIndex(), 
+        queryRequest.getPageSize(),
+        queryRequest.getMapBean(),
+        queryRequest.getOrderBy());
+
+    RouteQueryResponseData data = RouteQueryResponseData.of(queryRequest).build(page);
+
+    return new DefaultApiResponse<RouteQueryResponseData>(data);
+  }
+  
+  @GetMapping(path = "/{id}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<RouteLoadResponseData> load(@PathVariable("id") String id) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Route route = routeService.selectById(id);
+
+    if (route == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+    
+    RouteLoadResponseData data = RouteLoadResponseData.of(route);
+
+    return new DefaultApiResponse<RouteLoadResponseData>(data);
+  }
+
+  @PostMapping(consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<RouteCreateResponseData> create(
+      @RequestBody RouteCreateRequest createRequest) {
+    
+    // FIXME: 验证数据有效性
+    
+    Route route = createRequest.getEntity();
+    
+    Route ret = routeService.insert(route);
+    
+    RouteCreateResponseData data = RouteCreateResponseData.build(ret);
+
+    return new DefaultApiResponse<RouteCreateResponseData>(data);
+  }
+  
+  @PutMapping(path = "/{id}", consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<RouteUpdateResponseData> update(
+      @PathVariable("id") String id, 
+      @RequestBody RouteUpdateRequest updateRequest) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.update.id.must.not.empty");
+    }
+
+    Route tmp = routeService.selectById(id);
+    if (tmp == null) {
+      throw new RuntimeException("exception.update.domain.not.exist");
+    }
+
+    Route route = updateRequest.getEntity();
+    route.setId(id);
+
+    route = EntityUtils.merge(tmp, route);
+
+    Route ret = routeService.update(route);
+
+    RouteUpdateResponseData data = RouteUpdateResponseData.build(ret);
+    
+    return new DefaultApiResponse<RouteUpdateResponseData>(data);
+  }
+
+  @DeleteMapping(path = "/{id}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<RouteRemoveResponseData> delete(
+      @PathVariable("id") String id) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.delete.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Route tmp = routeService.selectById(id);
+    if (tmp == null) {
+      throw new RuntimeException("exception.delete.domain.not.exist"); // FIXME: RestException
+    }
+
+    routeService.deleteById(id);
+
+    RouteRemoveResponseData data = RouteRemoveResponseData.build(tmp);
+    return new DefaultApiResponse<RouteRemoveResponseData>(data);
+  }
+
+  @DeleteMapping(path = "/batch", consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<RouteDeleteBatchResponseData> deleteBatch(
+      @RequestBody RouteDeleteBatchRequest deleteBatchRequest) {
+    
+    List<String> ids = deleteBatchRequest.getIds();
+    
+    routeService.deleteBatch(ids);
+    
+    RouteDeleteBatchResponseData data = RouteDeleteBatchResponseData.build(ids);
+    return new DefaultApiResponse<RouteDeleteBatchResponseData>(data);
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/authn/AuthnController.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/authn/AuthnController.java
new file mode 100644
index 0000000..d79f298
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/controller/authn/AuthnController.java
@@ -0,0 +1,320 @@
+package com.supwisdom.institute.backend.base.api.v1.controller.authn;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import io.swagger.annotations.Api;
+import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.supwisdom.institute.backend.base.api.v1.vo.authn.response.AuthnAccountPermissionsResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.authn.response.AuthnAccountResourcesResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.authn.response.AuthnAccountResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.authn.response.AuthnAccountRolesResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.authn.response.AuthnApplicationsResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.authn.response.AuthnPermissionRoleListResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.authn.response.AuthnResourceRoleListResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.authn.response.AuthnRolesResponseData;
+import com.supwisdom.institute.backend.base.api.v1.vo.authn.response.AuthnRoutesResponseData;
+import com.supwisdom.institute.backend.base.domain.entity.Account;
+import com.supwisdom.institute.backend.base.domain.entity.Application;
+import com.supwisdom.institute.backend.base.domain.entity.Permission;
+import com.supwisdom.institute.backend.base.domain.entity.Resource;
+import com.supwisdom.institute.backend.base.domain.entity.Role;
+import com.supwisdom.institute.backend.base.domain.entity.Route;
+import com.supwisdom.institute.backend.base.domain.model.PermissionRoleSet;
+import com.supwisdom.institute.backend.base.domain.model.ResourceRoleSet;
+import com.supwisdom.institute.backend.base.domain.service.AccountService;
+import com.supwisdom.institute.backend.base.domain.service.ApplicationService;
+import com.supwisdom.institute.backend.base.domain.service.PermissionService;
+import com.supwisdom.institute.backend.base.domain.service.ResourceService;
+import com.supwisdom.institute.backend.base.domain.service.RoleService;
+import com.supwisdom.institute.backend.base.domain.service.RouteService;
+import com.supwisdom.institute.backend.common.framework.vo.response.DefaultApiResponse;
+
+@Api(value = "BaseAuthn", tags = { "BaseAuthn" }, description = "认证授权接口")
+@Slf4j
+@RestController
+@RequestMapping("/v1/authn")
+public class AuthnController {
+  
+  @Autowired
+  private AccountService accountService;
+  
+  @Autowired
+  private RoleService roleService;
+  
+  @Autowired
+  private ApplicationService applicationService;
+
+  @Autowired
+  private PermissionService permissionService;
+
+  @Autowired
+  private ResourceService resourceService;
+
+  @Autowired
+  private RouteService routeService;
+
+  @GetMapping(path = "/{username}/account", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<AuthnAccountResponseData> account(
+      @PathVariable("username") String username) {
+
+    if (username == null || username.length() == 0) {
+      throw new RuntimeException("exception.get.username.must.not.empty");
+    }
+
+    Account account = accountService.selectByUsername(username);
+
+    if (account == null) {
+      throw new RuntimeException("exception.get.account.not.exist");
+    }
+    
+    AuthnAccountResponseData data = AuthnAccountResponseData.of(account);
+
+    return new DefaultApiResponse<AuthnAccountResponseData>(data);
+  }
+  
+  @GetMapping(path = "/{username}/roles", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<AuthnAccountRolesResponseData> accountRoles(
+      @PathVariable("username") String username) {
+
+    if (username == null || username.length() == 0) {
+      throw new RuntimeException("exception.get.username.must.not.empty");
+    }
+
+    Account account = accountService.selectByUsername(username);
+
+    if (account == null) {
+      throw new RuntimeException("exception.get.account.not.exist");
+    }
+    
+    List<Role> roles = roleService.selectByUsername(username);
+    
+    AuthnAccountRolesResponseData data = AuthnAccountRolesResponseData.of(roles);
+
+    return new DefaultApiResponse<AuthnAccountRolesResponseData>(data);
+  }
+
+  @GetMapping(path = "/{username}/applications", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<AuthnAccountPermissionsResponseData> accountApplications(
+      @PathVariable("username") String username,
+      @RequestParam(name = "applicationId", required = false) String applicationId) {
+
+    if (username == null || username.length() == 0) {
+      throw new RuntimeException("exception.get.username.must.not.empty");
+    }
+
+    Account account = accountService.selectByUsername(username);
+
+    if (account == null) {
+      throw new RuntimeException("exception.get.account.not.exist");
+    }
+    
+    List<Permission> applications = permissionService.selectByUsername(username, null, Permission.TYPE_APPLICATION);
+    
+    AuthnAccountPermissionsResponseData data = AuthnAccountPermissionsResponseData.of(applications);
+
+    return new DefaultApiResponse<AuthnAccountPermissionsResponseData>(data);
+  }
+
+  @GetMapping(path = "/{username}/menus", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<AuthnAccountPermissionsResponseData> accountMenus(
+      @PathVariable("username") String username, 
+      @RequestParam(name = "applicationId", required = false) String applicationId) {
+
+    if (username == null || username.length() == 0) {
+      throw new RuntimeException("exception.get.username.must.not.empty");
+    }
+
+    Account account = accountService.selectByUsername(username);
+
+    if (account == null) {
+      throw new RuntimeException("exception.get.account.not.exist");
+    }
+    
+    List<Permission> menus = permissionService.selectByUsername(username, applicationId, Permission.TYPE_MENU);
+    
+    AuthnAccountPermissionsResponseData data = AuthnAccountPermissionsResponseData.of(menus);
+
+    return new DefaultApiResponse<AuthnAccountPermissionsResponseData>(data);
+  }
+
+  @GetMapping(path = "/{username}/operations", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<AuthnAccountPermissionsResponseData> accountOperations(
+      @PathVariable("username") String username, 
+      @RequestParam(name = "applicationId", required = false) String applicationId) {
+
+    if (username == null || username.length() == 0) {
+      throw new RuntimeException("exception.get.username.must.not.empty");
+    }
+
+    Account account = accountService.selectByUsername(username);
+
+    if (account == null) {
+      throw new RuntimeException("exception.get.account.not.exist");
+    }
+    
+    List<Permission> operations = permissionService.selectByUsername(username, applicationId, Permission.TYPE_OPERATION);
+    
+    AuthnAccountPermissionsResponseData data = AuthnAccountPermissionsResponseData.of(operations);
+
+    return new DefaultApiResponse<AuthnAccountPermissionsResponseData>(data);
+  }
+
+  @GetMapping(path = "/{username}/resources", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<AuthnAccountResourcesResponseData> accountResources(
+      @PathVariable("username") String username, 
+      @RequestParam(name = "applicationId", required = false) String applicationId) {
+
+    if (username == null || username.length() == 0) {
+      throw new RuntimeException("exception.get.username.must.not.empty");
+    }
+
+    Account account = accountService.selectByUsername(username);
+
+    if (account == null) {
+      throw new RuntimeException("exception.get.account.not.exist");
+    }
+    
+    List<Resource> resources = null;// FIXME: resourceService.selectByUsername(username, applicationId);
+    
+    AuthnAccountResourcesResponseData data = AuthnAccountResourcesResponseData.of(resources);
+
+    return new DefaultApiResponse<AuthnAccountResourcesResponseData>(data);
+  }
+  
+//  @GetMapping(path = "/resources", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+//  @ResponseStatus(value = HttpStatus.OK)
+//  @ResponseBody
+//  public DefaultApiResponse<AuthnResourceRoleListResponseData> applicationResources(
+//      @RequestParam(name = "applicationId", required = false) String applicationId) {
+//
+//    List<ResourceRoleSet> resourceRoleSets = resourceService.selectByApplication(applicationId);
+//    
+//    AuthnResourceRoleListResponseData data = AuthnResourceRoleListResponseData.of(resourceRoleSets);
+//
+//    return new DefaultApiResponse<AuthnResourceRoleListResponseData>(data);
+//  }
+
+
+  @GetMapping(path = "/applications", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<AuthnApplicationsResponseData> applications() {
+    
+    Map<String, Object> mapBean = new HashMap<>();
+    Map<String, String> orderBy = null;
+    
+    mapBean.put("status", "1");
+
+    List<Application> applications = applicationService.selectList(mapBean, orderBy);
+    
+    AuthnApplicationsResponseData data = AuthnApplicationsResponseData.of(applications);
+
+    return new DefaultApiResponse<AuthnApplicationsResponseData>(data);
+  }
+  
+  @GetMapping(path = "/roles", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<AuthnRolesResponseData> roles() {
+    
+    Map<String, Object> mapBean = new HashMap<>();
+    Map<String, String> orderBy = null;
+    
+    mapBean.put("status", "1");
+
+    List<Role> roles = roleService.selectList(mapBean, orderBy);
+    
+    AuthnRolesResponseData data = AuthnRolesResponseData.of(roles);
+
+    return new DefaultApiResponse<AuthnRolesResponseData>(data);
+  }
+  
+  
+  @GetMapping(path = "/permissionRoleSets", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<AuthnPermissionRoleListResponseData> permissionRoleSets(
+      @RequestParam(name = "applicationId", required = false, defaultValue = Permission.APPLICATION_ID) String applicationId) {
+    
+    Map<String, Object> mapBean = new HashMap<>();
+    
+    mapBean.put("status", "1");
+    if (applicationId != null && !applicationId.isEmpty()) {
+      mapBean.put("applicationId", applicationId);
+    }
+    
+    List<PermissionRoleSet> permissionRoleSets = permissionService.selectPermissionRoleSet(mapBean);
+    
+    AuthnPermissionRoleListResponseData data = AuthnPermissionRoleListResponseData.of(permissionRoleSets);
+
+    return new DefaultApiResponse<AuthnPermissionRoleListResponseData>(data);
+  }
+  
+  
+  @GetMapping(path = "/resourceRoleSets", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<AuthnResourceRoleListResponseData> resourceRoleSets(
+      @RequestParam(name = "applicationId", required = false, defaultValue = Permission.APPLICATION_ID) String applicationId) {
+    
+    Map<String, Object> mapBean = new HashMap<>();
+    
+    mapBean.put("status", "1");
+    if (applicationId != null && !applicationId.isEmpty()) {
+      mapBean.put("applicationId", applicationId);
+    }
+    
+    List<ResourceRoleSet> resourceRoleSets = resourceService.selectResourceRoleSet(mapBean);
+    
+    AuthnResourceRoleListResponseData data = AuthnResourceRoleListResponseData.of(resourceRoleSets);
+
+    return new DefaultApiResponse<AuthnResourceRoleListResponseData>(data);
+  }
+  
+  
+
+  @GetMapping(path = "/routes", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<AuthnRoutesResponseData> routes() {
+    
+    Map<String, Object> mapBean = new HashMap<>();
+    Map<String, String> orderBy = null;
+    
+    mapBean.put("status", "1");
+
+    List<Route> routes = routeService.selectList(mapBean, orderBy);
+    
+    AuthnRoutesResponseData data = AuthnRoutesResponseData.of(routes);
+
+    return new DefaultApiResponse<AuthnRoutesResponseData>(data);
+  }
+  
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/.gitkeep b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/.gitkeep
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/AccountCreateRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/AccountCreateRequest.java
new file mode 100644
index 0000000..bf8cfc7
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/AccountCreateRequest.java
@@ -0,0 +1,21 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import com.supwisdom.institute.backend.base.domain.entity.Account;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiCreateRequest;
+
+/**
+ * @author loie
+ */
+public class AccountCreateRequest extends Account implements IApiCreateRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 3708476811446308442L;
+
+  public Account getEntity() {
+    return EntityUtils.copy(this, new Account());
+  }
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/AccountDeleteBatchRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/AccountDeleteBatchRequest.java
new file mode 100644
index 0000000..d936658
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/AccountDeleteBatchRequest.java
@@ -0,0 +1,21 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiRequest;
+
+public class AccountDeleteBatchRequest implements IApiRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -5886243153431313069L;
+  
+  @Getter
+  @Setter
+  private List<String> ids;
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/AccountQueryRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/AccountQueryRequest.java
new file mode 100644
index 0000000..f31652f
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/AccountQueryRequest.java
@@ -0,0 +1,40 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Map;
+
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * @author loie
+ */
+public class AccountQueryRequest implements IApiQueryRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -8594346811904818135L;
+
+  @Getter
+  @Setter
+  private boolean loadAll = false;
+  @Getter
+  @Setter
+  private int pageIndex = 0;
+  @Getter
+  @Setter
+  private int pageSize = 20;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, Object> mapBean;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, String> orderBy;
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/AccountRelateGroupsRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/AccountRelateGroupsRequest.java
new file mode 100644
index 0000000..acb37d9
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/AccountRelateGroupsRequest.java
@@ -0,0 +1,22 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.base.domain.entity.AccountGroup;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiRequest;
+
+public class AccountRelateGroupsRequest implements IApiRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 5685421340396797488L;
+
+  @Getter
+  @Setter
+  private List<AccountGroup> accountGroups;
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/AccountRelateRolesRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/AccountRelateRolesRequest.java
new file mode 100644
index 0000000..dc59b3f
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/AccountRelateRolesRequest.java
@@ -0,0 +1,22 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.base.domain.entity.AccountRole;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiRequest;
+
+public class AccountRelateRolesRequest implements IApiRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 6612833201128786481L;
+  
+  @Getter
+  @Setter
+  private List<AccountRole> accountRoles;
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/AccountRelatedGroupsRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/AccountRelatedGroupsRequest.java
new file mode 100644
index 0000000..d63a859
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/AccountRelatedGroupsRequest.java
@@ -0,0 +1,40 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Map;
+
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * @author loie
+ */
+public class AccountRelatedGroupsRequest implements IApiQueryRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -1998794079137356318L;
+  
+  @Getter
+  @Setter
+  private boolean loadAll = false;
+  @Getter
+  @Setter
+  private int pageIndex = 0;
+  @Getter
+  @Setter
+  private int pageSize = 20;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, Object> mapBean;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, String> orderBy;
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/AccountRelatedRolesRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/AccountRelatedRolesRequest.java
new file mode 100644
index 0000000..d484452
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/AccountRelatedRolesRequest.java
@@ -0,0 +1,40 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Map;
+
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * @author loie
+ */
+public class AccountRelatedRolesRequest implements IApiQueryRequest {
+  
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -2118300523470868823L;
+
+  @Getter
+  @Setter
+  private boolean loadAll = false;
+  @Getter
+  @Setter
+  private int pageIndex = 0;
+  @Getter
+  @Setter
+  private int pageSize = 20;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, Object> mapBean;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, String> orderBy;
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/AccountUpdateRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/AccountUpdateRequest.java
new file mode 100644
index 0000000..795c687
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/AccountUpdateRequest.java
@@ -0,0 +1,28 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import com.supwisdom.institute.backend.base.domain.entity.Account;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiUpdateRequest;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class AccountUpdateRequest extends Account implements IApiUpdateRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -7297542338912221083L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  public Account getEntity() {
+    return EntityUtils.copy(this, new Account());
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/ApplicationCreateRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/ApplicationCreateRequest.java
new file mode 100644
index 0000000..f950918
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/ApplicationCreateRequest.java
@@ -0,0 +1,21 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import com.supwisdom.institute.backend.base.domain.entity.Application;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiCreateRequest;
+
+/**
+ * @author loie
+ */
+public class ApplicationCreateRequest extends Application implements IApiCreateRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 7619506409619195684L;
+
+  public Application getEntity() {
+    return EntityUtils.copy(this, new Application());
+  }
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/ApplicationDeleteBatchRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/ApplicationDeleteBatchRequest.java
new file mode 100644
index 0000000..62e69ed
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/ApplicationDeleteBatchRequest.java
@@ -0,0 +1,21 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiRequest;
+
+public class ApplicationDeleteBatchRequest implements IApiRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -8296636817441994821L;
+
+  @Getter
+  @Setter
+  private List<String> ids;
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/ApplicationQueryRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/ApplicationQueryRequest.java
new file mode 100644
index 0000000..b4b6ea1
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/ApplicationQueryRequest.java
@@ -0,0 +1,40 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Map;
+
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * @author loie
+ */
+public class ApplicationQueryRequest implements IApiQueryRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -4728297863070197861L;
+
+  @Getter
+  @Setter
+  private boolean loadAll = false;
+  @Getter
+  @Setter
+  private int pageIndex = 0;
+  @Getter
+  @Setter
+  private int pageSize = 20;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, Object> mapBean;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, String> orderBy;
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/ApplicationUpdateRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/ApplicationUpdateRequest.java
new file mode 100644
index 0000000..12f8a49
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/ApplicationUpdateRequest.java
@@ -0,0 +1,28 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import com.supwisdom.institute.backend.base.domain.entity.Application;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiUpdateRequest;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class ApplicationUpdateRequest extends Application implements IApiUpdateRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -2370390240675594737L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  public Application getEntity() {
+    return EntityUtils.copy(this, new Application());
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/ConfigCreateRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/ConfigCreateRequest.java
new file mode 100644
index 0000000..6506f3b
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/ConfigCreateRequest.java
@@ -0,0 +1,21 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import com.supwisdom.institute.backend.base.domain.entity.Config;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiCreateRequest;
+
+/**
+ * @author loie
+ */
+public class ConfigCreateRequest extends Config implements IApiCreateRequest {
+  
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 8380208871984763567L;
+
+  public Config getEntity() {
+    return EntityUtils.copy(this, new Config());
+  }
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/ConfigQueryRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/ConfigQueryRequest.java
new file mode 100644
index 0000000..53f2b29
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/ConfigQueryRequest.java
@@ -0,0 +1,40 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Map;
+
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * @author loie
+ */
+public class ConfigQueryRequest implements IApiQueryRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -1033044092932525382L;
+
+  @Getter
+  @Setter
+  private boolean loadAll = false;
+  @Getter
+  @Setter
+  private int pageIndex = 0;
+  @Getter
+  @Setter
+  private int pageSize = 20;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, Object> mapBean;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, String> orderBy;
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/ConfigUpdateRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/ConfigUpdateRequest.java
new file mode 100644
index 0000000..6fd4fc9
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/ConfigUpdateRequest.java
@@ -0,0 +1,29 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import com.supwisdom.institute.backend.base.domain.entity.Config;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiUpdateRequest;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class ConfigUpdateRequest extends Config implements IApiUpdateRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 6002556449210326472L;
+
+  @Getter
+  @Setter
+  private String id;
+  
+  
+  public Config getEntity() {
+    return EntityUtils.copy(this, new Config());
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/GroupCreateRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/GroupCreateRequest.java
new file mode 100644
index 0000000..8f60f9c
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/GroupCreateRequest.java
@@ -0,0 +1,21 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import com.supwisdom.institute.backend.base.domain.entity.Group;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiCreateRequest;
+
+/**
+ * @author loie
+ */
+public class GroupCreateRequest extends Group implements IApiCreateRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -5922604207821365121L;
+
+  public Group getEntity() {
+    return EntityUtils.copy(this, new Group());
+  }
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/GroupQueryRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/GroupQueryRequest.java
new file mode 100644
index 0000000..ea610b3
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/GroupQueryRequest.java
@@ -0,0 +1,40 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Map;
+
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * @author loie
+ */
+public class GroupQueryRequest implements IApiQueryRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -4023230222050081483L;
+
+  @Getter
+  @Setter
+  private boolean loadAll = false;
+  @Getter
+  @Setter
+  private int pageIndex = 0;
+  @Getter
+  @Setter
+  private int pageSize = 20;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, Object> mapBean;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, String> orderBy;
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/GroupRelateAccountsRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/GroupRelateAccountsRequest.java
new file mode 100644
index 0000000..8b81132
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/GroupRelateAccountsRequest.java
@@ -0,0 +1,22 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.base.domain.entity.AccountGroup;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiRequest;
+
+public class GroupRelateAccountsRequest implements IApiRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 440910731161851824L;
+
+  @Getter
+  @Setter
+  private List<AccountGroup> groupAccounts;
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/GroupRelateRolesRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/GroupRelateRolesRequest.java
new file mode 100644
index 0000000..52b4486
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/GroupRelateRolesRequest.java
@@ -0,0 +1,22 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.base.domain.entity.GroupRole;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiRequest;
+
+public class GroupRelateRolesRequest implements IApiRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 795337416058792163L;
+
+  @Getter
+  @Setter
+  private List<GroupRole> groupRoles;
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/GroupRelatedAccountsRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/GroupRelatedAccountsRequest.java
new file mode 100644
index 0000000..6a1b13d
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/GroupRelatedAccountsRequest.java
@@ -0,0 +1,40 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Map;
+
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * @author loie
+ */
+public class GroupRelatedAccountsRequest implements IApiQueryRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -3102006206798574212L;
+
+  @Getter
+  @Setter
+  private boolean loadAll = false;
+  @Getter
+  @Setter
+  private int pageIndex = 0;
+  @Getter
+  @Setter
+  private int pageSize = 20;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, Object> mapBean;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, String> orderBy;
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/GroupRelatedRolesRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/GroupRelatedRolesRequest.java
new file mode 100644
index 0000000..6000d6a
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/GroupRelatedRolesRequest.java
@@ -0,0 +1,40 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Map;
+
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * @author loie
+ */
+public class GroupRelatedRolesRequest implements IApiQueryRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -6642264903131667637L;
+
+  @Getter
+  @Setter
+  private boolean loadAll = false;
+  @Getter
+  @Setter
+  private int pageIndex = 0;
+  @Getter
+  @Setter
+  private int pageSize = 20;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, Object> mapBean;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, String> orderBy;
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/GroupUpdateRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/GroupUpdateRequest.java
new file mode 100644
index 0000000..63e82fd
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/GroupUpdateRequest.java
@@ -0,0 +1,28 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import com.supwisdom.institute.backend.base.domain.entity.Group;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiUpdateRequest;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class GroupUpdateRequest extends Group implements IApiUpdateRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -6406242462582902819L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  public Group getEntity() {
+    return EntityUtils.copy(this, new Group());
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/PermissionCreateRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/PermissionCreateRequest.java
new file mode 100644
index 0000000..e287f65
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/PermissionCreateRequest.java
@@ -0,0 +1,30 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.base.domain.entity.Permission;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiCreateRequest;
+
+/**
+ * @author loie
+ */
+public class PermissionCreateRequest extends Permission implements IApiCreateRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 8247861768124918116L;
+
+  @Getter
+  @Setter
+  private List<String> resourceIds;
+
+  public Permission getEntity() {
+    return EntityUtils.copy(this, new Permission());
+  }
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/PermissionDeleteBatchRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/PermissionDeleteBatchRequest.java
new file mode 100644
index 0000000..9777b30
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/PermissionDeleteBatchRequest.java
@@ -0,0 +1,21 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiRequest;
+
+public class PermissionDeleteBatchRequest implements IApiRequest {
+  
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -9180558051324597595L;
+  
+  @Getter
+  @Setter
+  private List<String> ids;
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/PermissionQueryRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/PermissionQueryRequest.java
new file mode 100644
index 0000000..d32f363
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/PermissionQueryRequest.java
@@ -0,0 +1,40 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Map;
+
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * @author loie
+ */
+public class PermissionQueryRequest implements IApiQueryRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -3944162671254599673L;
+
+  @Getter
+  @Setter
+  private boolean loadAll = false;
+  @Getter
+  @Setter
+  private int pageIndex = 0;
+  @Getter
+  @Setter
+  private int pageSize = 20;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, Object> mapBean;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, String> orderBy;
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/PermissionRelateResourcesRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/PermissionRelateResourcesRequest.java
new file mode 100644
index 0000000..ae09876
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/PermissionRelateResourcesRequest.java
@@ -0,0 +1,22 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import java.util.List;
+
+import com.supwisdom.institute.backend.base.domain.entity.PermissionResource;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiRequest;
+
+import lombok.Getter;
+import lombok.Setter;
+
+public class PermissionRelateResourcesRequest implements IApiRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -7016517754275684031L;
+
+  @Getter
+  @Setter
+  private List<PermissionResource> permissionResources;
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/PermissionRelatedResourcesRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/PermissionRelatedResourcesRequest.java
new file mode 100644
index 0000000..4419071
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/PermissionRelatedResourcesRequest.java
@@ -0,0 +1,40 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Map;
+
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * @author loie
+ */
+public class PermissionRelatedResourcesRequest implements IApiQueryRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -7634304817036292579L;
+
+  @Getter
+  @Setter
+  private boolean loadAll = false;
+  @Getter
+  @Setter
+  private int pageIndex = 0;
+  @Getter
+  @Setter
+  private int pageSize = 20;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, Object> mapBean;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, String> orderBy;
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/PermissionUpdateRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/PermissionUpdateRequest.java
new file mode 100644
index 0000000..00d28c4
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/PermissionUpdateRequest.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import java.util.List;
+
+import com.supwisdom.institute.backend.base.domain.entity.Permission;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiUpdateRequest;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class PermissionUpdateRequest extends Permission implements IApiUpdateRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -5923008482749557479L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  @Getter
+  @Setter
+  private List<String> resourceIds;
+
+  public Permission getEntity() {
+    return EntityUtils.copy(this, new Permission());
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/ResourceCreateRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/ResourceCreateRequest.java
new file mode 100644
index 0000000..3c17cac
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/ResourceCreateRequest.java
@@ -0,0 +1,21 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import com.supwisdom.institute.backend.base.domain.entity.Resource;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiCreateRequest;
+
+/**
+ * @author loie
+ */
+public class ResourceCreateRequest extends Resource implements IApiCreateRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -7999657157256566904L;
+
+  public Resource getEntity() {
+    return EntityUtils.copy(this, new Resource());
+  }
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/ResourceQueryRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/ResourceQueryRequest.java
new file mode 100644
index 0000000..7171557
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/ResourceQueryRequest.java
@@ -0,0 +1,40 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Map;
+
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * @author loie
+ */
+public class ResourceQueryRequest implements IApiQueryRequest {
+  
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -911488989591489730L;
+
+  @Getter
+  @Setter
+  private boolean loadAll = false;
+  @Getter
+  @Setter
+  private int pageIndex = 0;
+  @Getter
+  @Setter
+  private int pageSize = 20;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, Object> mapBean;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, String> orderBy;
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/ResourceUpdateRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/ResourceUpdateRequest.java
new file mode 100644
index 0000000..596cbd7
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/ResourceUpdateRequest.java
@@ -0,0 +1,28 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import com.supwisdom.institute.backend.base.domain.entity.Resource;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiUpdateRequest;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class ResourceUpdateRequest extends Resource implements IApiUpdateRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 8922380312193462613L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  public Resource getEntity() {
+    return EntityUtils.copy(this, new Resource());
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RoleCreateRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RoleCreateRequest.java
new file mode 100644
index 0000000..e91cfba
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RoleCreateRequest.java
@@ -0,0 +1,21 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import com.supwisdom.institute.backend.base.domain.entity.Role;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiCreateRequest;
+
+/**
+ * @author loie
+ */
+public class RoleCreateRequest extends Role implements IApiCreateRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -5891171833738647843L;
+
+  public Role getEntity() {
+    return EntityUtils.copy(this, new Role());
+  }
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RoleDeleteBatchRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RoleDeleteBatchRequest.java
new file mode 100644
index 0000000..94a23a5
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RoleDeleteBatchRequest.java
@@ -0,0 +1,21 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiRequest;
+
+public class RoleDeleteBatchRequest implements IApiRequest {
+  
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 3121491905067659896L;
+
+  @Getter
+  @Setter
+  private List<String> ids;
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RoleQueryRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RoleQueryRequest.java
new file mode 100644
index 0000000..db54ea4
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RoleQueryRequest.java
@@ -0,0 +1,40 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Map;
+
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * @author loie
+ */
+public class RoleQueryRequest implements IApiQueryRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -8717113985995182773L;
+  
+  @Getter
+  @Setter
+  private boolean loadAll = false;
+  @Getter
+  @Setter
+  private int pageIndex = 0;
+  @Getter
+  @Setter
+  private int pageSize = 20;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, Object> mapBean;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, String> orderBy;
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RoleRelateAccountsRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RoleRelateAccountsRequest.java
new file mode 100644
index 0000000..1fc5848
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RoleRelateAccountsRequest.java
@@ -0,0 +1,22 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.base.domain.entity.AccountRole;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiRequest;
+
+public class RoleRelateAccountsRequest implements IApiRequest {
+  
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -7935779803020137819L;
+
+  @Getter
+  @Setter
+  private List<AccountRole> roleAccounts;
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RoleRelateGroupsRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RoleRelateGroupsRequest.java
new file mode 100644
index 0000000..e400df4
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RoleRelateGroupsRequest.java
@@ -0,0 +1,22 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.base.domain.entity.GroupRole;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiRequest;
+
+public class RoleRelateGroupsRequest implements IApiRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 9206282464451171269L;
+  
+  @Getter
+  @Setter
+  private List<GroupRole> roleGroups;
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RoleRelatePermissionsRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RoleRelatePermissionsRequest.java
new file mode 100644
index 0000000..dc31c16
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RoleRelatePermissionsRequest.java
@@ -0,0 +1,22 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.base.domain.entity.RolePermission;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiRequest;
+
+public class RoleRelatePermissionsRequest implements IApiRequest {
+  
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -7524940232102183145L;
+
+  @Getter
+  @Setter
+  private List<RolePermission> rolePermissions;
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RoleRelatedAccountsRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RoleRelatedAccountsRequest.java
new file mode 100644
index 0000000..2d8b77e
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RoleRelatedAccountsRequest.java
@@ -0,0 +1,40 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Map;
+
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * @author loie
+ */
+public class RoleRelatedAccountsRequest implements IApiQueryRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -1798385113493396290L;
+
+  @Getter
+  @Setter
+  private boolean loadAll = false;
+  @Getter
+  @Setter
+  private int pageIndex = 0;
+  @Getter
+  @Setter
+  private int pageSize = 20;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, Object> mapBean;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, String> orderBy;
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RoleRelatedGroupsRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RoleRelatedGroupsRequest.java
new file mode 100644
index 0000000..b69aef5
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RoleRelatedGroupsRequest.java
@@ -0,0 +1,40 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Map;
+
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * @author loie
+ */
+public class RoleRelatedGroupsRequest implements IApiQueryRequest {
+  
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 7253046382409370442L;
+
+  @Getter
+  @Setter
+  private boolean loadAll = false;
+  @Getter
+  @Setter
+  private int pageIndex = 0;
+  @Getter
+  @Setter
+  private int pageSize = 20;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, Object> mapBean;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, String> orderBy;
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RoleRelatedPermissionsRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RoleRelatedPermissionsRequest.java
new file mode 100644
index 0000000..7ff22a2
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RoleRelatedPermissionsRequest.java
@@ -0,0 +1,40 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Map;
+
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * @author loie
+ */
+public class RoleRelatedPermissionsRequest implements IApiQueryRequest {
+  
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 5695303573516182415L;
+
+  @Getter
+  @Setter
+  private boolean loadAll = false;
+  @Getter
+  @Setter
+  private int pageIndex = 0;
+  @Getter
+  @Setter
+  private int pageSize = 20;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, Object> mapBean;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, String> orderBy;
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RoleUpdateRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RoleUpdateRequest.java
new file mode 100644
index 0000000..b1137db
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RoleUpdateRequest.java
@@ -0,0 +1,28 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import com.supwisdom.institute.backend.base.domain.entity.Role;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiUpdateRequest;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class RoleUpdateRequest extends Role implements IApiUpdateRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -7272388577177587142L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  public Role getEntity() {
+    return EntityUtils.copy(this, new Role());
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RouteCreateRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RouteCreateRequest.java
new file mode 100644
index 0000000..f1af807
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RouteCreateRequest.java
@@ -0,0 +1,21 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import com.supwisdom.institute.backend.base.domain.entity.Route;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiCreateRequest;
+
+/**
+ * @author loie
+ */
+public class RouteCreateRequest extends Route implements IApiCreateRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -1610189545107922L;
+
+  public Route getEntity() {
+    return EntityUtils.copy(this, new Route());
+  }
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RouteDeleteBatchRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RouteDeleteBatchRequest.java
new file mode 100644
index 0000000..af2657c
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RouteDeleteBatchRequest.java
@@ -0,0 +1,21 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiRequest;
+
+public class RouteDeleteBatchRequest implements IApiRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -6325676083300813571L;
+
+  @Getter
+  @Setter
+  private List<String> ids;
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RouteQueryRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RouteQueryRequest.java
new file mode 100644
index 0000000..b077d34
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RouteQueryRequest.java
@@ -0,0 +1,40 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Map;
+
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * @author loie
+ */
+public class RouteQueryRequest implements IApiQueryRequest {
+  
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 5501929058767629345L;
+
+  @Getter
+  @Setter
+  private boolean loadAll = false;
+  @Getter
+  @Setter
+  private int pageIndex = 0;
+  @Getter
+  @Setter
+  private int pageSize = 20;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, Object> mapBean;
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, String> orderBy;
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RouteUpdateRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RouteUpdateRequest.java
new file mode 100644
index 0000000..e96eb28
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/request/RouteUpdateRequest.java
@@ -0,0 +1,28 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.request;
+
+import com.supwisdom.institute.backend.base.domain.entity.Route;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiUpdateRequest;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class RouteUpdateRequest extends Route implements IApiUpdateRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -1999295284394866961L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  public Route getEntity() {
+    return EntityUtils.copy(this, new Route());
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/AccountCreateResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/AccountCreateResponseData.java
new file mode 100644
index 0000000..d4b91d8
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/AccountCreateResponseData.java
@@ -0,0 +1,33 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import com.supwisdom.institute.backend.base.domain.entity.Account;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiCreateResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class AccountCreateResponseData extends Account implements IApiCreateResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -8882701323050726169L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private AccountCreateResponseData() {
+
+  }
+
+  public static AccountCreateResponseData build(Account entity) {
+    AccountCreateResponseData data = new AccountCreateResponseData();
+
+    return EntityUtils.copy(entity, data);
+  }
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/AccountDeleteBatchResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/AccountDeleteBatchResponseData.java
new file mode 100644
index 0000000..6c471ee
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/AccountDeleteBatchResponseData.java
@@ -0,0 +1,35 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+/**
+ * @author loie
+ */
+public class AccountDeleteBatchResponseData implements IApiResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 8625399816385346436L;
+
+  @Getter
+  @Setter
+  private List<String> ids;
+
+  private AccountDeleteBatchResponseData() {
+    
+  }
+  
+  public static AccountDeleteBatchResponseData build(List<String> ids) {
+    AccountDeleteBatchResponseData data = new AccountDeleteBatchResponseData();
+    data.setIds(ids);
+    
+    return data;
+  }
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/AccountLoadResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/AccountLoadResponseData.java
new file mode 100644
index 0000000..a9f250d
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/AccountLoadResponseData.java
@@ -0,0 +1,33 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import com.supwisdom.institute.backend.base.domain.entity.Account;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiLoadResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class AccountLoadResponseData extends Account implements IApiLoadResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -8236721435440256832L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private AccountLoadResponseData() {
+
+  }
+
+  public static AccountLoadResponseData of(Account entity) {
+    AccountLoadResponseData data = new AccountLoadResponseData();
+    return EntityUtils.copy(entity, data);
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/AccountQueryResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/AccountQueryResponseData.java
new file mode 100644
index 0000000..a36d033
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/AccountQueryResponseData.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.data.domain.Page;
+
+import com.supwisdom.institute.backend.base.domain.entity.Account;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiQueryResponseData;
+
+/**
+ * @author loie
+ */
+public class AccountQueryResponseData implements IApiQueryResponseData<Account> {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 3949426339318397970L;
+
+  private AccountQueryResponseData(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean, Map<String, String> orderBy) {
+    this.loadAll = loadAll;
+    this.pageIndex = pageIndex;
+    this.pageSize = pageSize;
+    this.mapBean = mapBean;
+    this.orderBy = orderBy;
+  }
+
+  public static AccountQueryResponseData of(IApiQueryRequest queryRequest) {
+    AccountQueryResponseData configQueryResponse = new AccountQueryResponseData(
+        queryRequest.isLoadAll(), 
+        queryRequest.getPageIndex(), 
+        queryRequest.getPageSize(), 
+        queryRequest.getMapBean(), 
+        queryRequest.getOrderBy()
+    );
+    
+    return configQueryResponse;
+  }
+  
+  public AccountQueryResponseData build(Page<Account> page) {
+    this.currentItemCount = page.getNumberOfElements();
+    this.pageCount = page.getTotalPages();
+    this.recordCount = page.getTotalElements();
+    this.items = page.getContent();
+
+    return this;
+  }
+
+  @Getter
+  private boolean loadAll;
+  @Getter
+  private int pageIndex;
+  @Getter
+  private int pageSize;
+  @Getter
+  private Map<String, Object> mapBean;
+  @Getter
+  private Map<String, String> orderBy;
+  
+  @Getter
+  @Setter
+  private int pageCount;
+  @Getter
+  @Setter
+  private long recordCount;
+  
+  @Getter
+  @Setter
+  private int currentItemCount;
+  
+  @Getter
+  @Setter
+  private List<Account> items;
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/AccountRelateGroupsResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/AccountRelateGroupsResponseData.java
new file mode 100644
index 0000000..a269841
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/AccountRelateGroupsResponseData.java
@@ -0,0 +1,29 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+public class AccountRelateGroupsResponseData implements IApiResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -720047649055920794L;
+  
+  @Getter
+  @Setter
+  private String message;
+
+  public AccountRelateGroupsResponseData(String message) {
+    this.message = message;
+  }
+
+  public static AccountRelateGroupsResponseData of(String message) {
+    AccountRelateGroupsResponseData data = new AccountRelateGroupsResponseData(message);
+    
+    return data;
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/AccountRelateRolesResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/AccountRelateRolesResponseData.java
new file mode 100644
index 0000000..93895ff
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/AccountRelateRolesResponseData.java
@@ -0,0 +1,29 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+public class AccountRelateRolesResponseData implements IApiResponseData {
+  
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -5011147266134585651L;
+  
+  @Getter
+  @Setter
+  private String message;
+
+  public AccountRelateRolesResponseData(String message) {
+    this.message = message;
+  }
+
+  public static AccountRelateRolesResponseData of(String message) {
+    AccountRelateRolesResponseData data = new AccountRelateRolesResponseData(message);
+    
+    return data;
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/AccountRelatedGroupsResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/AccountRelatedGroupsResponseData.java
new file mode 100644
index 0000000..802437d
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/AccountRelatedGroupsResponseData.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.data.domain.Page;
+
+import com.supwisdom.institute.backend.base.domain.entity.AccountGroup;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiQueryResponseData;
+
+/**
+ * @author loie
+ */
+public class AccountRelatedGroupsResponseData implements IApiQueryResponseData<AccountGroup> {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 3949426339318397970L;
+
+  private AccountRelatedGroupsResponseData(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean, Map<String, String> orderBy) {
+    this.loadAll = loadAll;
+    this.pageIndex = pageIndex;
+    this.pageSize = pageSize;
+    this.mapBean = mapBean;
+    this.orderBy = orderBy;
+  }
+
+  public static AccountRelatedGroupsResponseData of(IApiQueryRequest queryRequest) {
+    AccountRelatedGroupsResponseData configQueryResponse = new AccountRelatedGroupsResponseData(
+        queryRequest.isLoadAll(), 
+        queryRequest.getPageIndex(), 
+        queryRequest.getPageSize(), 
+        queryRequest.getMapBean(), 
+        queryRequest.getOrderBy()
+    );
+    
+    return configQueryResponse;
+  }
+  
+  public AccountRelatedGroupsResponseData build(Page<AccountGroup> page) {
+    this.currentItemCount = page.getNumberOfElements();
+    this.pageCount = page.getTotalPages();
+    this.recordCount = page.getTotalElements();
+    this.items = page.getContent();
+
+    return this;
+  }
+
+  @Getter
+  private boolean loadAll;
+  @Getter
+  private int pageIndex;
+  @Getter
+  private int pageSize;
+  @Getter
+  private Map<String, Object> mapBean;
+  @Getter
+  private Map<String, String> orderBy;
+  
+  @Getter
+  @Setter
+  private int pageCount;
+  @Getter
+  @Setter
+  private long recordCount;
+  
+  @Getter
+  @Setter
+  private int currentItemCount;
+  
+  @Getter
+  @Setter
+  private List<AccountGroup> items;
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/AccountRelatedRolesResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/AccountRelatedRolesResponseData.java
new file mode 100644
index 0000000..5289a30
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/AccountRelatedRolesResponseData.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.data.domain.Page;
+
+import com.supwisdom.institute.backend.base.domain.entity.AccountRole;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiQueryResponseData;
+
+/**
+ * @author loie
+ */
+public class AccountRelatedRolesResponseData implements IApiQueryResponseData<AccountRole> {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 4933467585328478547L;
+
+  private AccountRelatedRolesResponseData(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean, Map<String, String> orderBy) {
+    this.loadAll = loadAll;
+    this.pageIndex = pageIndex;
+    this.pageSize = pageSize;
+    this.mapBean = mapBean;
+    this.orderBy = orderBy;
+  }
+
+  public static AccountRelatedRolesResponseData of(IApiQueryRequest queryRequest) {
+    AccountRelatedRolesResponseData configQueryResponse = new AccountRelatedRolesResponseData(
+        queryRequest.isLoadAll(), 
+        queryRequest.getPageIndex(), 
+        queryRequest.getPageSize(), 
+        queryRequest.getMapBean(), 
+        queryRequest.getOrderBy()
+    );
+    
+    return configQueryResponse;
+  }
+  
+  public AccountRelatedRolesResponseData build(Page<AccountRole> page) {
+    this.currentItemCount = page.getNumberOfElements();
+    this.pageCount = page.getTotalPages();
+    this.recordCount = page.getTotalElements();
+    this.items = page.getContent();
+
+    return this;
+  }
+
+  @Getter
+  private boolean loadAll;
+  @Getter
+  private int pageIndex;
+  @Getter
+  private int pageSize;
+  @Getter
+  private Map<String, Object> mapBean;
+  @Getter
+  private Map<String, String> orderBy;
+  
+  @Getter
+  @Setter
+  private int pageCount;
+  @Getter
+  @Setter
+  private long recordCount;
+  
+  @Getter
+  @Setter
+  private int currentItemCount;
+  
+  @Getter
+  @Setter
+  private List<AccountRole> items;
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/AccountRemoveResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/AccountRemoveResponseData.java
new file mode 100644
index 0000000..40d0b87
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/AccountRemoveResponseData.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.base.domain.entity.Account;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiRemoveResponseData;
+
+/**
+ * @author loie
+ */
+public class AccountRemoveResponseData implements IApiRemoveResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 8510464738198696332L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private AccountRemoveResponseData() {
+    
+  }
+  
+  public static AccountRemoveResponseData build(Account entity) {
+    AccountRemoveResponseData data = new AccountRemoveResponseData();
+    
+    return EntityUtils.copy(entity, data);
+  }
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/AccountUpdateResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/AccountUpdateResponseData.java
new file mode 100644
index 0000000..59cba51
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/AccountUpdateResponseData.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import com.supwisdom.institute.backend.base.domain.entity.Account;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiUpdateResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class AccountUpdateResponseData extends Account implements IApiUpdateResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -7885193620877829529L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private AccountUpdateResponseData() {
+
+  }
+
+  public static AccountUpdateResponseData build(Account entity) {
+    AccountUpdateResponseData data = new AccountUpdateResponseData();
+
+    return EntityUtils.copy(entity, data);
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ApplicationCreateResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ApplicationCreateResponseData.java
new file mode 100644
index 0000000..b4a7c94
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ApplicationCreateResponseData.java
@@ -0,0 +1,33 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import com.supwisdom.institute.backend.base.domain.entity.Application;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiCreateResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class ApplicationCreateResponseData extends Application implements IApiCreateResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -5986639173730939508L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private ApplicationCreateResponseData() {
+
+  }
+
+  public static ApplicationCreateResponseData build(Application entity) {
+    ApplicationCreateResponseData data = new ApplicationCreateResponseData();
+
+    return EntityUtils.copy(entity, data);
+  }
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ApplicationDeleteBatchResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ApplicationDeleteBatchResponseData.java
new file mode 100644
index 0000000..9e22692
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ApplicationDeleteBatchResponseData.java
@@ -0,0 +1,35 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+/**
+ * @author loie
+ */
+public class ApplicationDeleteBatchResponseData implements IApiResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -1441419457044931039L;
+
+  @Getter
+  @Setter
+  private List<String> ids;
+
+  private ApplicationDeleteBatchResponseData() {
+    
+  }
+  
+  public static ApplicationDeleteBatchResponseData build(List<String> ids) {
+    ApplicationDeleteBatchResponseData data = new ApplicationDeleteBatchResponseData();
+    data.setIds(ids);
+    
+    return data;
+  }
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ApplicationLoadResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ApplicationLoadResponseData.java
new file mode 100644
index 0000000..27acd2f
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ApplicationLoadResponseData.java
@@ -0,0 +1,33 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import com.supwisdom.institute.backend.base.domain.entity.Application;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiLoadResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class ApplicationLoadResponseData extends Application implements IApiLoadResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -4866881763278414027L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private ApplicationLoadResponseData() {
+
+  }
+
+  public static ApplicationLoadResponseData of(Application entity) {
+    ApplicationLoadResponseData data = new ApplicationLoadResponseData();
+    return EntityUtils.copy(entity, data);
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ApplicationQueryResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ApplicationQueryResponseData.java
new file mode 100644
index 0000000..c0fb9bd
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ApplicationQueryResponseData.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.data.domain.Page;
+
+import com.supwisdom.institute.backend.base.domain.entity.Application;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiQueryResponseData;
+
+/**
+ * @author loie
+ */
+public class ApplicationQueryResponseData implements IApiQueryResponseData<Application> {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 1355060716641084862L;
+
+  private ApplicationQueryResponseData(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean, Map<String, String> orderBy) {
+    this.loadAll = loadAll;
+    this.pageIndex = pageIndex;
+    this.pageSize = pageSize;
+    this.mapBean = mapBean;
+    this.orderBy = orderBy;
+  }
+
+  public static ApplicationQueryResponseData of(IApiQueryRequest queryRequest) {
+    ApplicationQueryResponseData configQueryResponse = new ApplicationQueryResponseData(
+        queryRequest.isLoadAll(), 
+        queryRequest.getPageIndex(), 
+        queryRequest.getPageSize(), 
+        queryRequest.getMapBean(), 
+        queryRequest.getOrderBy()
+    );
+    
+    return configQueryResponse;
+  }
+  
+  public ApplicationQueryResponseData build(Page<Application> page) {
+    this.currentItemCount = page.getNumberOfElements();
+    this.pageCount = page.getTotalPages();
+    this.recordCount = page.getTotalElements();
+    this.items = page.getContent();
+
+    return this;
+  }
+
+  @Getter
+  private boolean loadAll;
+  @Getter
+  private int pageIndex;
+  @Getter
+  private int pageSize;
+  @Getter
+  private Map<String, Object> mapBean;
+  @Getter
+  private Map<String, String> orderBy;
+  
+  @Getter
+  @Setter
+  private int pageCount;
+  @Getter
+  @Setter
+  private long recordCount;
+  
+  @Getter
+  @Setter
+  private int currentItemCount;
+  
+  @Getter
+  @Setter
+  private List<Application> items;
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ApplicationRemoveResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ApplicationRemoveResponseData.java
new file mode 100644
index 0000000..97f1a87
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ApplicationRemoveResponseData.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.base.domain.entity.Application;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiRemoveResponseData;
+
+/**
+ * @author loie
+ */
+public class ApplicationRemoveResponseData implements IApiRemoveResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -8179059520857121842L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private ApplicationRemoveResponseData() {
+    
+  }
+  
+  public static ApplicationRemoveResponseData build(Application entity) {
+    ApplicationRemoveResponseData data = new ApplicationRemoveResponseData();
+    
+    return EntityUtils.copy(entity, data);
+  }
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ApplicationUpdateResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ApplicationUpdateResponseData.java
new file mode 100644
index 0000000..1a72e5c
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ApplicationUpdateResponseData.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import com.supwisdom.institute.backend.base.domain.entity.Application;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiUpdateResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class ApplicationUpdateResponseData extends Application implements IApiUpdateResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -2690439553701722798L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private ApplicationUpdateResponseData() {
+
+  }
+
+  public static ApplicationUpdateResponseData build(Application entity) {
+    ApplicationUpdateResponseData data = new ApplicationUpdateResponseData();
+
+    return EntityUtils.copy(entity, data);
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ConfigCreateResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ConfigCreateResponseData.java
new file mode 100644
index 0000000..663f179
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ConfigCreateResponseData.java
@@ -0,0 +1,32 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import com.supwisdom.institute.backend.base.domain.entity.Config;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiCreateResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class ConfigCreateResponseData extends Config implements IApiCreateResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -2300091307366254182L;
+  
+  @Getter
+  @Setter
+  private String id;
+
+  private ConfigCreateResponseData() {
+    
+  }
+  
+  public static ConfigCreateResponseData build(Config entity) {
+    ConfigCreateResponseData configCreateResponse = new ConfigCreateResponseData();
+    return EntityUtils.copy(entity, configCreateResponse);
+  }
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ConfigLoadResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ConfigLoadResponseData.java
new file mode 100644
index 0000000..c8ddafa
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ConfigLoadResponseData.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import com.supwisdom.institute.backend.base.domain.entity.Config;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiLoadResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class ConfigLoadResponseData extends Config implements IApiLoadResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -579946566129915779L;
+
+  @Getter
+  @Setter
+  private String id;
+  
+  
+  private ConfigLoadResponseData() {
+    
+  }
+  
+  public static ConfigLoadResponseData build(Config entity) {
+    ConfigLoadResponseData configCreateResponse = new ConfigLoadResponseData();
+    return EntityUtils.copy(entity, configCreateResponse);
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ConfigQueryResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ConfigQueryResponseData.java
new file mode 100644
index 0000000..6255a4b
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ConfigQueryResponseData.java
@@ -0,0 +1,83 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.data.domain.Page;
+
+import com.supwisdom.institute.backend.base.api.v1.vo.admin.request.ConfigQueryRequest;
+import com.supwisdom.institute.backend.base.domain.entity.Config;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiQueryResponseData;
+
+/**
+ * @author loie
+ */
+public class ConfigQueryResponseData implements IApiQueryResponseData<Config> {
+  
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 3188467441502226095L;
+  
+//  private ConfigQueryResponseData() {
+//  }
+  
+  public ConfigQueryResponseData(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean, Map<String, String> orderBy) {
+    this.loadAll = loadAll;
+    this.pageIndex = pageIndex;
+    this.pageSize = pageSize;
+    this.mapBean = mapBean;
+    this.orderBy = orderBy;
+  }
+
+  public static ConfigQueryResponseData of(ConfigQueryRequest configQueryRequest) {
+    ConfigQueryResponseData configQueryResponse = new ConfigQueryResponseData(
+        configQueryRequest.isLoadAll(), 
+        configQueryRequest.getPageIndex(), 
+        configQueryRequest.getPageSize(), 
+        configQueryRequest.getMapBean(), 
+        configQueryRequest.getOrderBy()
+    );
+    
+    return configQueryResponse;
+  }
+  
+  public ConfigQueryResponseData build(Page<Config> page) {
+    this.currentItemCount = page.getNumberOfElements();
+    this.pageCount = page.getTotalPages();
+    this.recordCount = page.getTotalElements();
+    this.items = page.getContent();
+
+    return this;
+  }
+
+  @Getter
+  private boolean loadAll;
+  @Getter
+  private int pageIndex;
+  @Getter
+  private int pageSize;
+  @Getter
+  private Map<String, Object> mapBean;
+  @Getter
+  private Map<String, String> orderBy;
+  
+  @Getter
+  @Setter
+  private int pageCount;
+  @Getter
+  @Setter
+  private long recordCount;
+  
+  @Getter
+  @Setter
+  private int currentItemCount;
+  
+  @Getter
+  @Setter
+  private List<Config> items;
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ConfigRemoveResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ConfigRemoveResponseData.java
new file mode 100644
index 0000000..e656a1f
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ConfigRemoveResponseData.java
@@ -0,0 +1,33 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.base.domain.entity.Config;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiRemoveResponseData;
+
+/**
+ * @author loie
+ */
+public class ConfigRemoveResponseData implements IApiRemoveResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 8510464738198696332L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private ConfigRemoveResponseData() {
+    
+  }
+  
+  public static ConfigRemoveResponseData build(Config entity) {
+    ConfigRemoveResponseData configRemoveResponse = new ConfigRemoveResponseData();
+    return EntityUtils.copy(entity, configRemoveResponse);
+  }
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ConfigUpdateResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ConfigUpdateResponseData.java
new file mode 100644
index 0000000..df4de29
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ConfigUpdateResponseData.java
@@ -0,0 +1,33 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import com.supwisdom.institute.backend.base.domain.entity.Config;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiUpdateResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class ConfigUpdateResponseData extends Config implements IApiUpdateResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 2798387429543859170L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private ConfigUpdateResponseData() {
+    
+  }
+  
+  public static ConfigUpdateResponseData build(Config entity) {
+    ConfigUpdateResponseData configUpdateResponse = new ConfigUpdateResponseData();
+    return EntityUtils.copy(entity, configUpdateResponse);
+  }
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/GroupCreateResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/GroupCreateResponseData.java
new file mode 100644
index 0000000..4c3f545
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/GroupCreateResponseData.java
@@ -0,0 +1,33 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import com.supwisdom.institute.backend.base.domain.entity.Group;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiCreateResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class GroupCreateResponseData extends Group implements IApiCreateResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 3720317246428656184L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private GroupCreateResponseData() {
+
+  }
+
+  public static GroupCreateResponseData build(Group entity) {
+    GroupCreateResponseData data = new GroupCreateResponseData();
+
+    return EntityUtils.copy(entity, data);
+  }
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/GroupLoadResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/GroupLoadResponseData.java
new file mode 100644
index 0000000..a3b83b0
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/GroupLoadResponseData.java
@@ -0,0 +1,33 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import com.supwisdom.institute.backend.base.domain.entity.Group;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiLoadResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class GroupLoadResponseData extends Group implements IApiLoadResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 8064680472492241116L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private GroupLoadResponseData() {
+
+  }
+
+  public static GroupLoadResponseData of(Group entity) {
+    GroupLoadResponseData data = new GroupLoadResponseData();
+    return EntityUtils.copy(entity, data);
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/GroupQueryResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/GroupQueryResponseData.java
new file mode 100644
index 0000000..20e6fac
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/GroupQueryResponseData.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.data.domain.Page;
+
+import com.supwisdom.institute.backend.base.domain.entity.Group;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiQueryResponseData;
+
+/**
+ * @author loie
+ */
+public class GroupQueryResponseData implements IApiQueryResponseData<Group> {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -1512126460241874099L;
+
+  private GroupQueryResponseData(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean, Map<String, String> orderBy) {
+    this.loadAll = loadAll;
+    this.pageIndex = pageIndex;
+    this.pageSize = pageSize;
+    this.mapBean = mapBean;
+    this.orderBy = orderBy;
+  }
+
+  public static GroupQueryResponseData of(IApiQueryRequest queryRequest) {
+    GroupQueryResponseData configQueryResponse = new GroupQueryResponseData(
+        queryRequest.isLoadAll(), 
+        queryRequest.getPageIndex(), 
+        queryRequest.getPageSize(), 
+        queryRequest.getMapBean(), 
+        queryRequest.getOrderBy()
+    );
+    
+    return configQueryResponse;
+  }
+  
+  public GroupQueryResponseData build(Page<Group> page) {
+    this.currentItemCount = page.getNumberOfElements();
+    this.pageCount = page.getTotalPages();
+    this.recordCount = page.getTotalElements();
+    this.items = page.getContent();
+
+    return this;
+  }
+
+  @Getter
+  private boolean loadAll;
+  @Getter
+  private int pageIndex;
+  @Getter
+  private int pageSize;
+  @Getter
+  private Map<String, Object> mapBean;
+  @Getter
+  private Map<String, String> orderBy;
+  
+  @Getter
+  @Setter
+  private int pageCount;
+  @Getter
+  @Setter
+  private long recordCount;
+  
+  @Getter
+  @Setter
+  private int currentItemCount;
+  
+  @Getter
+  @Setter
+  private List<Group> items;
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/GroupRelateAccountsResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/GroupRelateAccountsResponseData.java
new file mode 100644
index 0000000..fbc6b08
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/GroupRelateAccountsResponseData.java
@@ -0,0 +1,29 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+public class GroupRelateAccountsResponseData implements IApiResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -6510043291221068404L;
+
+  @Getter
+  @Setter
+  private String message;
+
+  public GroupRelateAccountsResponseData(String message) {
+    this.message = message;
+  }
+
+  public static GroupRelateAccountsResponseData of(String message) {
+    GroupRelateAccountsResponseData data = new GroupRelateAccountsResponseData(message);
+    
+    return data;
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/GroupRelateRolesResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/GroupRelateRolesResponseData.java
new file mode 100644
index 0000000..104041c
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/GroupRelateRolesResponseData.java
@@ -0,0 +1,29 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+public class GroupRelateRolesResponseData implements IApiResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 1758005341902663320L;
+
+  @Getter
+  @Setter
+  private String message;
+
+  public GroupRelateRolesResponseData(String message) {
+    this.message = message;
+  }
+
+  public static GroupRelateRolesResponseData of(String message) {
+    GroupRelateRolesResponseData data = new GroupRelateRolesResponseData(message);
+    
+    return data;
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/GroupRelatedAccountsResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/GroupRelatedAccountsResponseData.java
new file mode 100644
index 0000000..1e9e819
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/GroupRelatedAccountsResponseData.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.data.domain.Page;
+
+import com.supwisdom.institute.backend.base.domain.entity.AccountGroup;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiQueryResponseData;
+
+/**
+ * @author loie
+ */
+public class GroupRelatedAccountsResponseData implements IApiQueryResponseData<AccountGroup> {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -6697630069320874813L;
+
+  private GroupRelatedAccountsResponseData(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean, Map<String, String> orderBy) {
+    this.loadAll = loadAll;
+    this.pageIndex = pageIndex;
+    this.pageSize = pageSize;
+    this.mapBean = mapBean;
+    this.orderBy = orderBy;
+  }
+
+  public static GroupRelatedAccountsResponseData of(IApiQueryRequest queryRequest) {
+    GroupRelatedAccountsResponseData configQueryResponse = new GroupRelatedAccountsResponseData(
+        queryRequest.isLoadAll(), 
+        queryRequest.getPageIndex(), 
+        queryRequest.getPageSize(), 
+        queryRequest.getMapBean(), 
+        queryRequest.getOrderBy()
+    );
+    
+    return configQueryResponse;
+  }
+  
+  public GroupRelatedAccountsResponseData build(Page<AccountGroup> page) {
+    this.currentItemCount = page.getNumberOfElements();
+    this.pageCount = page.getTotalPages();
+    this.recordCount = page.getTotalElements();
+    this.items = page.getContent();
+
+    return this;
+  }
+
+  @Getter
+  private boolean loadAll;
+  @Getter
+  private int pageIndex;
+  @Getter
+  private int pageSize;
+  @Getter
+  private Map<String, Object> mapBean;
+  @Getter
+  private Map<String, String> orderBy;
+  
+  @Getter
+  @Setter
+  private int pageCount;
+  @Getter
+  @Setter
+  private long recordCount;
+  
+  @Getter
+  @Setter
+  private int currentItemCount;
+  
+  @Getter
+  @Setter
+  private List<AccountGroup> items;
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/GroupRelatedRolesResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/GroupRelatedRolesResponseData.java
new file mode 100644
index 0000000..4ae2d5f
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/GroupRelatedRolesResponseData.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.data.domain.Page;
+
+import com.supwisdom.institute.backend.base.domain.entity.GroupRole;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiQueryResponseData;
+
+/**
+ * @author loie
+ */
+public class GroupRelatedRolesResponseData implements IApiQueryResponseData<GroupRole> {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 1574248919628509262L;
+
+  private GroupRelatedRolesResponseData(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean, Map<String, String> orderBy) {
+    this.loadAll = loadAll;
+    this.pageIndex = pageIndex;
+    this.pageSize = pageSize;
+    this.mapBean = mapBean;
+    this.orderBy = orderBy;
+  }
+
+  public static GroupRelatedRolesResponseData of(IApiQueryRequest queryRequest) {
+    GroupRelatedRolesResponseData configQueryResponse = new GroupRelatedRolesResponseData(
+        queryRequest.isLoadAll(), 
+        queryRequest.getPageIndex(), 
+        queryRequest.getPageSize(), 
+        queryRequest.getMapBean(), 
+        queryRequest.getOrderBy()
+    );
+    
+    return configQueryResponse;
+  }
+  
+  public GroupRelatedRolesResponseData build(Page<GroupRole> page) {
+    this.currentItemCount = page.getNumberOfElements();
+    this.pageCount = page.getTotalPages();
+    this.recordCount = page.getTotalElements();
+    this.items = page.getContent();
+
+    return this;
+  }
+
+  @Getter
+  private boolean loadAll;
+  @Getter
+  private int pageIndex;
+  @Getter
+  private int pageSize;
+  @Getter
+  private Map<String, Object> mapBean;
+  @Getter
+  private Map<String, String> orderBy;
+  
+  @Getter
+  @Setter
+  private int pageCount;
+  @Getter
+  @Setter
+  private long recordCount;
+  
+  @Getter
+  @Setter
+  private int currentItemCount;
+  
+  @Getter
+  @Setter
+  private List<GroupRole> items;
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/GroupRemoveResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/GroupRemoveResponseData.java
new file mode 100644
index 0000000..08e7440
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/GroupRemoveResponseData.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.base.domain.entity.Group;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiRemoveResponseData;
+
+/**
+ * @author loie
+ */
+public class GroupRemoveResponseData implements IApiRemoveResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -8704397808353314340L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private GroupRemoveResponseData() {
+    
+  }
+  
+  public static GroupRemoveResponseData build(Group entity) {
+    GroupRemoveResponseData data = new GroupRemoveResponseData();
+    
+    return EntityUtils.copy(entity, data);
+  }
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/GroupUpdateResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/GroupUpdateResponseData.java
new file mode 100644
index 0000000..3ae507d
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/GroupUpdateResponseData.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import com.supwisdom.institute.backend.base.domain.entity.Group;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiUpdateResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class GroupUpdateResponseData extends Group implements IApiUpdateResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -1248124641996970651L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private GroupUpdateResponseData() {
+
+  }
+
+  public static GroupUpdateResponseData build(Group entity) {
+    GroupUpdateResponseData data = new GroupUpdateResponseData();
+
+    return EntityUtils.copy(entity, data);
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/PermissionCreateResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/PermissionCreateResponseData.java
new file mode 100644
index 0000000..cec1042
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/PermissionCreateResponseData.java
@@ -0,0 +1,33 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import com.supwisdom.institute.backend.base.domain.entity.Permission;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiCreateResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class PermissionCreateResponseData extends Permission implements IApiCreateResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 4619013116994825066L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private PermissionCreateResponseData() {
+
+  }
+
+  public static PermissionCreateResponseData build(Permission entity) {
+    PermissionCreateResponseData data = new PermissionCreateResponseData();
+
+    return EntityUtils.copy(entity, data);
+  }
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/PermissionDeleteBatchResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/PermissionDeleteBatchResponseData.java
new file mode 100644
index 0000000..f1626bf
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/PermissionDeleteBatchResponseData.java
@@ -0,0 +1,35 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+/**
+ * @author loie
+ */
+public class PermissionDeleteBatchResponseData implements IApiResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -7406287471651267066L;
+
+  @Getter
+  @Setter
+  private List<String> ids;
+
+  private PermissionDeleteBatchResponseData() {
+    
+  }
+  
+  public static PermissionDeleteBatchResponseData build(List<String> ids) {
+    PermissionDeleteBatchResponseData data = new PermissionDeleteBatchResponseData();
+    data.setIds(ids);
+    
+    return data;
+  }
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/PermissionLoadResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/PermissionLoadResponseData.java
new file mode 100644
index 0000000..3815743
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/PermissionLoadResponseData.java
@@ -0,0 +1,44 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import java.util.List;
+
+import com.supwisdom.institute.backend.base.domain.entity.Permission;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiLoadResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class PermissionLoadResponseData extends Permission implements IApiLoadResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -2692036336252091453L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  @Getter
+  @Setter
+  private List<String> resourceIds;
+
+  
+  private PermissionLoadResponseData() {
+
+  }
+
+  public static PermissionLoadResponseData of(Permission entity, List<String> resourceIds) {
+    PermissionLoadResponseData data = new PermissionLoadResponseData();
+
+    EntityUtils.copy(entity, data);
+    data.setResourceIds(resourceIds);
+    
+    return data;
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/PermissionQueryResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/PermissionQueryResponseData.java
new file mode 100644
index 0000000..436a7ee
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/PermissionQueryResponseData.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.data.domain.Page;
+
+import com.supwisdom.institute.backend.base.domain.entity.Permission;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiQueryResponseData;
+
+/**
+ * @author loie
+ */
+public class PermissionQueryResponseData implements IApiQueryResponseData<Permission> {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -8586983968415937644L;
+
+  private PermissionQueryResponseData(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean, Map<String, String> orderBy) {
+    this.loadAll = loadAll;
+    this.pageIndex = pageIndex;
+    this.pageSize = pageSize;
+    this.mapBean = mapBean;
+    this.orderBy = orderBy;
+  }
+
+  public static PermissionQueryResponseData of(IApiQueryRequest queryRequest) {
+    PermissionQueryResponseData configQueryResponse = new PermissionQueryResponseData(
+        queryRequest.isLoadAll(), 
+        queryRequest.getPageIndex(), 
+        queryRequest.getPageSize(), 
+        queryRequest.getMapBean(), 
+        queryRequest.getOrderBy()
+    );
+    
+    return configQueryResponse;
+  }
+  
+  public PermissionQueryResponseData build(Page<Permission> page) {
+    this.currentItemCount = page.getNumberOfElements();
+    this.pageCount = page.getTotalPages();
+    this.recordCount = page.getTotalElements();
+    this.items = page.getContent();
+
+    return this;
+  }
+
+  @Getter
+  private boolean loadAll;
+  @Getter
+  private int pageIndex;
+  @Getter
+  private int pageSize;
+  @Getter
+  private Map<String, Object> mapBean;
+  @Getter
+  private Map<String, String> orderBy;
+  
+  @Getter
+  @Setter
+  private int pageCount;
+  @Getter
+  @Setter
+  private long recordCount;
+  
+  @Getter
+  @Setter
+  private int currentItemCount;
+  
+  @Getter
+  @Setter
+  private List<Permission> items;
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/PermissionRelateResourcesResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/PermissionRelateResourcesResponseData.java
new file mode 100644
index 0000000..e895aac
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/PermissionRelateResourcesResponseData.java
@@ -0,0 +1,29 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+public class PermissionRelateResourcesResponseData implements IApiResponseData {
+  
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 5576856617389758687L;
+
+  @Getter
+  @Setter
+  private String message;
+
+  public PermissionRelateResourcesResponseData(String message) {
+    this.message = message;
+  }
+
+  public static PermissionRelateResourcesResponseData of(String message) {
+    PermissionRelateResourcesResponseData data = new PermissionRelateResourcesResponseData(message);
+    
+    return data;
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/PermissionRelatedResourcesResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/PermissionRelatedResourcesResponseData.java
new file mode 100644
index 0000000..969d3c6
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/PermissionRelatedResourcesResponseData.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.data.domain.Page;
+
+import com.supwisdom.institute.backend.base.domain.entity.PermissionResource;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiQueryResponseData;
+
+/**
+ * @author loie
+ */
+public class PermissionRelatedResourcesResponseData implements IApiQueryResponseData<PermissionResource> {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -2095633445866557991L;
+
+  private PermissionRelatedResourcesResponseData(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean, Map<String, String> orderBy) {
+    this.loadAll = loadAll;
+    this.pageIndex = pageIndex;
+    this.pageSize = pageSize;
+    this.mapBean = mapBean;
+    this.orderBy = orderBy;
+  }
+
+  public static PermissionRelatedResourcesResponseData of(IApiQueryRequest queryRequest) {
+    PermissionRelatedResourcesResponseData configQueryResponse = new PermissionRelatedResourcesResponseData(
+        queryRequest.isLoadAll(), 
+        queryRequest.getPageIndex(), 
+        queryRequest.getPageSize(), 
+        queryRequest.getMapBean(), 
+        queryRequest.getOrderBy()
+    );
+    
+    return configQueryResponse;
+  }
+  
+  public PermissionRelatedResourcesResponseData build(Page<PermissionResource> page) {
+    this.currentItemCount = page.getNumberOfElements();
+    this.pageCount = page.getTotalPages();
+    this.recordCount = page.getTotalElements();
+    this.items = page.getContent();
+
+    return this;
+  }
+
+  @Getter
+  private boolean loadAll;
+  @Getter
+  private int pageIndex;
+  @Getter
+  private int pageSize;
+  @Getter
+  private Map<String, Object> mapBean;
+  @Getter
+  private Map<String, String> orderBy;
+  
+  @Getter
+  @Setter
+  private int pageCount;
+  @Getter
+  @Setter
+  private long recordCount;
+  
+  @Getter
+  @Setter
+  private int currentItemCount;
+  
+  @Getter
+  @Setter
+  private List<PermissionResource> items;
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/PermissionRemoveResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/PermissionRemoveResponseData.java
new file mode 100644
index 0000000..d54d0a1
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/PermissionRemoveResponseData.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.base.domain.entity.Permission;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiRemoveResponseData;
+
+/**
+ * @author loie
+ */
+public class PermissionRemoveResponseData implements IApiRemoveResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 216116075050505802L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private PermissionRemoveResponseData() {
+    
+  }
+  
+  public static PermissionRemoveResponseData build(Permission entity) {
+    PermissionRemoveResponseData data = new PermissionRemoveResponseData();
+    
+    return EntityUtils.copy(entity, data);
+  }
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/PermissionTreeResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/PermissionTreeResponseData.java
new file mode 100644
index 0000000..1735549
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/PermissionTreeResponseData.java
@@ -0,0 +1,31 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.base.domain.model.PermissionTreeNode;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+public class PermissionTreeResponseData implements IApiResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -6659447453323545811L;
+  
+  public PermissionTreeResponseData(PermissionTreeNode tree) {
+    this.tree = tree;
+  }
+  
+  @Getter
+  @Setter
+  private PermissionTreeNode tree;
+
+  public static PermissionTreeResponseData of(PermissionTreeNode tree) {
+    PermissionTreeResponseData data = new PermissionTreeResponseData(tree);
+    
+    return data;
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/PermissionUpdateResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/PermissionUpdateResponseData.java
new file mode 100644
index 0000000..806c2c6
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/PermissionUpdateResponseData.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import com.supwisdom.institute.backend.base.domain.entity.Permission;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiUpdateResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class PermissionUpdateResponseData extends Permission implements IApiUpdateResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -7627522286872083527L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private PermissionUpdateResponseData() {
+
+  }
+
+  public static PermissionUpdateResponseData build(Permission entity) {
+    PermissionUpdateResponseData data = new PermissionUpdateResponseData();
+
+    return EntityUtils.copy(entity, data);
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ResourceCreateResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ResourceCreateResponseData.java
new file mode 100644
index 0000000..1a07eb1
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ResourceCreateResponseData.java
@@ -0,0 +1,33 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import com.supwisdom.institute.backend.base.domain.entity.Resource;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiCreateResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class ResourceCreateResponseData extends Resource implements IApiCreateResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -1679712204106541555L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private ResourceCreateResponseData() {
+
+  }
+
+  public static ResourceCreateResponseData build(Resource entity) {
+    ResourceCreateResponseData data = new ResourceCreateResponseData();
+
+    return EntityUtils.copy(entity, data);
+  }
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ResourceLoadResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ResourceLoadResponseData.java
new file mode 100644
index 0000000..289e776
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ResourceLoadResponseData.java
@@ -0,0 +1,33 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import com.supwisdom.institute.backend.base.domain.entity.Resource;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiLoadResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class ResourceLoadResponseData extends Resource implements IApiLoadResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -8194462780086471511L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private ResourceLoadResponseData() {
+
+  }
+
+  public static ResourceLoadResponseData of(Resource entity) {
+    ResourceLoadResponseData data = new ResourceLoadResponseData();
+    return EntityUtils.copy(entity, data);
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ResourceQueryResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ResourceQueryResponseData.java
new file mode 100644
index 0000000..b591899
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ResourceQueryResponseData.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.data.domain.Page;
+
+import com.supwisdom.institute.backend.base.domain.entity.Resource;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiQueryResponseData;
+
+/**
+ * @author loie
+ */
+public class ResourceQueryResponseData implements IApiQueryResponseData<Resource> {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 241098548786558434L;
+
+  private ResourceQueryResponseData(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean, Map<String, String> orderBy) {
+    this.loadAll = loadAll;
+    this.pageIndex = pageIndex;
+    this.pageSize = pageSize;
+    this.mapBean = mapBean;
+    this.orderBy = orderBy;
+  }
+
+  public static ResourceQueryResponseData of(IApiQueryRequest queryRequest) {
+    ResourceQueryResponseData configQueryResponse = new ResourceQueryResponseData(
+        queryRequest.isLoadAll(), 
+        queryRequest.getPageIndex(), 
+        queryRequest.getPageSize(), 
+        queryRequest.getMapBean(), 
+        queryRequest.getOrderBy()
+    );
+    
+    return configQueryResponse;
+  }
+  
+  public ResourceQueryResponseData build(Page<Resource> page) {
+    this.currentItemCount = page.getNumberOfElements();
+    this.pageCount = page.getTotalPages();
+    this.recordCount = page.getTotalElements();
+    this.items = page.getContent();
+
+    return this;
+  }
+
+  @Getter
+  private boolean loadAll;
+  @Getter
+  private int pageIndex;
+  @Getter
+  private int pageSize;
+  @Getter
+  private Map<String, Object> mapBean;
+  @Getter
+  private Map<String, String> orderBy;
+  
+  @Getter
+  @Setter
+  private int pageCount;
+  @Getter
+  @Setter
+  private long recordCount;
+  
+  @Getter
+  @Setter
+  private int currentItemCount;
+  
+  @Getter
+  @Setter
+  private List<Resource> items;
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ResourceRemoveResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ResourceRemoveResponseData.java
new file mode 100644
index 0000000..ecc9988
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ResourceRemoveResponseData.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.base.domain.entity.Resource;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiRemoveResponseData;
+
+/**
+ * @author loie
+ */
+public class ResourceRemoveResponseData implements IApiRemoveResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 2847742767960670522L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private ResourceRemoveResponseData() {
+    
+  }
+  
+  public static ResourceRemoveResponseData build(Resource entity) {
+    ResourceRemoveResponseData data = new ResourceRemoveResponseData();
+    
+    return EntityUtils.copy(entity, data);
+  }
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ResourceUpdateResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ResourceUpdateResponseData.java
new file mode 100644
index 0000000..f6c4292
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/ResourceUpdateResponseData.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import com.supwisdom.institute.backend.base.domain.entity.Resource;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiUpdateResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class ResourceUpdateResponseData extends Resource implements IApiUpdateResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 893314081061207360L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private ResourceUpdateResponseData() {
+
+  }
+
+  public static ResourceUpdateResponseData build(Resource entity) {
+    ResourceUpdateResponseData data = new ResourceUpdateResponseData();
+
+    return EntityUtils.copy(entity, data);
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleCreateResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleCreateResponseData.java
new file mode 100644
index 0000000..bae356d
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleCreateResponseData.java
@@ -0,0 +1,33 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import com.supwisdom.institute.backend.base.domain.entity.Role;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiCreateResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class RoleCreateResponseData extends Role implements IApiCreateResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -6548339240060091209L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private RoleCreateResponseData() {
+
+  }
+
+  public static RoleCreateResponseData build(Role entity) {
+    RoleCreateResponseData data = new RoleCreateResponseData();
+
+    return EntityUtils.copy(entity, data);
+  }
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleDeleteBatchResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleDeleteBatchResponseData.java
new file mode 100644
index 0000000..61776d3
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleDeleteBatchResponseData.java
@@ -0,0 +1,35 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+/**
+ * @author loie
+ */
+public class RoleDeleteBatchResponseData implements IApiResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -7775951512990321469L;
+
+  @Getter
+  @Setter
+  private List<String> ids;
+
+  private RoleDeleteBatchResponseData() {
+    
+  }
+  
+  public static RoleDeleteBatchResponseData build(List<String> ids) {
+    RoleDeleteBatchResponseData data = new RoleDeleteBatchResponseData();
+    data.setIds(ids);
+    
+    return data;
+  }
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleLoadResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleLoadResponseData.java
new file mode 100644
index 0000000..bbbc439
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleLoadResponseData.java
@@ -0,0 +1,33 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import com.supwisdom.institute.backend.base.domain.entity.Role;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiLoadResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class RoleLoadResponseData extends Role implements IApiLoadResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 57708002489387653L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private RoleLoadResponseData() {
+
+  }
+
+  public static RoleLoadResponseData of(Role entity) {
+    RoleLoadResponseData data = new RoleLoadResponseData();
+    return EntityUtils.copy(entity, data);
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleQueryResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleQueryResponseData.java
new file mode 100644
index 0000000..24d6a09
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleQueryResponseData.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.data.domain.Page;
+
+import com.supwisdom.institute.backend.base.domain.entity.Role;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiQueryResponseData;
+
+/**
+ * @author loie
+ */
+public class RoleQueryResponseData implements IApiQueryResponseData<Role> {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 2542992897241822185L;
+
+  private RoleQueryResponseData(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean, Map<String, String> orderBy) {
+    this.loadAll = loadAll;
+    this.pageIndex = pageIndex;
+    this.pageSize = pageSize;
+    this.mapBean = mapBean;
+    this.orderBy = orderBy;
+  }
+
+  public static RoleQueryResponseData of(IApiQueryRequest queryRequest) {
+    RoleQueryResponseData configQueryResponse = new RoleQueryResponseData(
+        queryRequest.isLoadAll(), 
+        queryRequest.getPageIndex(), 
+        queryRequest.getPageSize(), 
+        queryRequest.getMapBean(), 
+        queryRequest.getOrderBy()
+    );
+    
+    return configQueryResponse;
+  }
+  
+  public RoleQueryResponseData build(Page<Role> page) {
+    this.currentItemCount = page.getNumberOfElements();
+    this.pageCount = page.getTotalPages();
+    this.recordCount = page.getTotalElements();
+    this.items = page.getContent();
+
+    return this;
+  }
+
+  @Getter
+  private boolean loadAll;
+  @Getter
+  private int pageIndex;
+  @Getter
+  private int pageSize;
+  @Getter
+  private Map<String, Object> mapBean;
+  @Getter
+  private Map<String, String> orderBy;
+  
+  @Getter
+  @Setter
+  private int pageCount;
+  @Getter
+  @Setter
+  private long recordCount;
+  
+  @Getter
+  @Setter
+  private int currentItemCount;
+  
+  @Getter
+  @Setter
+  private List<Role> items;
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleRelateAccountsResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleRelateAccountsResponseData.java
new file mode 100644
index 0000000..f137972
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleRelateAccountsResponseData.java
@@ -0,0 +1,29 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+public class RoleRelateAccountsResponseData implements IApiResponseData {
+  
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -1732402839300368775L;
+
+  @Getter
+  @Setter
+  private String message;
+
+  public RoleRelateAccountsResponseData(String message) {
+    this.message = message;
+  }
+
+  public static RoleRelateAccountsResponseData of(String message) {
+    RoleRelateAccountsResponseData data = new RoleRelateAccountsResponseData(message);
+    
+    return data;
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleRelateGroupsResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleRelateGroupsResponseData.java
new file mode 100644
index 0000000..5c752ae
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleRelateGroupsResponseData.java
@@ -0,0 +1,29 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+public class RoleRelateGroupsResponseData implements IApiResponseData {
+  
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -248658847598115689L;
+
+  @Getter
+  @Setter
+  private String message;
+
+  public RoleRelateGroupsResponseData(String message) {
+    this.message = message;
+  }
+
+  public static RoleRelateGroupsResponseData of(String message) {
+    RoleRelateGroupsResponseData data = new RoleRelateGroupsResponseData(message);
+    
+    return data;
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleRelatePermissionsResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleRelatePermissionsResponseData.java
new file mode 100644
index 0000000..da7c9c2
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleRelatePermissionsResponseData.java
@@ -0,0 +1,29 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+public class RoleRelatePermissionsResponseData implements IApiResponseData {
+  
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 5398594379968952750L;
+
+  @Getter
+  @Setter
+  private String message;
+
+  public RoleRelatePermissionsResponseData(String message) {
+    this.message = message;
+  }
+
+  public static RoleRelatePermissionsResponseData of(String message) {
+    RoleRelatePermissionsResponseData data = new RoleRelatePermissionsResponseData(message);
+    
+    return data;
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleRelatedAccountsResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleRelatedAccountsResponseData.java
new file mode 100644
index 0000000..a906d6e
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleRelatedAccountsResponseData.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.data.domain.Page;
+
+import com.supwisdom.institute.backend.base.domain.entity.AccountRole;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiQueryResponseData;
+
+/**
+ * @author loie
+ */
+public class RoleRelatedAccountsResponseData implements IApiQueryResponseData<AccountRole> {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 5272966183728979505L;
+
+  private RoleRelatedAccountsResponseData(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean, Map<String, String> orderBy) {
+    this.loadAll = loadAll;
+    this.pageIndex = pageIndex;
+    this.pageSize = pageSize;
+    this.mapBean = mapBean;
+    this.orderBy = orderBy;
+  }
+
+  public static RoleRelatedAccountsResponseData of(IApiQueryRequest queryRequest) {
+    RoleRelatedAccountsResponseData configQueryResponse = new RoleRelatedAccountsResponseData(
+        queryRequest.isLoadAll(), 
+        queryRequest.getPageIndex(), 
+        queryRequest.getPageSize(), 
+        queryRequest.getMapBean(), 
+        queryRequest.getOrderBy()
+    );
+    
+    return configQueryResponse;
+  }
+  
+  public RoleRelatedAccountsResponseData build(Page<AccountRole> page) {
+    this.currentItemCount = page.getNumberOfElements();
+    this.pageCount = page.getTotalPages();
+    this.recordCount = page.getTotalElements();
+    this.items = page.getContent();
+
+    return this;
+  }
+
+  @Getter
+  private boolean loadAll;
+  @Getter
+  private int pageIndex;
+  @Getter
+  private int pageSize;
+  @Getter
+  private Map<String, Object> mapBean;
+  @Getter
+  private Map<String, String> orderBy;
+  
+  @Getter
+  @Setter
+  private int pageCount;
+  @Getter
+  @Setter
+  private long recordCount;
+  
+  @Getter
+  @Setter
+  private int currentItemCount;
+  
+  @Getter
+  @Setter
+  private List<AccountRole> items;
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleRelatedGroupsResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleRelatedGroupsResponseData.java
new file mode 100644
index 0000000..cf5dc80
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleRelatedGroupsResponseData.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.data.domain.Page;
+
+import com.supwisdom.institute.backend.base.domain.entity.GroupRole;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiQueryResponseData;
+
+/**
+ * @author loie
+ */
+public class RoleRelatedGroupsResponseData implements IApiQueryResponseData<GroupRole> {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -1327331843770679868L;
+
+  private RoleRelatedGroupsResponseData(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean, Map<String, String> orderBy) {
+    this.loadAll = loadAll;
+    this.pageIndex = pageIndex;
+    this.pageSize = pageSize;
+    this.mapBean = mapBean;
+    this.orderBy = orderBy;
+  }
+
+  public static RoleRelatedGroupsResponseData of(IApiQueryRequest queryRequest) {
+    RoleRelatedGroupsResponseData configQueryResponse = new RoleRelatedGroupsResponseData(
+        queryRequest.isLoadAll(), 
+        queryRequest.getPageIndex(), 
+        queryRequest.getPageSize(), 
+        queryRequest.getMapBean(), 
+        queryRequest.getOrderBy()
+    );
+    
+    return configQueryResponse;
+  }
+  
+  public RoleRelatedGroupsResponseData build(Page<GroupRole> page) {
+    this.currentItemCount = page.getNumberOfElements();
+    this.pageCount = page.getTotalPages();
+    this.recordCount = page.getTotalElements();
+    this.items = page.getContent();
+
+    return this;
+  }
+
+  @Getter
+  private boolean loadAll;
+  @Getter
+  private int pageIndex;
+  @Getter
+  private int pageSize;
+  @Getter
+  private Map<String, Object> mapBean;
+  @Getter
+  private Map<String, String> orderBy;
+  
+  @Getter
+  @Setter
+  private int pageCount;
+  @Getter
+  @Setter
+  private long recordCount;
+  
+  @Getter
+  @Setter
+  private int currentItemCount;
+  
+  @Getter
+  @Setter
+  private List<GroupRole> items;
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleRelatedPermissionsResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleRelatedPermissionsResponseData.java
new file mode 100644
index 0000000..05ab0c5
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleRelatedPermissionsResponseData.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.data.domain.Page;
+
+import com.supwisdom.institute.backend.base.domain.entity.RolePermission;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiQueryResponseData;
+
+/**
+ * @author loie
+ */
+public class RoleRelatedPermissionsResponseData implements IApiQueryResponseData<RolePermission> {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -7556328257191962088L;
+
+  private RoleRelatedPermissionsResponseData(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean, Map<String, String> orderBy) {
+    this.loadAll = loadAll;
+    this.pageIndex = pageIndex;
+    this.pageSize = pageSize;
+    this.mapBean = mapBean;
+    this.orderBy = orderBy;
+  }
+
+  public static RoleRelatedPermissionsResponseData of(IApiQueryRequest queryRequest) {
+    RoleRelatedPermissionsResponseData configQueryResponse = new RoleRelatedPermissionsResponseData(
+        queryRequest.isLoadAll(), 
+        queryRequest.getPageIndex(), 
+        queryRequest.getPageSize(), 
+        queryRequest.getMapBean(), 
+        queryRequest.getOrderBy()
+    );
+    
+    return configQueryResponse;
+  }
+  
+  public RoleRelatedPermissionsResponseData build(Page<RolePermission> page) {
+    this.currentItemCount = page.getNumberOfElements();
+    this.pageCount = page.getTotalPages();
+    this.recordCount = page.getTotalElements();
+    this.items = page.getContent();
+
+    return this;
+  }
+
+  @Getter
+  private boolean loadAll;
+  @Getter
+  private int pageIndex;
+  @Getter
+  private int pageSize;
+  @Getter
+  private Map<String, Object> mapBean;
+  @Getter
+  private Map<String, String> orderBy;
+  
+  @Getter
+  @Setter
+  private int pageCount;
+  @Getter
+  @Setter
+  private long recordCount;
+  
+  @Getter
+  @Setter
+  private int currentItemCount;
+  
+  @Getter
+  @Setter
+  private List<RolePermission> items;
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleRemoveResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleRemoveResponseData.java
new file mode 100644
index 0000000..3f97566
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleRemoveResponseData.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.base.domain.entity.Role;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiRemoveResponseData;
+
+/**
+ * @author loie
+ */
+public class RoleRemoveResponseData implements IApiRemoveResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -2556956704425975795L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private RoleRemoveResponseData() {
+    
+  }
+  
+  public static RoleRemoveResponseData build(Role entity) {
+    RoleRemoveResponseData data = new RoleRemoveResponseData();
+    
+    return EntityUtils.copy(entity, data);
+  }
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleUpdateResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleUpdateResponseData.java
new file mode 100644
index 0000000..3f5e93f
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RoleUpdateResponseData.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import com.supwisdom.institute.backend.base.domain.entity.Role;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiUpdateResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class RoleUpdateResponseData extends Role implements IApiUpdateResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -7822437040348157286L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private RoleUpdateResponseData() {
+
+  }
+
+  public static RoleUpdateResponseData build(Role entity) {
+    RoleUpdateResponseData data = new RoleUpdateResponseData();
+
+    return EntityUtils.copy(entity, data);
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RouteCreateResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RouteCreateResponseData.java
new file mode 100644
index 0000000..ea50cd4
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RouteCreateResponseData.java
@@ -0,0 +1,33 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import com.supwisdom.institute.backend.base.domain.entity.Route;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiCreateResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class RouteCreateResponseData extends Route implements IApiCreateResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -3115757571075013907L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private RouteCreateResponseData() {
+
+  }
+
+  public static RouteCreateResponseData build(Route entity) {
+    RouteCreateResponseData data = new RouteCreateResponseData();
+
+    return EntityUtils.copy(entity, data);
+  }
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RouteDeleteBatchResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RouteDeleteBatchResponseData.java
new file mode 100644
index 0000000..da236b1
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RouteDeleteBatchResponseData.java
@@ -0,0 +1,35 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+/**
+ * @author loie
+ */
+public class RouteDeleteBatchResponseData implements IApiResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -2204591486919573195L;
+
+  @Getter
+  @Setter
+  private List<String> ids;
+
+  private RouteDeleteBatchResponseData() {
+    
+  }
+  
+  public static RouteDeleteBatchResponseData build(List<String> ids) {
+    RouteDeleteBatchResponseData data = new RouteDeleteBatchResponseData();
+    data.setIds(ids);
+    
+    return data;
+  }
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RouteLoadResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RouteLoadResponseData.java
new file mode 100644
index 0000000..5fd52d1
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RouteLoadResponseData.java
@@ -0,0 +1,33 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import com.supwisdom.institute.backend.base.domain.entity.Route;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiLoadResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class RouteLoadResponseData extends Route implements IApiLoadResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 3295217395256011396L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private RouteLoadResponseData() {
+
+  }
+
+  public static RouteLoadResponseData of(Route entity) {
+    RouteLoadResponseData data = new RouteLoadResponseData();
+    return EntityUtils.copy(entity, data);
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RouteQueryResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RouteQueryResponseData.java
new file mode 100644
index 0000000..99d3fb7
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RouteQueryResponseData.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.data.domain.Page;
+
+import com.supwisdom.institute.backend.base.domain.entity.Route;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiQueryResponseData;
+
+/**
+ * @author loie
+ */
+public class RouteQueryResponseData implements IApiQueryResponseData<Route> {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -2632433248197514787L;
+
+  private RouteQueryResponseData(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean, Map<String, String> orderBy) {
+    this.loadAll = loadAll;
+    this.pageIndex = pageIndex;
+    this.pageSize = pageSize;
+    this.mapBean = mapBean;
+    this.orderBy = orderBy;
+  }
+
+  public static RouteQueryResponseData of(IApiQueryRequest queryRequest) {
+    RouteQueryResponseData configQueryResponse = new RouteQueryResponseData(
+        queryRequest.isLoadAll(), 
+        queryRequest.getPageIndex(), 
+        queryRequest.getPageSize(), 
+        queryRequest.getMapBean(), 
+        queryRequest.getOrderBy()
+    );
+    
+    return configQueryResponse;
+  }
+  
+  public RouteQueryResponseData build(Page<Route> page) {
+    this.currentItemCount = page.getNumberOfElements();
+    this.pageCount = page.getTotalPages();
+    this.recordCount = page.getTotalElements();
+    this.items = page.getContent();
+
+    return this;
+  }
+
+  @Getter
+  private boolean loadAll;
+  @Getter
+  private int pageIndex;
+  @Getter
+  private int pageSize;
+  @Getter
+  private Map<String, Object> mapBean;
+  @Getter
+  private Map<String, String> orderBy;
+  
+  @Getter
+  @Setter
+  private int pageCount;
+  @Getter
+  @Setter
+  private long recordCount;
+  
+  @Getter
+  @Setter
+  private int currentItemCount;
+  
+  @Getter
+  @Setter
+  private List<Route> items;
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RouteRemoveResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RouteRemoveResponseData.java
new file mode 100644
index 0000000..4735d20
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RouteRemoveResponseData.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.base.domain.entity.Route;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiRemoveResponseData;
+
+/**
+ * @author loie
+ */
+public class RouteRemoveResponseData implements IApiRemoveResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -143516413115089421L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private RouteRemoveResponseData() {
+    
+  }
+  
+  public static RouteRemoveResponseData build(Route entity) {
+    RouteRemoveResponseData data = new RouteRemoveResponseData();
+    
+    return EntityUtils.copy(entity, data);
+  }
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RouteUpdateResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RouteUpdateResponseData.java
new file mode 100644
index 0000000..89ace30
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/admin/response/RouteUpdateResponseData.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.admin.response;
+
+import com.supwisdom.institute.backend.base.domain.entity.Route;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiUpdateResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class RouteUpdateResponseData extends Route implements IApiUpdateResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 364945014290674973L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private RouteUpdateResponseData() {
+
+  }
+
+  public static RouteUpdateResponseData build(Route entity) {
+    RouteUpdateResponseData data = new RouteUpdateResponseData();
+
+    return EntityUtils.copy(entity, data);
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/authn/response/AuthnAccountPermissionsResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/authn/response/AuthnAccountPermissionsResponseData.java
new file mode 100644
index 0000000..3d3aa4c
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/authn/response/AuthnAccountPermissionsResponseData.java
@@ -0,0 +1,31 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.authn.response;
+
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.base.domain.entity.Permission;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+public class AuthnAccountPermissionsResponseData implements IApiResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 8276878427539012799L;
+
+  @Getter
+  @Setter
+  public List<Permission> permissions;
+  
+  
+  public static AuthnAccountPermissionsResponseData of(List<Permission> permissions) {
+    AuthnAccountPermissionsResponseData data = new AuthnAccountPermissionsResponseData();
+    
+    data.setPermissions(permissions);
+    
+    return data;
+  }
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/authn/response/AuthnAccountResourcesResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/authn/response/AuthnAccountResourcesResponseData.java
new file mode 100644
index 0000000..617be41
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/authn/response/AuthnAccountResourcesResponseData.java
@@ -0,0 +1,31 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.authn.response;
+
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.base.domain.entity.Resource;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+public class AuthnAccountResourcesResponseData implements IApiResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 4924064418294890589L;
+
+  @Getter
+  @Setter
+  public List<Resource> resources;
+  
+  
+  public static AuthnAccountResourcesResponseData of(List<Resource> resources) {
+    AuthnAccountResourcesResponseData data = new AuthnAccountResourcesResponseData();
+    
+    data.setResources(resources);
+    
+    return data;
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/authn/response/AuthnAccountResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/authn/response/AuthnAccountResponseData.java
new file mode 100644
index 0000000..a84f199
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/authn/response/AuthnAccountResponseData.java
@@ -0,0 +1,19 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.authn.response;
+
+import com.supwisdom.institute.backend.base.domain.entity.Account;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+public class AuthnAccountResponseData extends Account implements IApiResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -289285783805128927L;
+
+  public static AuthnAccountResponseData of(Account entity) {
+    AuthnAccountResponseData data = new AuthnAccountResponseData();
+    return EntityUtils.copy(entity, data);
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/authn/response/AuthnAccountRolesResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/authn/response/AuthnAccountRolesResponseData.java
new file mode 100644
index 0000000..2241c15
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/authn/response/AuthnAccountRolesResponseData.java
@@ -0,0 +1,31 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.authn.response;
+
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.base.domain.entity.Role;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+public class AuthnAccountRolesResponseData implements IApiResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -6588284546370817275L;
+  
+  @Getter
+  @Setter
+  public List<Role> roles;
+  
+  
+  public static AuthnAccountRolesResponseData of(List<Role> roles) {
+    AuthnAccountRolesResponseData data = new AuthnAccountRolesResponseData();
+    
+    data.setRoles(roles);
+    
+    return data;
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/authn/response/AuthnApplicationsResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/authn/response/AuthnApplicationsResponseData.java
new file mode 100644
index 0000000..79dae74
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/authn/response/AuthnApplicationsResponseData.java
@@ -0,0 +1,31 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.authn.response;
+
+import java.util.List;
+
+import com.supwisdom.institute.backend.base.domain.entity.Application;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+public class AuthnApplicationsResponseData implements IApiResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 277008963651011910L;
+
+  @Getter
+  @Setter
+  public List<Application> applications;
+  
+  
+  public static AuthnApplicationsResponseData of(List<Application> applications) {
+    AuthnApplicationsResponseData data = new AuthnApplicationsResponseData();
+    
+    data.setApplications(applications);
+    
+    return data;
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/authn/response/AuthnPermissionRoleListResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/authn/response/AuthnPermissionRoleListResponseData.java
new file mode 100644
index 0000000..bca4b9d
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/authn/response/AuthnPermissionRoleListResponseData.java
@@ -0,0 +1,30 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.authn.response;
+
+import java.util.List;
+
+import com.supwisdom.institute.backend.base.domain.model.PermissionRoleSet;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+public class AuthnPermissionRoleListResponseData implements IApiResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -1484993874632504947L;
+
+  @Getter
+  @Setter
+  public List<PermissionRoleSet> permissionRoleSets;
+  
+  public static AuthnPermissionRoleListResponseData of(List<PermissionRoleSet> permissionRoleSets) {
+    AuthnPermissionRoleListResponseData data = new AuthnPermissionRoleListResponseData();
+    
+    data.setPermissionRoleSets(permissionRoleSets);
+    
+    return data;
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/authn/response/AuthnResourceRoleListResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/authn/response/AuthnResourceRoleListResponseData.java
new file mode 100644
index 0000000..da6df80
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/authn/response/AuthnResourceRoleListResponseData.java
@@ -0,0 +1,30 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.authn.response;
+
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.base.domain.model.ResourceRoleSet;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+public class AuthnResourceRoleListResponseData implements IApiResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -9178840503146742900L;
+
+  @Getter
+  @Setter
+  public List<ResourceRoleSet> resourceRoleSets;
+  
+  public static AuthnResourceRoleListResponseData of(List<ResourceRoleSet> resourceRoleSets) {
+    AuthnResourceRoleListResponseData data = new AuthnResourceRoleListResponseData();
+    
+    data.setResourceRoleSets(resourceRoleSets);
+    
+    return data;
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/authn/response/AuthnRolesResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/authn/response/AuthnRolesResponseData.java
new file mode 100644
index 0000000..4f503ed
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/authn/response/AuthnRolesResponseData.java
@@ -0,0 +1,31 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.authn.response;
+
+import java.util.List;
+
+import com.supwisdom.institute.backend.base.domain.entity.Role;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+public class AuthnRolesResponseData implements IApiResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -3695033930841370744L;
+
+  @Getter
+  @Setter
+  public List<Role> roles;
+  
+  
+  public static AuthnRolesResponseData of(List<Role> roles) {
+    AuthnRolesResponseData data = new AuthnRolesResponseData();
+    
+    data.setRoles(roles);
+    
+    return data;
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/authn/response/AuthnRoutesResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/authn/response/AuthnRoutesResponseData.java
new file mode 100644
index 0000000..3c46ae4
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/vo/authn/response/AuthnRoutesResponseData.java
@@ -0,0 +1,31 @@
+package com.supwisdom.institute.backend.base.api.v1.vo.authn.response;
+
+import java.util.List;
+
+import com.supwisdom.institute.backend.base.domain.entity.Route;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+public class AuthnRoutesResponseData implements IApiResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -7221340609572046915L;
+
+  @Getter
+  @Setter
+  public List<Route> routes;
+  
+  
+  public static AuthnRoutesResponseData of(List<Route> routes) {
+    AuthnRoutesResponseData data = new AuthnRoutesResponseData();
+    
+    data.setRoutes(routes);
+    
+    return data;
+  }
+
+}
diff --git a/base/domain/.gitignore b/base/domain/.gitignore
new file mode 100644
index 0000000..b83d222
--- /dev/null
+++ b/base/domain/.gitignore
@@ -0,0 +1 @@
+/target/
diff --git a/base/domain/pom.xml b/base/domain/pom.xml
new file mode 100644
index 0000000..dc3eda6
--- /dev/null
+++ b/base/domain/pom.xml
@@ -0,0 +1,99 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.supwisdom.institute</groupId>
+    <artifactId>sw-backend-parent</artifactId>
+    <version>0.0.1</version>
+    <relativePath>../../</relativePath>
+  </parent>
+
+  <groupId>com.supwisdom.institute</groupId>
+  <artifactId>sw-backend-base-domain</artifactId>
+  <version>0.0.1</version>
+  <packaging>jar</packaging>
+
+  <name>Supwisdom Backend Framework Base Domain</name>
+  <description>Supwisdom Backend Framework Base Domain project</description>
+
+  <dependencies>
+
+    <dependency>
+      <groupId>org.projectlombok</groupId>
+      <artifactId>lombok</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>com.supwisdom.institute</groupId>
+      <artifactId>sw-backend-common-core</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.supwisdom.institute</groupId>
+      <artifactId>sw-backend-common-utils</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.supwisdom.institute</groupId>
+      <artifactId>sw-backend-common-framework</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-data-jpa</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-release-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-failsafe-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/.gitkeep b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/.gitkeep
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/Account.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/Account.java
new file mode 100644
index 0000000..6cca9f9
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/Account.java
@@ -0,0 +1,164 @@
+package com.supwisdom.institute.backend.base.domain.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+import com.supwisdom.institute.backend.common.framework.entity.ABaseEntity;
+
+@Entity
+@Table(name = "TB_BASE_ACCOUNT")
+public class Account extends ABaseEntity {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 7955624268022038897L;
+  
+  public static final String STATUS_ENABLED = "1";
+  public static final String STATUS_DISABLED = "0";
+  
+
+  /**
+   * 用户名
+   */
+  @Column(name = "USERNAME", unique = true)
+  private String username;
+  
+  /**
+   * 密码
+   */
+  @Column(name = "PASSWORD")
+  private String password;
+
+  /**
+   * 是否可用,1 可用,0 不可用,默认:1
+   */
+  @Column(name = "ENABLED")
+  private Boolean enabled;
+  /**
+   * 账号未过期,1 未过期,0 过期,默认:1
+   */
+  @Column(name = "ACCOUNT_NON_EXPIRED")
+  private Boolean accountNonExpired;
+  /**
+   * 账号未锁定,1 未锁定,0 锁定,默认:1
+   */
+  @Column(name = "ACCOUNT_NON_LOCKED")
+  private Boolean accountNonLocked;
+  /**
+   * 密码未过期,1 未过期,0 过期,默认:1
+   */
+  @Column(name = "CREDENTIALS_NON_EXPIRED")
+  private Boolean credentialsNonExpired;
+
+  /**
+   * 姓名
+   */
+  @Column(name = "NAME")
+  private String name;
+
+  /**
+   * 备注
+   */
+  @Column(name = "MEMO")
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  @Column(name = "STATUS")
+  private String status;
+
+  /**
+   * 登录手机
+   */
+  @Column(name = "MOBILE", nullable = true)
+  private String mobile;
+  /**
+   * 登录邮箱
+   */
+  @Column(name = "EMAIL", nullable = true)
+  private String email;
+
+  public String getUsername() {
+    return username;
+  }
+
+  public void setUsername(String username) {
+    this.username = username;
+  }
+
+  public String getPassword() {
+    return password;
+  }
+
+  public void setPassword(String password) {
+    this.password = password;
+  }
+
+  public Boolean getEnabled() {
+    return enabled;
+  }
+
+  public void setEnabled(Boolean enabled) {
+    this.enabled = enabled;
+  }
+
+  public Boolean getAccountNonExpired() {
+    return accountNonExpired;
+  }
+
+  public void setAccountNonExpired(Boolean accountNonExpired) {
+    this.accountNonExpired = accountNonExpired;
+  }
+
+  public Boolean getAccountNonLocked() {
+    return accountNonLocked;
+  }
+
+  public void setAccountNonLocked(Boolean accountNonLocked) {
+    this.accountNonLocked = accountNonLocked;
+  }
+
+  public Boolean getCredentialsNonExpired() {
+    return credentialsNonExpired;
+  }
+
+  public void setCredentialsNonExpired(Boolean credentialsNonExpired) {
+    this.credentialsNonExpired = credentialsNonExpired;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public String getStatus() {
+    return status;
+  }
+
+  public void setStatus(String status) {
+    this.status = status;
+  }
+
+  public String getMobile() {
+    return mobile;
+  }
+
+  public void setMobile(String mobile) {
+    this.mobile = mobile;
+  }
+
+  public String getEmail() {
+    return email;
+  }
+
+  public void setEmail(String email) {
+    this.email = email;
+  }
+
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/AccountGroup.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/AccountGroup.java
new file mode 100644
index 0000000..f46767b
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/AccountGroup.java
@@ -0,0 +1,46 @@
+package com.supwisdom.institute.backend.base.domain.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+import com.supwisdom.institute.backend.common.framework.entity.ABaseEntity;
+
+@Entity
+@Table(name = "TB_BASE_ACCOUNT_GROUP")
+public class AccountGroup extends ABaseEntity {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -4239845385965871983L;
+
+  /**
+   * 帐号ID
+   */
+  @Column(name = "ACCOUNT_ID")
+  private String accountId;
+
+  /**
+   * 用户组ID
+   */
+  @Column(name = "GROUP_ID")
+  private String groupId;
+
+  public String getAccountId() {
+    return accountId;
+  }
+
+  public void setAccountId(String accountId) {
+    this.accountId = accountId;
+  }
+
+  public String getGroupId() {
+    return groupId;
+  }
+
+  public void setGroupId(String groupId) {
+    this.groupId = groupId;
+  }
+
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/AccountRole.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/AccountRole.java
new file mode 100644
index 0000000..b08506a
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/AccountRole.java
@@ -0,0 +1,46 @@
+package com.supwisdom.institute.backend.base.domain.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+import com.supwisdom.institute.backend.common.framework.entity.ABaseEntity;
+
+@Entity
+@Table(name = "TB_BASE_ACCOUNT_ROLE")
+public class AccountRole extends ABaseEntity {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -6158470486381850942L;
+
+  /**
+   * 帐号ID
+   */
+  @Column(name = "ACCOUNT_ID")
+  private String accountId;
+
+  /**
+   * 角色ID
+   */
+  @Column(name = "ROLE_ID")
+  private String roleId;
+
+  public String getAccountId() {
+    return accountId;
+  }
+
+  public void setAccountId(String accountId) {
+    this.accountId = accountId;
+  }
+
+  public String getRoleId() {
+    return roleId;
+  }
+
+  public void setRoleId(String roleId) {
+    this.roleId = roleId;
+  }
+
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/Application.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/Application.java
new file mode 100644
index 0000000..b68fd38
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/Application.java
@@ -0,0 +1,69 @@
+package com.supwisdom.institute.backend.base.domain.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.entity.ABaseEntity;
+
+@Entity
+@Table(name = "TB_BASE_APPLICATION")
+public class Application extends ABaseEntity {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -1874484930838884168L;
+
+  /**
+   * 代码
+   */
+  @Column(name = "CODE")
+  @Getter
+  @Setter
+  private String code;
+
+  /**
+   * 名称
+   */
+  @Column(name = "NAME")
+  @Getter
+  @Setter
+  private String name;
+
+  /**
+   * 备注
+   */
+  @Column(name = "MEMO")
+  @Getter
+  @Setter
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  @Column(name = "STATUS")
+  @Getter
+  @Setter
+  private String status;
+
+  /**
+   * 系统访问地址
+   */
+  @Column(name = "URL", nullable = true)
+  @Getter
+  @Setter
+  private String url;
+
+  /**
+   * SPI接口地址前缀
+   */
+  @Column(name = "SPI_URL_PREFIX", nullable = true)
+  @Getter
+  @Setter
+  private String spiUrlPrefix;
+
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/Config.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/Config.java
new file mode 100644
index 0000000..ee78348
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/Config.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.base.domain.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.entity.ABaseEntity;
+
+/**
+ * @author loie
+ */
+@Entity
+@Table(name = "TB_CONFIG")
+public class Config extends ABaseEntity {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -2721844710909809785L;
+
+  /**
+   * 分类代码
+   */
+  @Getter
+  @Setter
+  @Column(name = "CATEGORY_CODE")
+  private String categoryCode;
+  
+  /**
+   * 分类名称
+   */
+  @Getter
+  @Setter
+  @Column(name = "CATEGORY_NAME")
+  private String categoryName;
+
+  /**
+   * 名称
+   */
+  @Getter
+  @Setter
+  @Column(name = "NAME")
+  private String name;
+
+  /**
+   * 描述
+   */
+  @Getter
+  @Setter
+  @Column(name = "DESCRIPTION")
+  private String description;
+
+  /**
+   * 配置键
+   */
+  @Getter
+  @Setter
+  @Column(name = "CONFIG_KEY")
+  private String configKey;
+
+  /**
+   * 配置值
+   */
+  @Getter
+  @Setter
+  @Column(name = "CONFIG_VALUE")
+  private String configValue;
+
+  /**
+   * 是否可修改
+   */
+  @Getter
+  @Setter
+  @Column(name = "EDITABLE")
+  private Boolean editable;
+
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/Group.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/Group.java
new file mode 100644
index 0000000..5e1de9a
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/Group.java
@@ -0,0 +1,74 @@
+package com.supwisdom.institute.backend.base.domain.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+import com.supwisdom.institute.backend.common.framework.entity.ABaseEntity;
+
+@Entity(name = "Group_")
+@Table(name = "TB_BASE_GROUP")
+public class Group extends ABaseEntity {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 4260326816456622523L;
+
+  /**
+   * 代码
+   */
+  @Column(name = "CODE")
+  private String code;
+
+  /**
+   * 名称
+   */
+  @Column(name = "NAME")
+  private String name;
+
+  /**
+   * 备注
+   */
+  @Column(name = "MEMO")
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  @Column(name = "STATUS")
+  private String status;
+
+  public String getCode() {
+    return code;
+  }
+
+  public void setCode(String code) {
+    this.code = code;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public String getMemo() {
+    return memo;
+  }
+
+  public void setMemo(String memo) {
+    this.memo = memo;
+  }
+
+  public String getStatus() {
+    return status;
+  }
+
+  public void setStatus(String status) {
+    this.status = status;
+  }
+
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/GroupRole.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/GroupRole.java
new file mode 100644
index 0000000..dafbcb5
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/GroupRole.java
@@ -0,0 +1,46 @@
+package com.supwisdom.institute.backend.base.domain.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+import com.supwisdom.institute.backend.common.framework.entity.ABaseEntity;
+
+@Entity
+@Table(name = "TB_BASE_GROUP_ROLE")
+public class GroupRole extends ABaseEntity {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -3141266845902556712L;
+
+  /**
+   * 用户组ID
+   */
+  @Column(name = "GROUP_ID")
+  private String groupId;
+
+  /**
+   * 角色ID
+   */
+  @Column(name = "ROLE_ID")
+  private String roleId;
+
+  public String getGroupId() {
+    return groupId;
+  }
+
+  public void setGroupId(String groupId) {
+    this.groupId = groupId;
+  }
+
+  public String getRoleId() {
+    return roleId;
+  }
+
+  public void setRoleId(String roleId) {
+    this.roleId = roleId;
+  }
+
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/Permission.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/Permission.java
new file mode 100644
index 0000000..df33e6e
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/Permission.java
@@ -0,0 +1,210 @@
+package com.supwisdom.institute.backend.base.domain.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+import com.supwisdom.institute.backend.common.framework.entity.ABaseEntity;
+
+@Entity
+@Table(name = "TB_BASE_PERMISSION")
+public class Permission extends ABaseEntity {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -8834200833972243635L;
+  
+  public static final String ROOT_PARENT_ID = "0";
+  
+  public static final String APPLICATION_ID = "1";
+
+  public static final String TYPE_APPLICATION = "1";
+  public static final String TYPE_MENU = "2";
+  public static final String TYPE_OPERATION = "3";
+  
+  public static final Integer DEFAULT_ORDER = 0;
+
+  /**
+   * 代码
+   */
+  @Column(name = "CODE")
+  private String code;
+
+  /**
+   * 名称
+   */
+  @Column(name = "NAME")
+  private String name;
+
+  /**
+   * 备注
+   */
+  @Column(name = "MEMO", nullable = true)
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  @Column(name = "STATUS")
+  private String status;
+
+  /**
+   * 系统ID
+   */
+  @Column(name = "APPLICATION_ID", nullable = true)
+  private String applicationId;
+
+  /**
+   * 父级ID
+   */
+  @Column(name = "PARENT_ID")
+  private String parentId;
+
+  /**
+   * 类型(1 应用,2 菜单,3 操作)
+   */
+  @Column(name = "TYPE_")
+  private String type;
+
+  /**
+   * 菜单图标
+   */
+  @Column(name = "ICON", nullable = true)
+  private String icon;
+
+  /**
+   * URL地址
+   */
+  @Column(name = "URL", nullable = true)
+  private String url;
+
+  /**
+   * 排序
+   */
+  @Column(name = "ORDER_")
+  private Integer order;
+
+  /**
+   * 层次
+   */
+  @Column(name = "LEVEL_")
+  private Integer level;
+
+  /**
+   * 左序
+   */
+  @Column(name = "LFT")
+  private Integer lft;
+
+  /**
+   * 右序
+   */
+  @Column(name = "RGT")
+  private Integer rgt;
+
+  public String getCode() {
+    return code;
+  }
+
+  public void setCode(String code) {
+    this.code = code;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public String getMemo() {
+    return memo;
+  }
+
+  public void setMemo(String memo) {
+    this.memo = memo;
+  }
+
+  public String getStatus() {
+    return status;
+  }
+
+  public void setStatus(String status) {
+    this.status = status;
+  }
+
+  public String getType() {
+    return type;
+  }
+
+  public void setType(String type) {
+    this.type = type;
+  }
+
+  public String getIcon() {
+    return icon;
+  }
+
+  public void setIcon(String icon) {
+    this.icon = icon;
+  }
+
+  public String getUrl() {
+    return url;
+  }
+
+  public void setUrl(String url) {
+    this.url = url;
+  }
+
+  public String getApplicationId() {
+    return applicationId;
+  }
+
+  public void setApplicationId(String applicationId) {
+    this.applicationId = applicationId;
+  }
+
+  public String getParentId() {
+    return parentId;
+  }
+
+  public void setParentId(String parentId) {
+    this.parentId = parentId;
+  }
+
+  public Integer getOrder() {
+    return order;
+  }
+
+  public void setOrder(Integer order) {
+    this.order = order;
+  }
+
+  public Integer getLevel() {
+    return level;
+  }
+
+  public void setLevel(Integer level) {
+    this.level = level;
+  }
+
+  public Integer getLft() {
+    return lft;
+  }
+
+  public void setLft(Integer lft) {
+    this.lft = lft;
+  }
+
+  public Integer getRgt() {
+    return rgt;
+  }
+
+  public void setRgt(Integer rgt) {
+    this.rgt = rgt;
+  }
+
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/PermissionResource.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/PermissionResource.java
new file mode 100644
index 0000000..7ec0413
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/PermissionResource.java
@@ -0,0 +1,46 @@
+package com.supwisdom.institute.backend.base.domain.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+import com.supwisdom.institute.backend.common.framework.entity.ABaseEntity;
+
+@Entity
+@Table(name = "TB_BASE_PERMISSION_RESOURCE")
+public class PermissionResource extends ABaseEntity {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 4224037561100974979L;
+
+  /**
+   * 权限ID
+   */
+  @Column(name = "PERMISSION_ID")
+  private String permissionId;
+
+  /**
+   * 资源ID
+   */
+  @Column(name = "RESOURCE_ID")
+  private String resourceId;
+
+  public String getPermissionId() {
+    return permissionId;
+  }
+
+  public void setPermissionId(String permissionId) {
+    this.permissionId = permissionId;
+  }
+
+  public String getResourceId() {
+    return resourceId;
+  }
+
+  public void setResourceId(String resourceId) {
+    this.resourceId = resourceId;
+  }
+
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/Resource.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/Resource.java
new file mode 100644
index 0000000..8f8f8a4
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/Resource.java
@@ -0,0 +1,83 @@
+package com.supwisdom.institute.backend.base.domain.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.entity.ABaseEntity;
+
+@Entity
+@Table(name = "TB_BASE_RESOURCE")
+public class Resource extends ABaseEntity {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 4288268877209267453L;
+
+  public static final String ACCESS_ANONYMOUS = "anonymous"; // 匿名访问anonymous
+  public static final String ACCESS_AUTHENTICATE = "authenticate"; // 认证访问authenticate
+  public static final String ACCESS_AUTHORIZE = "authorize"; // 授权访问authorize
+  public static final String ACCESS_PERMIT_ALL = "permitAll"; // 允许所有permitAll
+  public static final String ACCESS_DENY_ALL = "denyAll"; // 拒绝所有denyAll
+
+  /**
+   * 代码
+   */
+  @Getter
+  @Setter
+  @Column(name = "CODE")
+  private String code;
+
+  /**
+   * 名称
+   */
+  @Getter
+  @Setter
+  @Column(name = "NAME")
+  private String name;
+
+  /**
+   * 备注
+   */
+  @Getter
+  @Setter
+  @Column(name = "MEMO", nullable = true)
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  @Getter
+  @Setter
+  @Column(name = "STATUS")
+  private String status;
+
+  /**
+   * 请求方式
+   */
+  @Getter
+  @Setter
+  @Column(name = "METHOD")
+  private String method;
+
+  /**
+   * 请求路径
+   */
+  @Getter
+  @Setter
+  @Column(name = "PATH")
+  private String path;
+
+  /**
+   * 访问规则(匿名访问anonymous、认证访问authenticate、授权访问authorize、允许所有permitAll、拒绝所有denyAll)
+   */
+  @Getter
+  @Setter
+  @Column(name = "ACCESS")
+  private String access;
+
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/Role.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/Role.java
new file mode 100644
index 0000000..c75cbe9
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/Role.java
@@ -0,0 +1,74 @@
+package com.supwisdom.institute.backend.base.domain.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+import com.supwisdom.institute.backend.common.framework.entity.ABaseEntity;
+
+@Entity
+@Table(name = "TB_BASE_ROLE")
+public class Role extends ABaseEntity {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 5470129732727732514L;
+
+  /**
+   * 代码
+   */
+  @Column(name = "CODE")
+  private String code;
+
+  /**
+   * 名称
+   */
+  @Column(name = "NAME")
+  private String name;
+
+  /**
+   * 备注
+   */
+  @Column(name = "MEMO")
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  @Column(name = "STATUS")
+  private String status;
+
+  public String getCode() {
+    return code;
+  }
+
+  public void setCode(String code) {
+    this.code = code;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public String getMemo() {
+    return memo;
+  }
+
+  public void setMemo(String memo) {
+    this.memo = memo;
+  }
+
+  public String getStatus() {
+    return status;
+  }
+
+  public void setStatus(String status) {
+    this.status = status;
+  }
+
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/RolePermission.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/RolePermission.java
new file mode 100644
index 0000000..19ba5b4
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/RolePermission.java
@@ -0,0 +1,46 @@
+package com.supwisdom.institute.backend.base.domain.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+import com.supwisdom.institute.backend.common.framework.entity.ABaseEntity;
+
+@Entity
+@Table(name = "TB_BASE_ROLE_PERMISSION")
+public class RolePermission extends ABaseEntity {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 5293251541687343949L;
+
+  /**
+   * 角色ID
+   */
+  @Column(name = "ROLE_ID")
+  private String roleId;
+
+  /**
+   * 权限ID
+   */
+  @Column(name = "PERMISSION_ID")
+  private String permissionId;
+
+  public String getRoleId() {
+    return roleId;
+  }
+
+  public void setRoleId(String roleId) {
+    this.roleId = roleId;
+  }
+
+  public String getPermissionId() {
+    return permissionId;
+  }
+
+  public void setPermissionId(String permissionId) {
+    this.permissionId = permissionId;
+  }
+
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/Route.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/Route.java
new file mode 100644
index 0000000..6a58e92
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/Route.java
@@ -0,0 +1,77 @@
+package com.supwisdom.institute.backend.base.domain.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.entity.ABaseEntity;
+
+@Entity
+@Table(name = "TB_BASE_ROUTE")
+public class Route extends ABaseEntity {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -7435404154376360786L;
+
+  /**
+   * 代码
+   */
+  @Column(name = "CODE")
+  @Getter
+  @Setter
+  private String code;
+
+  /**
+   * 名称
+   */
+  @Column(name = "NAME")
+  @Getter
+  @Setter
+  private String name;
+
+  /**
+   * 备注
+   */
+  @Column(name = "MEMO")
+  @Getter
+  @Setter
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  @Column(name = "STATUS")
+  @Getter
+  @Setter
+  private String status;
+
+  /**
+   * 路径前缀
+   */
+  @Column(name = "PATH_PREFIX")
+  @Getter
+  @Setter
+  private String pathPrefix;
+
+  /**
+   * 路由地址
+   */
+  @Column(name = "URL")
+  @Getter
+  @Setter
+  private String url;
+
+  /**
+   * 是否排除前缀
+   */
+  @Column(name = "STRIP_PREFIX")
+  @Getter
+  @Setter
+  private Boolean stripPrefix;
+
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/exception/.gitkeep b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/exception/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/exception/.gitkeep
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/exception/ConfigException.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/exception/ConfigException.java
new file mode 100644
index 0000000..77fee05
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/exception/ConfigException.java
@@ -0,0 +1,12 @@
+package com.supwisdom.institute.backend.base.domain.exception;
+
+import com.supwisdom.institute.backend.common.framework.exception.BaseException;
+
+public class ConfigException extends BaseException {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 8112079911386045865L;
+
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/model/.gitkeep b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/model/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/model/.gitkeep
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/model/PermissionRoleSet.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/model/PermissionRoleSet.java
new file mode 100644
index 0000000..a95a0f7
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/model/PermissionRoleSet.java
@@ -0,0 +1,31 @@
+package com.supwisdom.institute.backend.base.domain.model;
+
+import java.util.Collection;
+
+import com.supwisdom.institute.backend.base.domain.entity.Permission;
+import com.supwisdom.institute.backend.base.domain.entity.Role;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.model.IModel;
+
+import lombok.Getter;
+import lombok.Setter;
+
+public class PermissionRoleSet extends Permission implements IModel {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -1823157193945264156L;
+
+  @Getter
+  @Setter
+  Collection<Role> roles;
+  
+  public PermissionRoleSet(Permission permission, Collection<Role> roles) {
+    
+    EntityUtils.copy(permission, this);
+    
+    this.roles = roles;
+  }
+  
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/model/PermissionTreeNode.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/model/PermissionTreeNode.java
new file mode 100644
index 0000000..233de5f
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/model/PermissionTreeNode.java
@@ -0,0 +1,24 @@
+package com.supwisdom.institute.backend.base.domain.model;
+
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import com.supwisdom.institute.backend.base.domain.entity.Permission;
+import com.supwisdom.institute.backend.common.framework.model.IModel;
+
+@ToString
+public class PermissionTreeNode extends Permission implements IModel {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 6984458902464386215L;
+  
+  @Getter
+  @Setter
+  List<PermissionTreeNode> children;
+
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/model/ResourceRoleSet.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/model/ResourceRoleSet.java
new file mode 100644
index 0000000..598b30d
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/model/ResourceRoleSet.java
@@ -0,0 +1,31 @@
+package com.supwisdom.institute.backend.base.domain.model;
+
+import java.util.Collection;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.base.domain.entity.Resource;
+import com.supwisdom.institute.backend.base.domain.entity.Role;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.model.IModel;
+
+public class ResourceRoleSet extends Resource implements IModel {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 2220390504237330816L;
+
+  @Getter
+  @Setter
+  Collection<Role> roles;
+
+  public ResourceRoleSet(Resource resource, Collection<Role> roles) {
+    
+    EntityUtils.copy(resource, this);
+    
+    this.roles = roles;
+  }
+  
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/.gitkeep b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/.gitkeep
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/AccountGroupRepository.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/AccountGroupRepository.java
new file mode 100644
index 0000000..be61930
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/AccountGroupRepository.java
@@ -0,0 +1,191 @@
+package com.supwisdom.institute.backend.base.domain.repo;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleMatcher;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Repository;
+
+import com.supwisdom.institute.backend.base.domain.entity.Account;
+import com.supwisdom.institute.backend.base.domain.entity.AccountGroup;
+import com.supwisdom.institute.backend.base.domain.entity.Group;
+import com.supwisdom.institute.backend.common.framework.repo.BaseJpaRepository;
+import com.supwisdom.institute.backend.common.util.MapBeanUtils;
+
+@Repository
+public interface AccountGroupRepository extends BaseJpaRepository<AccountGroup> {
+
+  public default Page<AccountGroup> selectPageList(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+    AccountGroup probe = new AccountGroup();
+    if (mapBean != null) {
+      probe.setGroupId(MapBeanUtils.getString(mapBean, "groupId"));
+      probe.setAccountId(MapBeanUtils.getString(mapBean, "accountId"));
+    }
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("groupId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("accountId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<AccountGroup> example = Example.of(probe, matcher);
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+
+    Page<AccountGroup> page = this.findAll(example, pageRequest);
+
+    return page;
+  }
+
+  public default Page<AccountGroup> selectAccountGroups(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+
+    AccountGroup probe = new AccountGroup();
+    if (mapBean != null) {
+      probe.setGroupId(MapBeanUtils.getString(mapBean, "groupId"));
+      probe.setAccountId(MapBeanUtils.getString(mapBean, "accountId"));
+    }
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("groupId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("accountId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<AccountGroup> example = Example.of(probe, matcher);
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+
+    Page<AccountGroup> page = this.findAll(example, pageRequest); // FIXME: 多表关联查询
+
+    return page;
+  }
+
+  public default void relateAccountGroups(Account account, List<AccountGroup> accountGroups) {
+
+    List<AccountGroup> existAccountGroups = this.selectListByAccountId(account.getId());
+
+    Map<String, AccountGroup> existMapAccountGroups = new LinkedHashMap<String, AccountGroup>();
+    for (AccountGroup accountGroup : existAccountGroups) {
+      String k = String.format("%s", accountGroup.getGroupId());
+      existMapAccountGroups.put(k, accountGroup);
+    }
+
+    for (AccountGroup accountGroup : accountGroups) {
+      String k = String.format("%s", accountGroup.getGroupId());
+
+      if (existMapAccountGroups.containsKey(k)) {
+        existMapAccountGroups.remove(k);
+      } else {
+        accountGroup.setCompanyId(account.getCompanyId());
+        accountGroup.setAccountId(account.getId());
+
+        this.insert(accountGroup);
+      }
+    }
+
+    for (AccountGroup accountGroup : existMapAccountGroups.values()) {
+      this.deleteById(accountGroup.getId());
+    }
+  }
+
+  public default List<AccountGroup> selectListByAccountId(String accountId) {
+
+    AccountGroup probe = new AccountGroup();
+    probe.setAccountId(accountId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("accountId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<AccountGroup> example = Example.of(probe, matcher);
+
+    List<AccountGroup> accountGroups = this.findAll(example);
+
+    return accountGroups;
+  }
+
+  public default void relateGroupAccounts(Group group, List<AccountGroup> accountGroups) {
+
+    List<AccountGroup> existGroupAccounts = this.selectListByGroupId(group.getId());
+
+    Map<String, AccountGroup> existMapGroupAccounts = new LinkedHashMap<String, AccountGroup>();
+    for (AccountGroup accountGroup : existGroupAccounts) {
+      String k = String.format("%s", accountGroup.getAccountId());
+      existMapGroupAccounts.put(k, accountGroup);
+    }
+
+    for (AccountGroup accountGroup : accountGroups) {
+      String k = String.format("%s", accountGroup.getAccountId());
+
+      if (existMapGroupAccounts.containsKey(k)) {
+        existMapGroupAccounts.remove(k);
+      } else {
+        accountGroup.setCompanyId(group.getCompanyId());
+        accountGroup.setGroupId(group.getId());
+
+        this.insert(accountGroup);
+      }
+    }
+
+    for (AccountGroup accountGroup : existMapGroupAccounts.values()) {
+      this.deleteById(accountGroup.getId());
+    }
+  }
+
+  public default List<AccountGroup> selectListByGroupId(String groupId) {
+
+    AccountGroup probe = new AccountGroup();
+    probe.setGroupId(groupId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("groupId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<AccountGroup> example = Example.of(probe, matcher);
+
+    List<AccountGroup> accountGroups = this.findAll(example);
+
+    return accountGroups;
+  }
+  
+  public default AccountGroup selectOneByAccountGroup(String accountId, String groupId) {
+
+    AccountGroup probe = new AccountGroup();
+    probe.setAccountId(accountId);
+    probe.setGroupId(groupId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("accountId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("groupId", ExampleMatcher.GenericPropertyMatchers.exact())
+        ;
+
+    Example<AccountGroup> example = Example.of(probe, matcher);
+    
+    Optional<AccountGroup> o = this.findOne(example);
+    
+    return o.isPresent() ? o.get() : null;
+  }
+
+  public default void addAccountGroup(String accountId, String groupId) {
+    
+    AccountGroup accountGroup = this.selectOneByAccountGroup(accountId, groupId);
+    
+    if (accountGroup == null) {
+      accountGroup = new AccountGroup();
+      //accountGroup.setCompanyId(companyId);
+      accountGroup.setAccountId(accountId);
+      accountGroup.setGroupId(groupId);
+      
+      this.insert(accountGroup);
+    }
+  }
+
+  public default void removeAccountGroup(String accountId, String groupId) {
+    
+    AccountGroup accountGroup = this.selectOneByAccountGroup(accountId, groupId);
+    
+    if (accountGroup != null) {
+      this.deleteById(accountGroup.getId());
+    }
+  }
+
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/AccountRepository.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/AccountRepository.java
new file mode 100644
index 0000000..e4d9e07
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/AccountRepository.java
@@ -0,0 +1,199 @@
+package com.supwisdom.institute.backend.base.domain.repo;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleMatcher;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.stereotype.Repository;
+import org.springframework.util.StringUtils;
+
+import com.supwisdom.institute.backend.base.domain.entity.Account;
+import com.supwisdom.institute.backend.common.framework.repo.BaseJpaRepository;
+import com.supwisdom.institute.backend.common.util.MapBeanUtils;
+
+@Repository
+public interface AccountRepository extends BaseJpaRepository<Account> {
+  
+//  public default Page<Account> selectPageList(int pageIndex, int pageSize, Account probe) {
+//    
+//    ExampleMatcher matcher = ExampleMatcher.matching()
+//        .withMatcher("username", ExampleMatcher.GenericPropertyMatchers.contains())
+//        .withMatcher("name", ExampleMatcher.GenericPropertyMatchers.contains())
+//        .withMatcher("status", ExampleMatcher.GenericPropertyMatchers.exact());
+//    
+//    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+//    Example<Account> example = Example.of(probe, matcher);
+//    
+//    Page<Account> page = this.findAll(example, pageRequest);
+//    
+//    return page;
+//  }
+  
+  
+  @Override
+  public default Specification<Account> convertToSpec(Map<String, Object> mapBean) {
+    
+    Specification<Account> spec = new Specification<Account>() {
+
+      /**
+       * 
+       */
+      private static final long serialVersionUID = 9071470982419099273L;
+
+      @Override
+      public Predicate toPredicate(Root<Account> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
+        List<Predicate> predicates = new ArrayList<>();
+        
+        if (mapBean != null) {
+
+          if (MapBeanUtils.getBoolean(mapBean, "deleted") != null) {
+            predicates.add(criteriaBuilder.equal(root.get("deleted"), MapBeanUtils.getBoolean(mapBean, "deleted")));
+          }
+
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "username"))) {
+            predicates.add(criteriaBuilder.like(root.get("username"), "%" + MapBeanUtils.getString(mapBean, "username") + "%"));
+          }
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "name"))) {
+            predicates.add(criteriaBuilder.like(root.get("name"), "%" + MapBeanUtils.getString(mapBean, "name") + "%"));
+          }
+          
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "status"))) {
+            predicates.add(criteriaBuilder.equal(root.get("status"), MapBeanUtils.getString(mapBean, "status")));
+          }
+          
+//          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "grantTimeBegin"))) {
+//            String grantTimeBegin = MapBeanUtils.getString(mapBean, "grantTimeBegin");
+//            Date d = DateUtil.parseDate(grantTimeBegin+" 00:00:00", "yyyy-MM-dd HH:mm:ss");
+//            
+//            if (d != null) {
+//              predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("grantTime"), d));
+//            }
+//          }
+//
+//          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "grantTimeEnd"))) {
+//            String grantTimeEnd = MapBeanUtils.getString(mapBean, "grantTimeEnd");
+//            Date d = DateUtil.parseDate(grantTimeEnd+" 23:59:59", "yyyy-MM-dd HH:mm:ss");
+//            
+//            if (d != null) {
+//              predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("grantTime"), d));
+//            }
+//          }
+
+          List<Predicate> predicatesKeyword = new ArrayList<>();
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "keyword"))) {
+            predicatesKeyword.add(criteriaBuilder.like(root.get("username"), "%" + MapBeanUtils.getString(mapBean, "keyword") + "%"));
+            predicatesKeyword.add(criteriaBuilder.like(root.get("name"), "%" + MapBeanUtils.getString(mapBean, "keyword") + "%"));
+            predicatesKeyword.add(criteriaBuilder.like(root.get("memo"), "%" + MapBeanUtils.getString(mapBean, "keyword") + "%"));
+            
+            predicates.add(criteriaBuilder.or(predicatesKeyword.toArray(new Predicate[predicatesKeyword.size()])));
+          }
+        }
+        
+        return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
+      }
+      
+    };
+    
+    return spec;
+  }
+  
+  
+//  @Override
+//  public default Page<Account> selectPageList(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean, Map<String, String> orderBy) {
+//    if (loadAll) {
+//      pageIndex = 0;
+//      pageSize = Integer.MAX_VALUE;
+//    }
+//    
+//    Account probe = new Account();
+//    if (mapBean != null) {
+//      probe.setDeleted(MapBeanUtils.getBoolean(mapBean, "deleted"));
+//      probe.setUsername(MapBeanUtils.getString(mapBean, "username"));
+//      probe.setName(MapBeanUtils.getString(mapBean, "name"));
+//      probe.setStatus(MapBeanUtils.getString(mapBean, "status"));
+//    }
+//    
+//    ExampleMatcher matcher = ExampleMatcher.matching()
+//        .withMatcher("deleted", ExampleMatcher.GenericPropertyMatchers.exact())
+//        .withMatcher("username", ExampleMatcher.GenericPropertyMatchers.contains())
+//        .withMatcher("name", ExampleMatcher.GenericPropertyMatchers.contains())
+//        .withMatcher("status", ExampleMatcher.GenericPropertyMatchers.exact());
+//    
+//    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+//    Example<Account> example = Example.of(probe, matcher);
+//    
+//    Page<Account> page = this.findAll(example, pageRequest);
+//    
+//    return page;
+//  }
+  
+  /*
+  public default User selectById(String id) {
+    
+    try {
+      Optional<User> entity = this.findById(id);
+      
+      return entity.get();
+    } catch(RuntimeException e) {
+      System.out.println("RuntimeException:"+e.getMessage());
+    } catch(Exception e) {
+      System.out.println("Exception:"+e.getMessage());
+    }
+    
+    return null;
+  }
+  
+  public default User insert(User entity) {
+    
+    if (entity.getCompanyId() == null || entity.getCompanyId().isEmpty()) {
+      entity.setCompanyId("1");
+    }
+    
+    entity.setDeleted(false);
+    //entity.setAddAccount(AuthUtil.getRemoteUser());
+    entity.setAddTime(Calendar.getInstance().getTime());
+    
+    User e = this.save(entity);
+    
+    return e;
+  }
+  
+  public default User update(User entity) {
+    
+    //entity.setEditAccount(AuthUtil.getRemoteUser());
+    entity.setEditTime(Calendar.getInstance().getTime());
+    
+    User e = this.save(entity);
+    
+    return e;
+  }
+  */
+  
+  public default Account selectByUsername(String username) {
+    Account probe = new Account();
+    probe.setUsername(username);
+    
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("username", ExampleMatcher.GenericPropertyMatchers.exact());
+    
+    Example<Account> example = Example.of(probe, matcher);
+    
+    Optional<Account> u = this.findOne(example);
+    
+    if (u.isPresent()) {
+      return u.get();
+    }
+    
+    return null;
+  }
+
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/AccountRoleRepository.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/AccountRoleRepository.java
new file mode 100644
index 0000000..503f3b5
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/AccountRoleRepository.java
@@ -0,0 +1,191 @@
+package com.supwisdom.institute.backend.base.domain.repo;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleMatcher;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Repository;
+
+import com.supwisdom.institute.backend.base.domain.entity.Account;
+import com.supwisdom.institute.backend.base.domain.entity.AccountRole;
+import com.supwisdom.institute.backend.base.domain.entity.Role;
+import com.supwisdom.institute.backend.common.framework.repo.BaseJpaRepository;
+import com.supwisdom.institute.backend.common.util.MapBeanUtils;
+
+@Repository
+public interface AccountRoleRepository extends BaseJpaRepository<AccountRole> {
+
+  public default Page<AccountRole> selectPageList(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+
+    AccountRole probe = new AccountRole();
+    if (mapBean != null) {
+      probe.setAccountId(MapBeanUtils.getString(mapBean, "accountId"));
+      probe.setRoleId(MapBeanUtils.getString(mapBean, "roleId"));
+    }
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("accountId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("roleId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+    Example<AccountRole> example = Example.of(probe, matcher);
+
+    Page<AccountRole> page = this.findAll(example, pageRequest);
+
+    return page;
+  }
+
+  public default Page<AccountRole> selectAccountRoles(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+
+    AccountRole probe = new AccountRole();
+    if (mapBean != null) {
+      probe.setAccountId(MapBeanUtils.getString(mapBean, "accountId"));
+      probe.setRoleId(MapBeanUtils.getString(mapBean, "roleId"));
+    }
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("accountId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("roleId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<AccountRole> example = Example.of(probe, matcher);
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+
+    Page<AccountRole> page = this.findAll(example, pageRequest); // FIXME: 多表关联查询
+
+    return page;
+  }
+
+  public default void relateAccountRoles(Account account, List<AccountRole> accountRoles) {
+
+    List<AccountRole> existAccountRoles = this.selectListByAccountId(account.getId());
+
+    Map<String, AccountRole> existMapAccountRoles = new LinkedHashMap<String, AccountRole>();
+    for (AccountRole accountRole : existAccountRoles) {
+      String k = String.format("%s", accountRole.getRoleId());
+      existMapAccountRoles.put(k, accountRole);
+    }
+
+    for (AccountRole accountRole : accountRoles) {
+      String k = String.format("%s", accountRole.getRoleId());
+
+      if (existMapAccountRoles.containsKey(k)) {
+        existMapAccountRoles.remove(k);
+      } else {
+        accountRole.setCompanyId(account.getCompanyId());
+        accountRole.setAccountId(account.getId());
+
+        this.insert(accountRole);
+      }
+    }
+
+    for (AccountRole accountRole : existMapAccountRoles.values()) {
+      this.deleteById(accountRole.getId());
+    }
+  }
+
+  public default List<AccountRole> selectListByAccountId(String accountId) {
+
+    AccountRole probe = new AccountRole();
+    probe.setAccountId(accountId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("accountId",
+        ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<AccountRole> example = Example.of(probe, matcher);
+
+    List<AccountRole> accountRoles = this.findAll(example);
+
+    return accountRoles;
+  }
+
+  public default void relateRoleAccounts(Role role, List<AccountRole> accountRoles) {
+
+    List<AccountRole> existRoleAccounts = this.selectListByRoleId(role.getId());
+
+    Map<String, AccountRole> existMapRoleAccounts = new LinkedHashMap<String, AccountRole>();
+    for (AccountRole accountRole : existRoleAccounts) {
+      String k = String.format("%s", accountRole.getAccountId());
+      existMapRoleAccounts.put(k, accountRole);
+    }
+
+    for (AccountRole accountRole : accountRoles) {
+      String k = String.format("%s", accountRole.getAccountId());
+
+      if (existMapRoleAccounts.containsKey(k)) {
+        existMapRoleAccounts.remove(k);
+      } else {
+        accountRole.setCompanyId(role.getCompanyId());
+        accountRole.setRoleId(role.getId());
+
+        this.insert(accountRole);
+      }
+    }
+
+    for (AccountRole accountRole : existMapRoleAccounts.values()) {
+      this.deleteById(accountRole.getId());
+    }
+  }
+
+  public default List<AccountRole> selectListByRoleId(String roleId) {
+
+    AccountRole probe = new AccountRole();
+    probe.setRoleId(roleId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("roleId",
+        ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<AccountRole> example = Example.of(probe, matcher);
+
+    List<AccountRole> accountRoles = this.findAll(example);
+
+    return accountRoles;
+  }
+  
+  public default AccountRole selectOneByAccountRole(String accountId, String roleId) {
+
+    AccountRole probe = new AccountRole();
+    probe.setAccountId(accountId);
+    probe.setRoleId(roleId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("accountId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("roleId", ExampleMatcher.GenericPropertyMatchers.exact())
+        ;
+
+    Example<AccountRole> example = Example.of(probe, matcher);
+    
+    Optional<AccountRole> o = this.findOne(example);
+    
+    return o.isPresent() ? o.get() : null;
+  }
+
+  public default void addAccountRole(String accountId, String roleId) {
+
+    AccountRole accountRole = this.selectOneByAccountRole(accountId, roleId);
+    
+    if (accountRole == null) {
+      accountRole = new AccountRole();
+      //accountRole.setCompanyId(companyId);
+      accountRole.setAccountId(accountId);
+      accountRole.setRoleId(roleId);
+      
+      this.insert(accountRole);
+    }
+  }
+
+  public default void removeAccountRole(String accountId, String roleId) {
+
+    AccountRole accountRole = this.selectOneByAccountRole(accountId, roleId);
+    
+    if (accountRole != null) {
+      this.deleteById(accountRole.getId());
+    }
+  }
+
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/ApplicationRepository.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/ApplicationRepository.java
new file mode 100644
index 0000000..3e1b6ea
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/ApplicationRepository.java
@@ -0,0 +1,76 @@
+package com.supwisdom.institute.backend.base.domain.repo;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.stereotype.Repository;
+import org.springframework.util.StringUtils;
+
+import com.supwisdom.institute.backend.base.domain.entity.Application;
+import com.supwisdom.institute.backend.common.framework.repo.BaseJpaRepository;
+import com.supwisdom.institute.backend.common.util.MapBeanUtils;
+
+@Repository
+public interface ApplicationRepository extends BaseJpaRepository<Application> {
+  
+  @Override
+  public default Specification<Application> convertToSpec(Map<String, Object> mapBean) {
+    
+    Specification<Application> spec = new Specification<Application>() {
+
+      /**
+       * 
+       */
+      private static final long serialVersionUID = -263282246904382286L;
+
+      @Override
+      public Predicate toPredicate(Root<Application> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
+        List<Predicate> predicates = new ArrayList<>();
+        
+        if (mapBean != null) {
+
+          if (MapBeanUtils.getBoolean(mapBean, "deleted") != null) {
+            predicates.add(criteriaBuilder.equal(root.get("deleted"), MapBeanUtils.getBoolean(mapBean, "deleted")));
+          }
+
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "code"))) {
+            predicates.add(criteriaBuilder.like(root.get("code"), "%" + MapBeanUtils.getString(mapBean, "code") + "%"));
+          }
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "name"))) {
+            predicates.add(criteriaBuilder.like(root.get("name"), "%" + MapBeanUtils.getString(mapBean, "name") + "%"));
+          }
+          
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "status"))) {
+            predicates.add(criteriaBuilder.equal(root.get("status"), MapBeanUtils.getString(mapBean, "status")));
+          }
+
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "url"))) {
+            predicates.add(criteriaBuilder.like(root.get("url"), "%" + MapBeanUtils.getString(mapBean, "url") + "%"));
+          }
+          
+          List<Predicate> predicatesKeyword = new ArrayList<>();
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "keyword"))) {
+            predicatesKeyword.add(criteriaBuilder.like(root.get("code"), "%" + MapBeanUtils.getString(mapBean, "keyword") + "%"));
+            predicatesKeyword.add(criteriaBuilder.like(root.get("name"), "%" + MapBeanUtils.getString(mapBean, "keyword") + "%"));
+            predicatesKeyword.add(criteriaBuilder.like(root.get("memo"), "%" + MapBeanUtils.getString(mapBean, "keyword") + "%"));
+            
+            predicates.add(criteriaBuilder.or(predicatesKeyword.toArray(new Predicate[predicatesKeyword.size()])));
+          }
+        }
+        
+        return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
+      }
+      
+    };
+    
+    return spec;
+  }
+
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/ConfigRepository.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/ConfigRepository.java
new file mode 100644
index 0000000..052115e
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/ConfigRepository.java
@@ -0,0 +1,77 @@
+package com.supwisdom.institute.backend.base.domain.repo;
+
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleMatcher;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Repository;
+
+import com.supwisdom.institute.backend.base.domain.entity.Config;
+import com.supwisdom.institute.backend.common.framework.repo.BaseJpaRepository;
+import com.supwisdom.institute.backend.common.util.MapBeanUtils;
+
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * @author loie
+ */
+@Repository
+public interface ConfigRepository extends BaseJpaRepository<Config> {
+  
+  public default Page<Config> selectPageList(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean, Map<String, String> orderBy) {
+    Config probe = new Config();
+    if (mapBean != null) {
+      probe.setDeleted(MapBeanUtils.getBoolean(mapBean, "deleted"));
+      probe.setCategoryCode(MapBeanUtils.getString(mapBean, "categoryCode"));
+      probe.setCategoryName(MapBeanUtils.getString(mapBean, "categoryName"));
+      probe.setName(MapBeanUtils.getString(mapBean, "name"));
+      probe.setDescription(MapBeanUtils.getString(mapBean, "description"));
+      probe.setConfigKey(MapBeanUtils.getString(mapBean, "configKey"));
+      probe.setEditable(MapBeanUtils.getBoolean(mapBean, "editable"));
+    }
+    
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("deleted", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("categoryCode", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("categoryName", ExampleMatcher.GenericPropertyMatchers.contains())
+        .withMatcher("name", ExampleMatcher.GenericPropertyMatchers.contains())
+        .withMatcher("description", ExampleMatcher.GenericPropertyMatchers.contains())
+        .withMatcher("configKey", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("editable", ExampleMatcher.GenericPropertyMatchers.exact())
+    ;
+    
+    if (loadAll) {
+      pageIndex = 0;
+      pageSize = Integer.MAX_VALUE;
+    }
+    
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+    Example<Config> example = Example.of(probe, matcher);
+    
+    Page<Config> page = this.findAll(example, pageRequest);
+    
+    return page;
+  }
+
+  public default Config selectByCategoryKey(String categoryCode, String configKey) {
+    Config probe = new Config();
+    
+    probe.setDeleted(false);
+    probe.setCategoryCode(categoryCode);
+    probe.setConfigKey(configKey);
+    
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("deleted", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("categoryCode", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("configKey", ExampleMatcher.GenericPropertyMatchers.exact())
+    ;
+    
+    Example<Config> example = Example.of(probe, matcher);
+    
+    Optional<Config> config = this.findOne(example);
+    
+    return config.isPresent() ? config.get() : null;
+  }
+
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/GroupRepository.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/GroupRepository.java
new file mode 100644
index 0000000..75ad2b4
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/GroupRepository.java
@@ -0,0 +1,107 @@
+package com.supwisdom.institute.backend.base.domain.repo;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.stereotype.Repository;
+import org.springframework.util.StringUtils;
+
+import com.supwisdom.institute.backend.base.domain.entity.Group;
+import com.supwisdom.institute.backend.common.framework.repo.BaseJpaRepository;
+import com.supwisdom.institute.backend.common.util.MapBeanUtils;
+
+@Repository
+public interface GroupRepository extends BaseJpaRepository<Group> {
+
+
+  @Override
+  public default Specification<Group> convertToSpec(Map<String, Object> mapBean) {
+    
+    Specification<Group> spec = new Specification<Group>() {
+
+      /**
+       * 
+       */
+      private static final long serialVersionUID = 5467747850605022253L;
+
+      @Override
+      public Predicate toPredicate(Root<Group> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
+        List<Predicate> predicates = new ArrayList<>();
+        
+        if (mapBean != null) {
+
+          if (MapBeanUtils.getBoolean(mapBean, "deleted") != null) {
+            predicates.add(criteriaBuilder.equal(root.get("deleted"), MapBeanUtils.getBoolean(mapBean, "deleted")));
+          }
+
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "code"))) {
+            predicates.add(criteriaBuilder.like(root.get("code"), "%" + MapBeanUtils.getString(mapBean, "code") + "%"));
+          }
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "name"))) {
+            predicates.add(criteriaBuilder.like(root.get("name"), "%" + MapBeanUtils.getString(mapBean, "name") + "%"));
+          }
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "memo"))) {
+            predicates.add(criteriaBuilder.like(root.get("memo"), "%" + MapBeanUtils.getString(mapBean, "memo") + "%"));
+          }
+          
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "status"))) {
+            predicates.add(criteriaBuilder.equal(root.get("status"), MapBeanUtils.getString(mapBean, "status")));
+          }
+
+          List<Predicate> predicatesKeyword = new ArrayList<>();
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "keyword"))) {
+            predicatesKeyword.add(criteriaBuilder.like(root.get("code"), "%" + MapBeanUtils.getString(mapBean, "keyword") + "%"));
+            predicatesKeyword.add(criteriaBuilder.like(root.get("name"), "%" + MapBeanUtils.getString(mapBean, "keyword") + "%"));
+            predicatesKeyword.add(criteriaBuilder.like(root.get("memo"), "%" + MapBeanUtils.getString(mapBean, "keyword") + "%"));
+            
+            predicates.add(criteriaBuilder.or(predicatesKeyword.toArray(new Predicate[predicatesKeyword.size()])));
+          }
+        }
+        
+        return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
+      }
+      
+    };
+    
+    return spec;
+  }
+
+//  @Override
+//  public default Page<Group> selectPageList(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean, Map<String, String> orderBy) {
+//    if (loadAll) {
+//      pageIndex = 0;
+//      pageSize = Integer.MAX_VALUE;
+//    }
+//    
+//    Group probe = new Group();
+//    if (mapBean != null) {
+//      probe.setDeleted(MapBeanUtils.getBoolean(mapBean, "deleted"));
+//      probe.setCode(MapBeanUtils.getString(mapBean, "code"));
+//      probe.setName(MapBeanUtils.getString(mapBean, "name"));
+//      probe.setMemo(MapBeanUtils.getString(mapBean, "memo"));
+//      probe.setStatus(MapBeanUtils.getString(mapBean, "status"));
+//    }
+//    
+//    ExampleMatcher matcher = ExampleMatcher.matching()
+//        .withMatcher("deleted", ExampleMatcher.GenericPropertyMatchers.exact())
+//        .withMatcher("code", ExampleMatcher.GenericPropertyMatchers.contains())
+//        .withMatcher("name", ExampleMatcher.GenericPropertyMatchers.contains())
+//        .withMatcher("memo", ExampleMatcher.GenericPropertyMatchers.contains())
+//        .withMatcher("status", ExampleMatcher.GenericPropertyMatchers.exact());
+//    
+//    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+//    Example<Group> example = Example.of(probe, matcher);
+//    
+//    Page<Group> page = this.findAll(example, pageRequest);
+//    
+//    return page;
+//  }
+  
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/GroupRoleRepository.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/GroupRoleRepository.java
new file mode 100644
index 0000000..35bd4d5
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/GroupRoleRepository.java
@@ -0,0 +1,191 @@
+package com.supwisdom.institute.backend.base.domain.repo;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleMatcher;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Repository;
+
+import com.supwisdom.institute.backend.base.domain.entity.Group;
+import com.supwisdom.institute.backend.base.domain.entity.GroupRole;
+import com.supwisdom.institute.backend.base.domain.entity.Role;
+import com.supwisdom.institute.backend.common.framework.repo.BaseJpaRepository;
+import com.supwisdom.institute.backend.common.util.MapBeanUtils;
+
+@Repository
+public interface GroupRoleRepository extends BaseJpaRepository<GroupRole> {
+
+  public default Page<GroupRole> selectPageList(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+    GroupRole probe = new GroupRole();
+    if (mapBean != null) {
+      probe.setGroupId(MapBeanUtils.getString(mapBean, "groupId"));
+      probe.setRoleId(MapBeanUtils.getString(mapBean, "roleId"));
+    }
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("groupId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("roleId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+    Example<GroupRole> example = Example.of(probe, matcher);
+
+    Page<GroupRole> page = this.findAll(example, pageRequest);
+
+    return page;
+  }
+
+  public default Page<GroupRole> selectGroupRoles(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+
+    GroupRole probe = new GroupRole();
+    if (mapBean != null) {
+      probe.setGroupId(MapBeanUtils.getString(mapBean, "groupId"));
+      probe.setRoleId(MapBeanUtils.getString(mapBean, "roleId"));
+    }
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("groupId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("roleId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<GroupRole> example = Example.of(probe, matcher);
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+
+    Page<GroupRole> page = this.findAll(example, pageRequest);  // FIXME: 多表关联查询
+
+    return page;
+  }
+
+  public default void relateGroupRoles(Group group, List<GroupRole> groupRoles) {
+
+    List<GroupRole> existGroupRoles = this.selectListByGroupId(group.getId());
+
+    Map<String, GroupRole> existMapGroupRoles = new LinkedHashMap<String, GroupRole>();
+    for (GroupRole groupRole : existGroupRoles) {
+      String k = String.format("%s", groupRole.getRoleId());
+      existMapGroupRoles.put(k, groupRole);
+    }
+
+    for (GroupRole groupRole : groupRoles) {
+      String k = String.format("%s", groupRole.getRoleId());
+
+      if (existMapGroupRoles.containsKey(k)) {
+        existMapGroupRoles.remove(k);
+      } else {
+        groupRole.setCompanyId(group.getCompanyId());
+        groupRole.setGroupId(group.getId());
+
+        this.insert(groupRole);
+      }
+    }
+
+    for (GroupRole groupRole : existMapGroupRoles.values()) {
+      this.deleteById(groupRole.getId());
+    }
+  }
+
+  public default List<GroupRole> selectListByGroupId(String groupId) {
+
+    GroupRole probe = new GroupRole();
+    probe.setGroupId(groupId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("groupId",
+        ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<GroupRole> example = Example.of(probe, matcher);
+
+    List<GroupRole> groupRoles = this.findAll(example);
+
+    return groupRoles;
+  }
+
+
+  public default void relateRoleGroups(Role role, List<GroupRole> groupRoles) {
+
+    List<GroupRole> existRoleGroups = this.selectListByRoleId(role.getCode());
+
+    Map<String, GroupRole> existMapRoleGroups = new LinkedHashMap<String, GroupRole>();
+    for (GroupRole groupRole : existRoleGroups) {
+      String k = String.format("%s", groupRole.getGroupId());
+      existMapRoleGroups.put(k, groupRole);
+    }
+
+    for (GroupRole groupRole : groupRoles) {
+      String k = String.format("%s", groupRole.getGroupId());
+
+      if (existMapRoleGroups.containsKey(k)) {
+        existMapRoleGroups.remove(k);
+      } else {
+        groupRole.setCompanyId(role.getCompanyId());
+        groupRole.setRoleId(role.getId());
+
+        this.insert(groupRole);
+      }
+    }
+
+    for (GroupRole groupRole : existMapRoleGroups.values()) {
+      this.deleteById(groupRole.getId());
+    }
+  }
+
+  public default List<GroupRole> selectListByRoleId(String roleId) {
+
+    GroupRole probe = new GroupRole();
+    probe.setRoleId(roleId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("roleId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<GroupRole> example = Example.of(probe, matcher);
+
+    List<GroupRole> groupRoles = this.findAll(example);
+
+    return groupRoles;
+  }
+
+  public default GroupRole selectOneByGroupRole(String groupId, String roleId) {
+
+    GroupRole probe = new GroupRole();
+    probe.setGroupId(groupId);
+    probe.setRoleId(roleId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("groupId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("roleId", ExampleMatcher.GenericPropertyMatchers.exact())
+        ;
+
+    Example<GroupRole> example = Example.of(probe, matcher);
+    
+    Optional<GroupRole> o = this.findOne(example);
+    
+    return o.isPresent() ? o.get() : null;
+  }
+
+  public default void addGroupRole(String groupId, String roleId) {
+
+    GroupRole groupRole = this.selectOneByGroupRole(groupId, roleId);
+    
+    if (groupRole == null) {
+      groupRole = new GroupRole();
+      //groupRole.setCompanyId(companyId);
+      groupRole.setGroupId(groupId);
+      groupRole.setRoleId(roleId);
+      
+      this.insert(groupRole);
+    }
+  }
+
+  public default void removeGroupRole(String groupId, String roleId) {
+
+    GroupRole groupRole = this.selectOneByGroupRole(groupId, roleId);
+    
+    if (groupRole != null) {
+      this.deleteById(groupRole.getId());
+    }
+  }
+
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/PermissionRepository.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/PermissionRepository.java
new file mode 100644
index 0000000..aaa0214
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/PermissionRepository.java
@@ -0,0 +1,419 @@
+package com.supwisdom.institute.backend.base.domain.repo;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+import javax.transaction.Transactional;
+
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleMatcher;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+import org.springframework.util.StringUtils;
+
+import com.supwisdom.institute.backend.base.domain.entity.Permission;
+import com.supwisdom.institute.backend.common.framework.repo.BaseJpaRepository;
+import com.supwisdom.institute.backend.common.util.MapBeanUtils;
+
+@Repository
+@Transactional
+public interface PermissionRepository extends BaseJpaRepository<Permission> {
+  
+
+  @Override
+  public default Specification<Permission> convertToSpec(Map<String, Object> mapBean) {
+    
+    Specification<Permission> spec = new Specification<Permission>() {
+
+      /**
+       * 
+       */
+      private static final long serialVersionUID = 6195601104797641573L;
+
+      @Override
+      public Predicate toPredicate(Root<Permission> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
+        List<Predicate> predicates = new ArrayList<>();
+        
+        if (mapBean != null) {
+
+          if (MapBeanUtils.getBoolean(mapBean, "deleted") != null) {
+            predicates.add(criteriaBuilder.equal(root.get("deleted"), MapBeanUtils.getBoolean(mapBean, "deleted")));
+          }
+          
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "code"))) {
+            predicates.add(criteriaBuilder.like(root.get("code"), "%" + MapBeanUtils.getString(mapBean, "code") + "%"));
+          }
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "name"))) {
+            predicates.add(criteriaBuilder.like(root.get("name"), "%" + MapBeanUtils.getString(mapBean, "name") + "%"));
+          }
+          
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "status"))) {
+            predicates.add(criteriaBuilder.equal(root.get("status"), MapBeanUtils.getString(mapBean, "status")));
+          }
+          
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "type"))) {
+            predicates.add(criteriaBuilder.equal(root.get("type"), MapBeanUtils.getString(mapBean, "type")));
+          }
+          
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "url"))) {
+            predicates.add(criteriaBuilder.like(root.get("url"), "%" + MapBeanUtils.getString(mapBean, "url") + "%"));
+          }
+
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "applicationId"))) {
+            predicates.add(criteriaBuilder.equal(root.get("applicationId"), MapBeanUtils.getString(mapBean, "applicationId")));
+          }
+
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "parentId"))) {
+            predicates.add(criteriaBuilder.equal(root.get("parentId"), MapBeanUtils.getString(mapBean, "parentId")));
+          }
+
+//          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "grantTimeBegin"))) {
+//            String grantTimeBegin = MapBeanUtils.getString(mapBean, "grantTimeBegin");
+//            Date d = DateUtil.parseDate(grantTimeBegin+" 00:00:00", "yyyy-MM-dd HH:mm:ss");
+//            
+//            if (d != null) {
+//              predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("grantTime"), d));
+//            }
+//          }
+//
+//          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "grantTimeEnd"))) {
+//            String grantTimeEnd = MapBeanUtils.getString(mapBean, "grantTimeEnd");
+//            Date d = DateUtil.parseDate(grantTimeEnd+" 23:59:59", "yyyy-MM-dd HH:mm:ss");
+//            
+//            if (d != null) {
+//              predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("grantTime"), d));
+//            }
+//          }
+
+          List<Predicate> predicatesKeyword = new ArrayList<>();
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "keyword"))) {
+            predicatesKeyword.add(criteriaBuilder.like(root.get("code"), "%" + MapBeanUtils.getString(mapBean, "keyword") + "%"));
+            predicatesKeyword.add(criteriaBuilder.like(root.get("name"), "%" + MapBeanUtils.getString(mapBean, "keyword") + "%"));
+            predicatesKeyword.add(criteriaBuilder.like(root.get("memo"), "%" + MapBeanUtils.getString(mapBean, "keyword") + "%"));
+            
+            predicates.add(criteriaBuilder.or(predicatesKeyword.toArray(new Predicate[predicatesKeyword.size()])));
+          }
+        }
+        
+        return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
+      }
+      
+    };
+    
+    return spec;
+  }
+  
+  @Override
+  public default Page<Permission> selectPageList(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean, Map<String, String> orderBy) {
+    if (loadAll) {
+      pageIndex = 0;
+      pageSize = Integer.MAX_VALUE;
+    }
+    
+    Permission probe = new Permission();
+    if (mapBean != null) {
+      probe.setDeleted(MapBeanUtils.getBoolean(mapBean, "deleted"));
+      probe.setCode(MapBeanUtils.getString(mapBean, "code"));
+      probe.setName(MapBeanUtils.getString(mapBean, "name"));
+      probe.setMemo(MapBeanUtils.getString(mapBean, "memo"));
+      probe.setStatus(MapBeanUtils.getString(mapBean, "status"));
+      probe.setType(MapBeanUtils.getString(mapBean, "type"));
+    }
+    
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("deleted", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("code", ExampleMatcher.GenericPropertyMatchers.contains())
+        .withMatcher("name", ExampleMatcher.GenericPropertyMatchers.contains())
+        .withMatcher("memo", ExampleMatcher.GenericPropertyMatchers.contains())
+        .withMatcher("status", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("type", ExampleMatcher.GenericPropertyMatchers.exact());
+    
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+    Example<Permission> example = Example.of(probe, matcher);
+    
+    Page<Permission> page = this.findAll(example, pageRequest);
+    
+    return page;
+  }
+  
+  
+  
+  @Query(value = "select max(p.rgt) from Permission p")
+  public int selectMaxRgt();
+  
+  @Query(value = "select p from Permission p where p.lft>:lft and p.rgt<:rgt order by p.lft")
+  public List<Permission> selectBetweenLftRgt(@Param("lft") int lft, @Param("rgt") int rgt);
+  
+  @Modifying
+  @Query(value = "update TB_BASE_PERMISSION "
+      + "set "
+      + "  LFT = (case when LFT >= :rgt then LFT + :offset else LFT end), "
+      + "  RGT = RGT + :offset "
+      + "where RGT >= :rgt", nativeQuery = true)
+  public int updateLftRgtWhenInsert(@Param("rgt") int rgt, @Param("offset") int offset);
+
+  @Modifying
+  @Query(value = "update TB_BASE_PERMISSION "
+      + "set "
+      + "  LFT = (case when LFT >= :rgt then LFT - :offset else LFT end), "
+      + "  RGT = RGT - :offset "
+      + "where RGT >= :rgt", nativeQuery = true)
+  public int updateLftRgtWhenDelete(@Param("rgt") int rgt, @Param("offset") int offset);
+  
+  
+  @Override
+  public default Permission insert(Permission entity) {
+
+    if (entity.getParentId() == null) {
+      entity.setParentId(Permission.ROOT_PARENT_ID);
+    }
+
+    if (entity.getParentId() == null || entity.getParentId().isEmpty() || Permission.ROOT_PARENT_ID.equals(entity.getParentId())) {
+      int maxRgt = selectMaxRgt();
+      entity.setLft((maxRgt+1));
+      entity.setRgt((maxRgt+1) + 1);
+      
+      entity.setLevel(1);
+    } else {
+      Permission parentEntity = this.selectById(entity.getParentId());
+      if (parentEntity == null) {
+        throw new RuntimeException(String.format("父级对象不存在!"));
+      } else {
+        // 将 lft或rgt 大于等于父级对象 rgt 的记录的 lft、rgt +offset
+        int rgt = parentEntity.getRgt();
+        int offset = 2;
+        updateLftRgtWhenInsert(rgt, offset);
+        
+        entity.setLft(rgt);
+        entity.setRgt(rgt + 1);
+        
+        entity.setLevel(parentEntity.getLevel() + 1);
+      }
+    }
+    
+    return BaseJpaRepository.super.insert(entity);
+  }
+  
+  @Override
+  public default Permission update(Permission entity) {
+
+    Permission originEntity = this.selectById(entity.getId());
+    if (originEntity == null) {
+      return null;
+    }
+    
+    //if (!this.checkFieldExists("code", entity.getCode(), entity.getId())) {
+    //  throw new RuntimeException(String.format("代码重复!"));
+    //}
+    
+    if (originEntity.getParentId() == null) {
+      originEntity.setParentId(Permission.ROOT_PARENT_ID);
+    }
+    
+    if (entity.getParentId() == null) {
+      entity.setParentId(Permission.ROOT_PARENT_ID);
+    }
+    
+    if (!originEntity.getParentId().equals(entity.getParentId()) ) {
+
+      int lft = originEntity.getLft();
+      int rgt = originEntity.getRgt();
+      int level = originEntity.getLevel();
+      int offset = rgt - lft +1;
+      
+      List<Permission> childEntities = this.selectBetweenLftRgt(lft, rgt);
+
+      if (entity.getParentId() == null || entity.getParentId().isEmpty() || Permission.ROOT_PARENT_ID.equals(entity.getParentId())) {
+        // 将 lft或rgt 大于等于该对象 rgt 的记录的 lft、rgt -offset
+        updateLftRgtWhenDelete(rgt, offset);
+  
+        int maxRgt = selectMaxRgt();
+        entity.setLft((maxRgt+1));
+        entity.setRgt((maxRgt+1) + 1 +offset-2);
+        
+        entity.setLevel(1);
+      } else {
+        // 将 lft或rgt 大于等于该对象 rgt 的记录的 lft、rgt -offset
+        updateLftRgtWhenDelete(rgt, offset);
+
+        Permission parentEntity = this.selectById(entity.getParentId());
+        if (parentEntity == null) {
+          throw new RuntimeException(String.format("父级对象不存在!"));
+        }
+        //System.out.println(String.format("pLft %s, pRgt %s", parentEntity.getLft(), parentEntity.getRgt()));
+        if (parentEntity.getLft() >= originEntity.getLft() && parentEntity.getRgt() <= originEntity.getRgt()) {
+          throw new RuntimeException(String.format("不能设置自身或自身的子节点作为父级!"));
+        }
+        
+        //parentEntity = this.selectById(entity.getParentId()); System.out.println(String.format("pLft %s, pRgt %s", parentEntity.getLft(), parentEntity.getRgt()));
+        // 将 lft或rgt 大于等于父级对象 rgt 的记录的 lft、rgt +offset
+        //int pLft = parentEntity.getLft();
+        int pRgt = parentEntity.getRgt();
+        updateLftRgtWhenInsert(pRgt, offset);
+        
+        entity.setLft(pRgt);
+        entity.setRgt(pRgt + 1 + offset-2);
+        
+        entity.setLevel(parentEntity.getLevel() + 1);
+      }
+      
+      int newLft = entity.getLft();
+      int newRgt = entity.getRgt();
+      int newLevel = entity.getLevel();
+      //System.out.println(String.format("newLft %s, newRgt %s, newLevel %s", newLft, newRgt, newLevel));
+      //System.out.println(String.format("lft %s, rgt %s, level %s", lft, rgt, level));
+      for (Permission childEntity : childEntities) {
+        //Permission pEntity = this.selectById(childEntity.getParentId());
+        
+        int cLft = childEntity.getLft();
+        int cRgt = childEntity.getRgt();
+        int cLevel = childEntity.getLevel();
+        
+        childEntity.setLft(cLft + (newLft - lft));
+        childEntity.setRgt(cRgt + (newRgt - rgt));
+        
+        childEntity.setLevel(cLevel + (newLevel - level));
+        
+        BaseJpaRepository.super.update(childEntity);
+      }
+
+    }
+    
+    return BaseJpaRepository.super.update(entity);
+  }
+
+  @Override
+  public default void delete(String id) {
+    
+    Permission originEntity = this.selectById(id);
+    if (originEntity == null) {
+      return;
+    }
+
+    int lft = originEntity.getLft();
+    int rgt = originEntity.getRgt();
+    int offset = rgt - lft +1;
+
+    // FIXME: 判断是否有子节点
+    //if (lft + 1 != rgt) {
+    //  return;
+    //}
+
+    List<Permission> childEntities = this.selectBetweenLftRgt(lft, rgt);
+    for (Permission childEntity : childEntities) {
+      BaseJpaRepository.super.delete(childEntity.getId());
+    }
+    
+    // 将 lft或rgt 大于等于该对象 rgt 的记录的 lft、rgt -offset
+    updateLftRgtWhenDelete(rgt, offset);
+    
+    BaseJpaRepository.super.delete(id);
+  }
+  
+  
+
+
+  public default List<Permission> selectList(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean, Map<String, String> orderBy) {
+    
+    Specification<Permission> spec = convertToSpec(mapBean);
+    
+    if (loadAll) {
+      pageIndex = 0;
+      pageSize = Integer.MAX_VALUE;
+    }
+    
+    Sort sort = convertToSort(orderBy);
+
+    if (sort == null) {
+      return this.findAll(spec);
+    } else {
+      return this.findAll(spec, sort);
+    }
+  }
+
+  
+
+  @Query(value = "select p from Permission p "
+      + "inner join RolePermission rp on p.id=rp.permissionId "
+      + "inner join Role r on rp.roleId=r.id "
+      + "inner join AccountRole ar on r.id=ar.roleId "
+      + "inner join Account a on ar.accountId=a.id "
+      + "where a.username=:username "
+      + "and p.lft >= :lft and p.rgt <= :rgt "
+      + "and (:type is null or p.type=:type) "
+      + "and p.status='1' and r.status='1' and a.status='1' and a.enabled=1 ")
+  public List<Permission> selectAccountRolePermissionByUsername(@Param("username") String username, @Param("lft") int lft, @Param("rgt") int rgt, @Param("type") String type);
+  
+  @Query(value = "select p from Permission p "
+      + "inner join RolePermission rp on p.id=rp.permissionId "
+      + "inner join Role r on rp.roleId=r.id "
+      + "inner join GroupRole gr on r.id=gr.roleId "
+      + "inner join Group_ g on gr.groupId=g.id "
+      + "inner join AccountGroup ag on g.id=ag.groupId "
+      + "inner join Account a on ag.accountId=a.id "
+      + "where a.username=:username "
+      + "and p.lft >= :lft and p.rgt <= :rgt "
+      + "and (:type is null or p.type=:type) "
+      + "and p.status='1' and r.status='1' and g.status='1' and a.status='1' and a.enabled=1 ")
+  public List<Permission> selectAccountGroupRolePermissionByUsername(@Param("username") String username, @Param("lft") int lft, @Param("rgt") int rgt, @Param("type") String type);
+
+
+  @Query(value = "select p from Permission p "
+      + "inner join RolePermission rp on p.id=rp.permissionId "
+      + "inner join Role r on rp.roleId=r.id "
+      + "inner join AccountRole ar on r.id=ar.roleId "
+      + "inner join Account a on ar.accountId=a.id "
+      + "where a.username=:username "
+      + "and p.applicationId = :applicationId "
+      + "and (:type is null or p.type=:type) "
+      + "and p.status='1' and r.status='1' and a.status='1' and a.enabled=1 ")
+  public List<Permission> selectAccountRolePermissionByUsername(@Param("username") String username, @Param("applicationId") String applicationId, @Param("type") String type);
+  
+  @Query(value = "select p from Permission p "
+      + "inner join RolePermission rp on p.id=rp.permissionId "
+      + "inner join Role r on rp.roleId=r.id "
+      + "inner join GroupRole gr on r.id=gr.roleId "
+      + "inner join Group_ g on gr.groupId=g.id "
+      + "inner join AccountGroup ag on g.id=ag.groupId "
+      + "inner join Account a on ag.accountId=a.id "
+      + "where a.username=:username "
+      + "and p.applicationId = :applicationId "
+      + "and (:type is null or p.type=:type) "
+      + "and p.status='1' and r.status='1' and g.status='1' and a.status='1' and a.enabled=1 ")
+  public List<Permission> selectAccountGroupRolePermissionByUsername(@Param("username") String username, @Param("applicationId") String applicationId, @Param("type") String type);
+
+
+  @Query(value = "select p from Permission p "
+      + "inner join RolePermission rp on p.id=rp.permissionId "
+      + "inner join Role r on rp.roleId=r.id "
+      + "inner join AccountRole ar on r.id=ar.roleId "
+      + "inner join Account a on ar.accountId=a.id "
+      + "where a.username=:username "
+      + "and (:type is null or p.type=:type) "
+      + "and p.status='1' and r.status='1' and a.status='1' and a.enabled=1 ")
+  public List<Permission> selectAccountRolePermissionByUsername(@Param("username") String username, @Param("type") String type);
+  
+  @Query(value = "select p from Permission p "
+      + "inner join RolePermission rp on p.id=rp.permissionId "
+      + "inner join Role r on rp.roleId=r.id "
+      + "inner join GroupRole gr on r.id=gr.roleId "
+      + "inner join Group_ g on gr.groupId=g.id "
+      + "inner join AccountGroup ag on g.id=ag.groupId "
+      + "inner join Account a on ag.accountId=a.id "
+      + "where a.username=:username "
+      + "and (:type is null or p.type=:type) "
+      + "and p.status='1' and r.status='1' and g.status='1' and a.status='1' and a.enabled=1 ")
+  public List<Permission> selectAccountGroupRolePermissionByUsername(@Param("username") String username, @Param("type") String type);
+
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/PermissionResourceRepository.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/PermissionResourceRepository.java
new file mode 100644
index 0000000..c40aef7
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/PermissionResourceRepository.java
@@ -0,0 +1,194 @@
+package com.supwisdom.institute.backend.base.domain.repo;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleMatcher;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Repository;
+
+import com.supwisdom.institute.backend.base.domain.entity.Permission;
+import com.supwisdom.institute.backend.base.domain.entity.PermissionResource;
+import com.supwisdom.institute.backend.base.domain.entity.Resource;
+import com.supwisdom.institute.backend.common.framework.repo.BaseJpaRepository;
+import com.supwisdom.institute.backend.common.util.MapBeanUtils;
+
+@Repository
+public interface PermissionResourceRepository extends BaseJpaRepository<PermissionResource> {
+
+  public default Page<PermissionResource> selectPageList(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+    PermissionResource probe = new PermissionResource();
+    if (mapBean != null) {
+      probe.setPermissionId(MapBeanUtils.getString(mapBean, "permissionId"));
+      probe.setResourceId(MapBeanUtils.getString(mapBean, "resourceId"));
+    }
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("permissionId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("resourceId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+    Example<PermissionResource> example = Example.of(probe, matcher);
+
+    Page<PermissionResource> page = this.findAll(example, pageRequest);
+
+    return page;
+  }
+
+  public default Page<PermissionResource> selectPermissionResources(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+
+    PermissionResource probe = new PermissionResource();
+    if (mapBean != null) {
+      probe.setPermissionId(MapBeanUtils.getString(mapBean, "permissionId"));
+      probe.setResourceId(MapBeanUtils.getString(mapBean, "resourceId"));
+    }
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("permissionId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("resourceId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<PermissionResource> example = Example.of(probe, matcher);
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+
+    Page<PermissionResource> page = this.findAll(example, pageRequest); // FIXME: 多表关联查询
+
+    return page;
+  }
+
+  public default void relatePermissionResources(Permission permission, List<PermissionResource> relateResources) {
+
+    List<PermissionResource> existPermissionResources = this.selectListByPermissionId(permission.getId());
+
+    Map<String, PermissionResource> existMapPermissionResources = new LinkedHashMap<String, PermissionResource>();
+    for (PermissionResource permissionResource : existPermissionResources) {
+      String k = String.format("%s", permissionResource.getResourceId());
+      existMapPermissionResources.put(k, permissionResource);
+    }
+
+    for (PermissionResource permissionResource : relateResources) {
+      String k = String.format("%s", permissionResource.getResourceId());
+
+      if (existMapPermissionResources.containsKey(k)) {
+        existMapPermissionResources.remove(k);
+      } else {
+        permissionResource.setCompanyId(permission.getCompanyId());
+        permissionResource.setPermissionId(permission.getId());
+
+        this.insert(permissionResource);
+      }
+    }
+
+    for (PermissionResource rolePermission : existMapPermissionResources.values()) {
+      this.deleteById(rolePermission.getId());
+    }
+  }
+
+  public default List<PermissionResource> selectListByPermissionId(String permissionId) {
+
+    PermissionResource probe = new PermissionResource();
+    probe.setPermissionId(permissionId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("permissionId",
+        ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<PermissionResource> example = Example.of(probe, matcher);
+
+    List<PermissionResource> rolePermissions = this.findAll(example);
+
+    return rolePermissions;
+  }
+
+  
+  public default void relateResourcePermissions(Resource resource, List<PermissionResource> relatePermissions) {
+
+    // 获取权限已关联的角色
+    List<PermissionResource> existResourcePermissions = this.selectListByResourceId(resource.getId());
+
+    Map<String, PermissionResource> existMapResourcePermissions = new LinkedHashMap<String, PermissionResource>();
+    for (PermissionResource permissionResource : existResourcePermissions) {
+      String k = String.format("%s", permissionResource.getPermissionId());
+      existMapResourcePermissions.put(k, permissionResource);
+    }
+
+    // 保存未关联的角色
+    for (PermissionResource permissionResource : relatePermissions) {
+      String k = String.format("%s", permissionResource.getPermissionId());
+
+      if (existMapResourcePermissions.containsKey(k)) {
+        existMapResourcePermissions.remove(k);
+      } else {
+        permissionResource.setCompanyId(resource.getCompanyId());
+        permissionResource.setResourceId(resource.getId());
+
+        this.insert(permissionResource);
+      }
+    }
+
+    // 删除移除关联的角色
+    for (PermissionResource rolePermission : existMapResourcePermissions.values()) {
+      this.deleteById(rolePermission.getId());
+    }
+  }
+  public default List<PermissionResource> selectListByResourceId(String resourceId) {
+
+    PermissionResource probe = new PermissionResource();
+    probe.setResourceId(resourceId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("resourceId",
+        ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<PermissionResource> example = Example.of(probe, matcher);
+
+    List<PermissionResource> rolePermissions = this.findAll(example);
+
+    return rolePermissions;
+  }
+
+  
+  public default PermissionResource selectOneByPermissionResource(String permissionId, String resourceId) {
+
+    PermissionResource probe = new PermissionResource();
+    probe.setPermissionId(permissionId);
+    probe.setResourceId(resourceId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("permissionId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("resourceId", ExampleMatcher.GenericPropertyMatchers.exact())
+        ;
+
+    Example<PermissionResource> example = Example.of(probe, matcher);
+    
+    Optional<PermissionResource> o = this.findOne(example);
+    
+    return o.isPresent() ? o.get() : null;
+  }
+
+  public default void addPermissionResource(String permissionId, String resourceId) {
+
+    PermissionResource rolePermission = this.selectOneByPermissionResource(permissionId, resourceId);
+    
+    if (rolePermission == null) {
+      rolePermission = new PermissionResource();
+      //rolePermission.setCompanyId(companyId);
+      rolePermission.setPermissionId(permissionId);
+      rolePermission.setResourceId(resourceId);
+      
+      this.insert(rolePermission);
+    }
+  }
+
+  public default void removePermissionResource(String permissionId, String resourceId) {
+
+    PermissionResource rolePermission = this.selectOneByPermissionResource(permissionId, resourceId);
+    
+    if (rolePermission != null) {
+      this.deleteById(rolePermission.getId());
+    }
+  }
+
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/ResourceRepository.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/ResourceRepository.java
new file mode 100644
index 0000000..a037866
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/ResourceRepository.java
@@ -0,0 +1,127 @@
+package com.supwisdom.institute.backend.base.domain.repo;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleMatcher;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.stereotype.Repository;
+import org.springframework.util.StringUtils;
+
+import com.supwisdom.institute.backend.base.domain.entity.Resource;
+import com.supwisdom.institute.backend.common.framework.repo.BaseJpaRepository;
+import com.supwisdom.institute.backend.common.util.MapBeanUtils;
+
+@Repository
+public interface ResourceRepository extends BaseJpaRepository<Resource> {
+
+  @Override
+  public default Specification<Resource> convertToSpec(Map<String, Object> mapBean) {
+    
+    Specification<Resource> spec = new Specification<Resource>() {
+
+      /**
+       * 
+       */
+      private static final long serialVersionUID = 7690039558726467041L;
+
+      @Override
+      public Predicate toPredicate(Root<Resource> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
+        List<Predicate> predicates = new ArrayList<>();
+        
+        if (mapBean != null) {
+
+          if (MapBeanUtils.getBoolean(mapBean, "deleted") != null) {
+            predicates.add(criteriaBuilder.equal(root.get("deleted"), MapBeanUtils.getBoolean(mapBean, "deleted")));
+          }
+
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "code"))) {
+            predicates.add(criteriaBuilder.like(root.get("code"), "%" + MapBeanUtils.getString(mapBean, "code") + "%"));
+          }
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "name"))) {
+            predicates.add(criteriaBuilder.like(root.get("name"), "%" + MapBeanUtils.getString(mapBean, "name") + "%"));
+          }
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "memo"))) {
+            predicates.add(criteriaBuilder.like(root.get("memo"), "%" + MapBeanUtils.getString(mapBean, "memo") + "%"));
+          }
+          
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "status"))) {
+            predicates.add(criteriaBuilder.equal(root.get("status"), MapBeanUtils.getString(mapBean, "status")));
+          }
+          
+          List<Predicate> predicatesKeyword = new ArrayList<>();
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "keyword"))) {
+            predicatesKeyword.add(criteriaBuilder.like(root.get("code"), "%" + MapBeanUtils.getString(mapBean, "keyword") + "%"));
+            predicatesKeyword.add(criteriaBuilder.like(root.get("name"), "%" + MapBeanUtils.getString(mapBean, "keyword") + "%"));
+            predicatesKeyword.add(criteriaBuilder.like(root.get("memo"), "%" + MapBeanUtils.getString(mapBean, "keyword") + "%"));
+            
+            predicates.add(criteriaBuilder.or(predicatesKeyword.toArray(new Predicate[predicatesKeyword.size()])));
+          }
+        }
+        
+        return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
+      }
+      
+    };
+    
+    return spec;
+  }
+  
+//  @Override
+//  public default Page<Resource> selectPageList(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean, Map<String, String> orderBy) {
+//    if (loadAll) {
+//      pageIndex = 0;
+//      pageSize = Integer.MAX_VALUE;
+//    }
+//    
+//    Resource probe = new Resource();
+//    if (mapBean != null) {
+//      probe.setDeleted(MapBeanUtils.getBoolean(mapBean, "deleted"));
+//      probe.setCode(MapBeanUtils.getString(mapBean, "code"));
+//      probe.setName(MapBeanUtils.getString(mapBean, "name"));
+//      probe.setMemo(MapBeanUtils.getString(mapBean, "memo"));
+//      probe.setStatus(MapBeanUtils.getString(mapBean, "status"));
+//    }
+//    
+//    ExampleMatcher matcher = ExampleMatcher.matching()
+//        .withMatcher("deleted", ExampleMatcher.GenericPropertyMatchers.exact())
+//        .withMatcher("code", ExampleMatcher.GenericPropertyMatchers.contains())
+//        .withMatcher("name", ExampleMatcher.GenericPropertyMatchers.contains())
+//        .withMatcher("memo", ExampleMatcher.GenericPropertyMatchers.contains())
+//        .withMatcher("status", ExampleMatcher.GenericPropertyMatchers.exact());
+//    
+//    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+//    Example<Resource> example = Example.of(probe, matcher);
+//    
+//    Page<Resource> page = this.findAll(example, pageRequest);
+//    
+//    return page;
+//  }
+
+  public default Resource selectByCode(String code) {
+    Resource probe = new Resource();
+    probe.setCode(code);
+    
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("code", ExampleMatcher.GenericPropertyMatchers.exact());
+    
+    Example<Resource> example = Example.of(probe, matcher);
+    
+    Optional<Resource> o = this.findOne(example);
+    
+    if (o.isPresent()) {
+      return o.get();
+    }
+    
+    return null;
+  }
+  
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/RolePermissionRepository.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/RolePermissionRepository.java
new file mode 100644
index 0000000..95f2814
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/RolePermissionRepository.java
@@ -0,0 +1,193 @@
+package com.supwisdom.institute.backend.base.domain.repo;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleMatcher;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Repository;
+
+import com.supwisdom.institute.backend.base.domain.entity.Permission;
+import com.supwisdom.institute.backend.base.domain.entity.Role;
+import com.supwisdom.institute.backend.base.domain.entity.RolePermission;
+import com.supwisdom.institute.backend.common.framework.repo.BaseJpaRepository;
+import com.supwisdom.institute.backend.common.util.MapBeanUtils;
+
+@Repository
+public interface RolePermissionRepository extends BaseJpaRepository<RolePermission> {
+
+  public default Page<RolePermission> selectPageList(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+    RolePermission probe = new RolePermission();
+    if (mapBean != null) {
+      probe.setRoleId(MapBeanUtils.getString(mapBean, "roleId"));
+      probe.setPermissionId(MapBeanUtils.getString(mapBean, "permissionId"));
+    }
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("roleId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("permissionId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+    Example<RolePermission> example = Example.of(probe, matcher);
+
+    Page<RolePermission> page = this.findAll(example, pageRequest);
+
+    return page;
+  }
+
+  public default Page<RolePermission> selectRolePermissions(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+
+    RolePermission probe = new RolePermission();
+    if (mapBean != null) {
+      probe.setRoleId(MapBeanUtils.getString(mapBean, "roleId"));
+      probe.setPermissionId(MapBeanUtils.getString(mapBean, "permissionId"));
+    }
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("roleId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("permissionId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<RolePermission> example = Example.of(probe, matcher);
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+
+    Page<RolePermission> page = this.findAll(example, pageRequest); // FIXME: 多表关联查询
+
+    return page;
+  }
+
+  public default void relateRolePermissions(Role role, List<RolePermission> rolePermissions) {
+
+    List<RolePermission> existRolePermissions = this.selectListByRoleId(role.getId());
+
+    Map<String, RolePermission> existMapRolePermissions = new LinkedHashMap<String, RolePermission>();
+    for (RolePermission rolePermission : existRolePermissions) {
+      String k = String.format("%s", rolePermission.getPermissionId());
+      existMapRolePermissions.put(k, rolePermission);
+    }
+
+    for (RolePermission rolePermission : rolePermissions) {
+      String k = String.format("%s", rolePermission.getPermissionId());
+
+      if (existMapRolePermissions.containsKey(k)) {
+        existMapRolePermissions.remove(k);
+      } else {
+        rolePermission.setCompanyId(role.getCompanyId());
+        rolePermission.setRoleId(role.getId());
+
+        this.insert(rolePermission);
+      }
+    }
+
+    for (RolePermission rolePermission : existMapRolePermissions.values()) {
+      this.deleteById(rolePermission.getId());
+    }
+  }
+
+  public default List<RolePermission> selectListByRoleId(String roleId) {
+
+    RolePermission probe = new RolePermission();
+    probe.setRoleId(roleId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("roleId",
+        ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<RolePermission> example = Example.of(probe, matcher);
+
+    List<RolePermission> rolePermissions = this.findAll(example);
+
+    return rolePermissions;
+  }
+
+  public default void relatePermissionRoles(Permission permission, List<RolePermission> rolePermissions) {
+
+    // 获取权限已关联的角色
+    List<RolePermission> existPermissionRoles = this.selectListByPermissionId(permission.getId());
+
+    Map<String, RolePermission> existMapPermissionRoles = new LinkedHashMap<String, RolePermission>();
+    for (RolePermission rolePermission : existPermissionRoles) {
+      String k = String.format("%s", rolePermission.getRoleId());
+      existMapPermissionRoles.put(k, rolePermission);
+    }
+
+    // 保存未关联的角色
+    for (RolePermission rolePermission : rolePermissions) {
+      String k = String.format("%s", rolePermission.getRoleId());
+
+      if (existMapPermissionRoles.containsKey(k)) {
+        existMapPermissionRoles.remove(k);
+      } else {
+        rolePermission.setCompanyId(permission.getCompanyId());
+        rolePermission.setPermissionId(permission.getId());
+
+        this.insert(rolePermission);
+      }
+    }
+
+    // 删除移除关联的角色
+    for (RolePermission rolePermission : existMapPermissionRoles.values()) {
+      this.deleteById(rolePermission.getId());
+    }
+  }
+
+  public default List<RolePermission> selectListByPermissionId(String permissionId) {
+
+    RolePermission probe = new RolePermission();
+    probe.setPermissionId(permissionId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("permissionId",
+        ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<RolePermission> example = Example.of(probe, matcher);
+
+    List<RolePermission> rolePermissions = this.findAll(example);
+
+    return rolePermissions;
+  }
+
+  public default RolePermission selectOneByRolePermission(String roleId, String permissionId) {
+
+    RolePermission probe = new RolePermission();
+    probe.setRoleId(roleId);
+    probe.setPermissionId(permissionId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("roleId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("permissionId", ExampleMatcher.GenericPropertyMatchers.exact())
+        ;
+
+    Example<RolePermission> example = Example.of(probe, matcher);
+    
+    Optional<RolePermission> o = this.findOne(example);
+    
+    return o.isPresent() ? o.get() : null;
+  }
+
+  public default void addRolePermission(String roleId, String permissionId) {
+
+    RolePermission rolePermission = this.selectOneByRolePermission(roleId, permissionId);
+    
+    if (rolePermission == null) {
+      rolePermission = new RolePermission();
+      //rolePermission.setCompanyId(companyId);
+      rolePermission.setRoleId(roleId);
+      rolePermission.setPermissionId(permissionId);
+      
+      this.insert(rolePermission);
+    }
+  }
+
+  public default void removeRolePermission(String roleId, String permissionId) {
+
+    RolePermission rolePermission = this.selectOneByRolePermission(roleId, permissionId);
+    
+    if (rolePermission != null) {
+      this.deleteById(rolePermission.getId());
+    }
+  }
+
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/RoleRepository.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/RoleRepository.java
new file mode 100644
index 0000000..00c3ee6
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/RoleRepository.java
@@ -0,0 +1,164 @@
+package com.supwisdom.institute.backend.base.domain.repo;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleMatcher;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+import org.springframework.util.StringUtils;
+
+import com.supwisdom.institute.backend.base.domain.entity.Role;
+import com.supwisdom.institute.backend.common.framework.repo.BaseJpaRepository;
+import com.supwisdom.institute.backend.common.util.MapBeanUtils;
+
+@Repository
+public interface RoleRepository extends BaseJpaRepository<Role> {
+
+  @Override
+  public default Specification<Role> convertToSpec(Map<String, Object> mapBean) {
+    
+    Specification<Role> spec = new Specification<Role>() {
+
+      /**
+       * 
+       */
+      private static final long serialVersionUID = -263282246904382286L;
+
+      @Override
+      public Predicate toPredicate(Root<Role> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
+        List<Predicate> predicates = new ArrayList<>();
+        
+        if (mapBean != null) {
+
+          if (MapBeanUtils.getBoolean(mapBean, "deleted") != null) {
+            predicates.add(criteriaBuilder.equal(root.get("deleted"), MapBeanUtils.getBoolean(mapBean, "deleted")));
+          }
+
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "code"))) {
+            predicates.add(criteriaBuilder.like(root.get("code"), "%" + MapBeanUtils.getString(mapBean, "code") + "%"));
+          }
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "name"))) {
+            predicates.add(criteriaBuilder.like(root.get("name"), "%" + MapBeanUtils.getString(mapBean, "name") + "%"));
+          }
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "memo"))) {
+            predicates.add(criteriaBuilder.like(root.get("memo"), "%" + MapBeanUtils.getString(mapBean, "memo") + "%"));
+          }
+          
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "status"))) {
+            predicates.add(criteriaBuilder.equal(root.get("status"), MapBeanUtils.getString(mapBean, "status")));
+          }
+
+          List<Predicate> predicatesKeyword = new ArrayList<>();
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "keyword"))) {
+            predicatesKeyword.add(criteriaBuilder.like(root.get("code"), "%" + MapBeanUtils.getString(mapBean, "keyword") + "%"));
+            predicatesKeyword.add(criteriaBuilder.like(root.get("name"), "%" + MapBeanUtils.getString(mapBean, "keyword") + "%"));
+            predicatesKeyword.add(criteriaBuilder.like(root.get("memo"), "%" + MapBeanUtils.getString(mapBean, "keyword") + "%"));
+            
+            predicates.add(criteriaBuilder.or(predicatesKeyword.toArray(new Predicate[predicatesKeyword.size()])));
+          }
+        }
+        
+        return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
+      }
+      
+    };
+    
+    return spec;
+  }
+
+//  @Override
+//  public default Page<Role> selectPageList(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean, Map<String, String> orderBy) {
+//    if (loadAll) {
+//      pageIndex = 0;
+//      pageSize = Integer.MAX_VALUE;
+//    }
+//    
+//    Role probe = new Role();
+//    if (mapBean != null) {
+//      probe.setDeleted(MapBeanUtils.getBoolean(mapBean, "deleted"));
+//      probe.setCode(MapBeanUtils.getString(mapBean, "code"));
+//      probe.setName(MapBeanUtils.getString(mapBean, "name"));
+//      probe.setMemo(MapBeanUtils.getString(mapBean, "memo"));
+//      probe.setStatus(MapBeanUtils.getString(mapBean, "status"));
+//    }
+//    
+//    ExampleMatcher matcher = ExampleMatcher.matching()
+//        .withMatcher("deleted", ExampleMatcher.GenericPropertyMatchers.exact())
+//        .withMatcher("code", ExampleMatcher.GenericPropertyMatchers.contains())
+//        .withMatcher("name", ExampleMatcher.GenericPropertyMatchers.contains())
+//        .withMatcher("memo", ExampleMatcher.GenericPropertyMatchers.contains())
+//        .withMatcher("status", ExampleMatcher.GenericPropertyMatchers.exact());
+//    
+//    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+//    Example<Role> example = Example.of(probe, matcher);
+//    
+//    Page<Role> page = this.findAll(example, pageRequest);
+//    
+//    return page;
+//  }
+  
+  
+  public default Role selectByCode(String code) {
+    Role probe = new Role();
+    probe.setCode(code);
+    
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("code", ExampleMatcher.GenericPropertyMatchers.exact());
+    
+    Example<Role> example = Example.of(probe, matcher);
+    
+    Optional<Role> o = this.findOne(example);
+    
+    if (o.isPresent()) {
+      return o.get();
+    }
+    
+    return null;
+  }
+  
+  @Query(value = "select r from Role r "
+      + "inner join AccountRole ar on r.id=ar.roleId "
+      + "inner join Account a on ar.accountId=a.id "
+      + "where a.username=:username "
+      + "and r.status='1' and a.status='1' and a.enabled=1 ")
+  public List<Role> selectAccountRoleByUsername(@Param("username") String username);
+  
+  @Query(value = "select r from Role r "
+      + "inner join GroupRole gr on r.id=gr.roleId "
+      + "inner join Group_ g on gr.groupId=g.id "
+      + "inner join AccountGroup ag on g.id=ag.groupId "
+      + "inner join Account a on ag.accountId=a.id "
+      + "where a.username=:username "
+      + "and r.status='1' and g.status='1' and a.status='1' and a.enabled=1 ")
+  public List<Role> selectAccountGroupRoleByUsername(@Param("username") String username);
+
+  
+  @Query(value = "select r from Role r "
+      + "inner join RolePermission rp on r.id=rp.roleId "
+      + "inner join Permission p on rp.permissionId=p.id "
+      + "where p.id=:permissionId "
+      + "and r.status='1' and p.status='1' ")
+  public List<Role> selectPermissionRolesByPermission(@Param("permissionId") String permissionId);
+  
+  
+  @Query(value = "select r from Role r "
+      + "inner join RolePermission rp on r.id=rp.roleId "
+      + "inner join Permission p on rp.permissionId=p.id "
+      + "inner join PermissionResource pres on p.id=pres.permissionId "
+      + "inner join Resource res on pres.resourceId=res.id "
+      + "where res.id=:resourceId "
+      + "and r.status='1' and p.status='1' and res.status='1' ")
+  public List<Role> selectResourceRolesByResource(@Param("resourceId") String resourceId);
+  
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/RouteRepository.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/RouteRepository.java
new file mode 100644
index 0000000..425138b
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/RouteRepository.java
@@ -0,0 +1,76 @@
+package com.supwisdom.institute.backend.base.domain.repo;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.stereotype.Repository;
+import org.springframework.util.StringUtils;
+
+import com.supwisdom.institute.backend.base.domain.entity.Route;
+import com.supwisdom.institute.backend.common.framework.repo.BaseJpaRepository;
+import com.supwisdom.institute.backend.common.util.MapBeanUtils;
+
+@Repository
+public interface RouteRepository extends BaseJpaRepository<Route> {
+  
+  @Override
+  public default Specification<Route> convertToSpec(Map<String, Object> mapBean) {
+    
+    Specification<Route> spec = new Specification<Route>() {
+
+      /**
+       * 
+       */
+      private static final long serialVersionUID = 9131799274128286026L;
+
+      @Override
+      public Predicate toPredicate(Root<Route> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
+        List<Predicate> predicates = new ArrayList<>();
+        
+        if (mapBean != null) {
+
+          if (MapBeanUtils.getBoolean(mapBean, "deleted") != null) {
+            predicates.add(criteriaBuilder.equal(root.get("deleted"), MapBeanUtils.getBoolean(mapBean, "deleted")));
+          }
+
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "code"))) {
+            predicates.add(criteriaBuilder.like(root.get("code"), "%" + MapBeanUtils.getString(mapBean, "code") + "%"));
+          }
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "name"))) {
+            predicates.add(criteriaBuilder.like(root.get("name"), "%" + MapBeanUtils.getString(mapBean, "name") + "%"));
+          }
+          
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "status"))) {
+            predicates.add(criteriaBuilder.equal(root.get("status"), MapBeanUtils.getString(mapBean, "status")));
+          }
+
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "url"))) {
+            predicates.add(criteriaBuilder.like(root.get("url"), "%" + MapBeanUtils.getString(mapBean, "url") + "%"));
+          }
+          
+          List<Predicate> predicatesKeyword = new ArrayList<>();
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "keyword"))) {
+            predicatesKeyword.add(criteriaBuilder.like(root.get("code"), "%" + MapBeanUtils.getString(mapBean, "keyword") + "%"));
+            predicatesKeyword.add(criteriaBuilder.like(root.get("name"), "%" + MapBeanUtils.getString(mapBean, "keyword") + "%"));
+            predicatesKeyword.add(criteriaBuilder.like(root.get("memo"), "%" + MapBeanUtils.getString(mapBean, "keyword") + "%"));
+            
+            predicates.add(criteriaBuilder.or(predicatesKeyword.toArray(new Predicate[predicatesKeyword.size()])));
+          }
+        }
+        
+        return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
+      }
+      
+    };
+    
+    return spec;
+  }
+
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/.gitkeep b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/.gitkeep
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/AccountService.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/AccountService.java
new file mode 100644
index 0000000..b0cad6c
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/AccountService.java
@@ -0,0 +1,203 @@
+package com.supwisdom.institute.backend.base.domain.service;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleMatcher;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Service;
+
+import com.supwisdom.institute.backend.base.domain.entity.Account;
+import com.supwisdom.institute.backend.base.domain.entity.AccountGroup;
+import com.supwisdom.institute.backend.base.domain.entity.AccountRole;
+import com.supwisdom.institute.backend.base.domain.repo.AccountGroupRepository;
+import com.supwisdom.institute.backend.base.domain.repo.AccountRepository;
+import com.supwisdom.institute.backend.base.domain.repo.AccountRoleRepository;
+import com.supwisdom.institute.backend.common.framework.service.ABaseService;
+import com.supwisdom.institute.backend.common.util.MapBeanUtils;
+
+@Service
+public class AccountService extends ABaseService<Account, AccountRepository> {
+  
+  @Override
+  public AccountRepository getRepo() {
+    return accountRepository;
+  }
+
+  @Autowired
+  private AccountRepository accountRepository;
+
+  @Autowired
+  private AccountGroupRepository accountGroupRepository;
+
+  @Autowired
+  private AccountRoleRepository accountRoleRepository;
+  
+  
+  @Override
+  public Account insert(Account entity) {
+
+    entity.setEnabled(Account.STATUS_ENABLED.equals(entity.getStatus()));
+    entity.setAccountNonExpired(true);
+    entity.setAccountNonLocked(true);
+    entity.setCredentialsNonExpired(true);
+    
+    return super.insert(entity);
+  }
+  
+  @Override
+  public Account update(Account entity) {
+    
+    entity.setEnabled(Account.STATUS_ENABLED.equals(entity.getStatus()));
+
+    return super.update(entity);
+  }
+  
+
+  public void deleteBatch(List<String> ids) {
+    
+    ids.stream().forEach(id -> {
+      this.deleteById(id);
+    });
+  }
+
+
+  public Page<AccountGroup> selectAccountGroups(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+
+    AccountGroup probe = new AccountGroup();
+    if (mapBean != null) {
+      probe.setGroupId(MapBeanUtils.getString(mapBean, "groupId"));
+      probe.setAccountId(MapBeanUtils.getString(mapBean, "accountId"));
+    }
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("groupId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("accountId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<AccountGroup> example = Example.of(probe, matcher);
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+
+    Page<AccountGroup> page = accountGroupRepository.findAll(example, pageRequest); // FIXME: 多表关联查询
+
+    return page;
+  }
+
+  public void relateAccountGroups(Account account, List<AccountGroup> accountGroups) {
+
+    List<AccountGroup> existAccountGroups = this.selectAccountGroupsByAccountId(account.getId());
+
+    Map<String, AccountGroup> existMapAccountGroups = new LinkedHashMap<String, AccountGroup>();
+    for (AccountGroup accountGroup : existAccountGroups) {
+      String k = String.format("%s", accountGroup.getGroupId());
+      existMapAccountGroups.put(k, accountGroup);
+    }
+
+    for (AccountGroup accountGroup : accountGroups) {
+      String k = String.format("%s", accountGroup.getGroupId());
+
+      if (existMapAccountGroups.containsKey(k)) {
+        existMapAccountGroups.remove(k);
+      } else {
+        accountGroup.setCompanyId(account.getCompanyId());
+        accountGroup.setAccountId(account.getId());
+
+        accountGroupRepository.insert(accountGroup);
+      }
+    }
+
+    for (AccountGroup accountGroup : existMapAccountGroups.values()) {
+      accountGroupRepository.deleteById(accountGroup.getId());
+    }
+  }
+
+  public List<AccountGroup> selectAccountGroupsByAccountId(String accountId) {
+
+    AccountGroup probe = new AccountGroup();
+    probe.setAccountId(accountId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("accountId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<AccountGroup> example = Example.of(probe, matcher);
+
+    List<AccountGroup> accountGroups = accountGroupRepository.findAll(example);
+
+    return accountGroups;
+  }
+
+  
+
+  public Page<AccountRole> selectAccountRoles(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+
+    AccountRole probe = new AccountRole();
+    if (mapBean != null) {
+      probe.setAccountId(MapBeanUtils.getString(mapBean, "accountId"));
+      probe.setRoleId(MapBeanUtils.getString(mapBean, "roleId"));
+    }
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("accountId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("roleId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<AccountRole> example = Example.of(probe, matcher);
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+
+    Page<AccountRole> page = accountRoleRepository.findAll(example, pageRequest); // FIXME: 多表关联查询
+
+    return page;
+  }
+
+  public void relateAccountRoles(Account account, List<AccountRole> accountRoles) {
+
+    List<AccountRole> existAccountRoles = this.selectAccountRolesByAccountId(account.getId());
+
+    Map<String, AccountRole> existMapAccountRoles = new LinkedHashMap<String, AccountRole>();
+    for (AccountRole accountRole : existAccountRoles) {
+      String k = String.format("%s", accountRole.getRoleId());
+      existMapAccountRoles.put(k, accountRole);
+    }
+
+    for (AccountRole accountRole : accountRoles) {
+      String k = String.format("%s", accountRole.getRoleId());
+
+      if (existMapAccountRoles.containsKey(k)) {
+        existMapAccountRoles.remove(k);
+      } else {
+        accountRole.setCompanyId(account.getCompanyId());
+        accountRole.setAccountId(account.getId());
+
+        accountRoleRepository.insert(accountRole);
+      }
+    }
+
+    for (AccountRole accountRole : existMapAccountRoles.values()) {
+      accountRoleRepository.deleteById(accountRole.getId());
+    }
+  }
+
+  public List<AccountRole> selectAccountRolesByAccountId(String accountId) {
+
+    AccountRole probe = new AccountRole();
+    probe.setAccountId(accountId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("accountId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<AccountRole> example = Example.of(probe, matcher);
+
+    List<AccountRole> accountRoles = accountRoleRepository.findAll(example);
+
+    return accountRoles;
+  }
+
+  
+  public Account selectByUsername(String username) {
+    return accountRepository.selectByUsername(username);
+  }
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/ApplicationService.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/ApplicationService.java
new file mode 100644
index 0000000..17e7503
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/ApplicationService.java
@@ -0,0 +1,91 @@
+package com.supwisdom.institute.backend.base.domain.service;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.supwisdom.institute.backend.base.domain.entity.Application;
+import com.supwisdom.institute.backend.base.domain.entity.Permission;
+import com.supwisdom.institute.backend.base.domain.repo.ApplicationRepository;
+import com.supwisdom.institute.backend.base.domain.repo.PermissionRepository;
+import com.supwisdom.institute.backend.common.framework.service.ABaseService;
+
+@Service
+public class ApplicationService extends ABaseService<Application, ApplicationRepository> {
+
+  @Override
+  public ApplicationRepository getRepo() {
+    return applicationRepository;
+  }
+
+  @Autowired
+  private ApplicationRepository applicationRepository;
+  
+  @Autowired
+  private PermissionRepository permissionRepository;
+
+  @Override
+  public Application insert(Application entity) {
+    entity = super.insert(entity);
+    
+    Permission p = new Permission();
+    p.setId(entity.getId());
+    p.setCode(entity.getCode());
+    p.setName(entity.getName());
+    p.setMemo(entity.getMemo());
+    p.setStatus(entity.getStatus());
+    p.setType(Permission.TYPE_APPLICATION);
+    p.setApplicationId(entity.getId());
+    p.setParentId(Permission.ROOT_PARENT_ID);
+    p.setOrder(Permission.DEFAULT_ORDER);
+    
+    permissionRepository.insert(p);
+    
+    return entity;
+  }
+  
+  @Override
+  public Application update(Application entity) {
+    entity = super.update(entity);
+    
+    Permission p = permissionRepository.selectById(entity.getId());
+    if (p != null) {
+      p = new Permission();
+      p.setCode(entity.getCode());
+      p.setName(entity.getName());
+      p.setMemo(entity.getMemo());
+      p.setStatus(entity.getStatus());
+      p.setType(Permission.TYPE_APPLICATION);
+      p.setApplicationId(entity.getId());
+      p.setParentId(Permission.ROOT_PARENT_ID);
+      p.setOrder(Permission.DEFAULT_ORDER);
+      
+      permissionRepository.update(p);
+    } else {
+      p = new Permission();
+      p.setId(entity.getId());
+      p.setCode(entity.getCode());
+      p.setName(entity.getName());
+      p.setMemo(entity.getMemo());
+      p.setStatus(entity.getStatus());
+      p.setType(Permission.TYPE_APPLICATION);
+      p.setApplicationId(entity.getId());
+      p.setParentId(Permission.ROOT_PARENT_ID);
+      p.setOrder(Permission.DEFAULT_ORDER);
+      
+      permissionRepository.insert(p);
+    }
+    
+    return entity;
+  }
+  
+  public void deleteBatch(List<String> ids) {
+    
+    ids.stream().forEach(id -> {
+      this.deleteById(id);
+    });
+  }
+
+  
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/ConfigService.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/ConfigService.java
new file mode 100644
index 0000000..d41e362
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/ConfigService.java
@@ -0,0 +1,26 @@
+package com.supwisdom.institute.backend.base.domain.service;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.supwisdom.institute.backend.base.domain.entity.Config;
+import com.supwisdom.institute.backend.base.domain.repo.ConfigRepository;
+import com.supwisdom.institute.backend.common.framework.service.ABaseService;
+
+@Service
+public class ConfigService extends ABaseService<Config, ConfigRepository> {
+  
+  @Override
+  public ConfigRepository getRepo() {
+    return configRepository;
+  }
+
+  @Autowired
+  private ConfigRepository configRepository;
+
+  public Config selectByCategoryKey(String categoryCode, String configKey) {
+    
+    return configRepository.selectByCategoryKey(categoryCode, configKey);
+  }
+
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/GroupService.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/GroupService.java
new file mode 100644
index 0000000..9951b3a
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/GroupService.java
@@ -0,0 +1,182 @@
+package com.supwisdom.institute.backend.base.domain.service;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleMatcher;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Service;
+
+import com.supwisdom.institute.backend.base.domain.entity.AccountGroup;
+import com.supwisdom.institute.backend.base.domain.entity.Group;
+import com.supwisdom.institute.backend.base.domain.entity.GroupRole;
+import com.supwisdom.institute.backend.base.domain.repo.AccountGroupRepository;
+import com.supwisdom.institute.backend.base.domain.repo.GroupRepository;
+import com.supwisdom.institute.backend.base.domain.repo.GroupRoleRepository;
+import com.supwisdom.institute.backend.common.framework.service.ABaseService;
+import com.supwisdom.institute.backend.common.util.MapBeanUtils;
+
+@Service
+public class GroupService extends ABaseService<Group, GroupRepository> {
+  
+  @Override
+  public GroupRepository getRepo() {
+    return groupRepository;
+  }
+
+  @Autowired
+  private GroupRepository groupRepository;
+
+  @Autowired
+  private AccountGroupRepository accountGroupRepository;
+
+  @Autowired
+  private GroupRoleRepository groupRoleRepository;
+
+
+  public void deleteBatch(List<String> ids) {
+    
+    ids.stream().forEach(id -> {
+      this.deleteById(id);
+    });
+  }
+
+
+  public Page<AccountGroup> selectGroupAccounts(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+
+    AccountGroup probe = new AccountGroup();
+    if (mapBean != null) {
+      probe.setAccountId(MapBeanUtils.getString(mapBean, "accountId"));
+      probe.setGroupId(MapBeanUtils.getString(mapBean, "groupId"));
+    }
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("accountId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("groupId", ExampleMatcher.GenericPropertyMatchers.exact())
+    ;
+
+    Example<AccountGroup> example = Example.of(probe, matcher);
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+
+    Page<AccountGroup> page = accountGroupRepository.findAll(example, pageRequest); // FIXME: 多表关联查询
+
+    return page;
+  }
+
+  public void relateGroupAccounts(Group group, List<AccountGroup> accountGroups) {
+
+    List<AccountGroup> existGroupAccounts = this.selectGroupAccountsByGroupId(group.getId());
+
+    Map<String, AccountGroup> existMapGroupAccounts = new LinkedHashMap<String, AccountGroup>();
+    for (AccountGroup accountGroup : existGroupAccounts) {
+      String k = String.format("%s", accountGroup.getAccountId());
+      existMapGroupAccounts.put(k, accountGroup);
+    }
+
+    for (AccountGroup accountGroup : accountGroups) {
+      String k = String.format("%s", accountGroup.getAccountId());
+
+      if (existMapGroupAccounts.containsKey(k)) {
+        existMapGroupAccounts.remove(k);
+      } else {
+        accountGroup.setCompanyId(group.getCompanyId());
+        accountGroup.setGroupId(group.getId());
+
+        accountGroupRepository.insert(accountGroup);
+      }
+    }
+
+    for (AccountGroup accountGroup : existMapGroupAccounts.values()) {
+      accountGroupRepository.deleteById(accountGroup.getId());
+    }
+  }
+
+  public List<AccountGroup> selectGroupAccountsByGroupId(String groupId) {
+
+    AccountGroup probe = new AccountGroup();
+    probe.setGroupId(groupId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("groupId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<AccountGroup> example = Example.of(probe, matcher);
+
+    List<AccountGroup> accountGroups = accountGroupRepository.findAll(example);
+
+    return accountGroups;
+  }
+  
+  
+  
+
+  public Page<GroupRole> selectGroupRoles(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+
+    GroupRole probe = new GroupRole();
+    if (mapBean != null) {
+      probe.setGroupId(MapBeanUtils.getString(mapBean, "groupId"));
+      probe.setRoleId(MapBeanUtils.getString(mapBean, "roleId"));
+    }
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("groupId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("roleId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<GroupRole> example = Example.of(probe, matcher);
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+
+    Page<GroupRole> page = groupRoleRepository.findAll(example, pageRequest);  // FIXME: 多表关联查询
+
+    return page;
+  }
+
+  public void relateGroupRoles(Group group, List<GroupRole> groupRoles) {
+
+    List<GroupRole> existGroupRoles = this.selectGroupRolesByGroupId(group.getId());
+
+    Map<String, GroupRole> existMapGroupRoles = new LinkedHashMap<String, GroupRole>();
+    for (GroupRole groupRole : existGroupRoles) {
+      String k = String.format("%s", groupRole.getRoleId());
+      existMapGroupRoles.put(k, groupRole);
+    }
+
+    for (GroupRole groupRole : groupRoles) {
+      String k = String.format("%s", groupRole.getRoleId());
+
+      if (existMapGroupRoles.containsKey(k)) {
+        existMapGroupRoles.remove(k);
+      } else {
+        groupRole.setCompanyId(group.getCompanyId());
+        groupRole.setGroupId(group.getId());
+
+        groupRoleRepository.insert(groupRole);
+      }
+    }
+
+    for (GroupRole groupRole : existMapGroupRoles.values()) {
+      groupRoleRepository.deleteById(groupRole.getId());
+    }
+  }
+
+  public List<GroupRole> selectGroupRolesByGroupId(String groupId) {
+
+    GroupRole probe = new GroupRole();
+    probe.setGroupId(groupId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("groupId",
+        ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<GroupRole> example = Example.of(probe, matcher);
+
+    List<GroupRole> groupRoles = groupRoleRepository.findAll(example);
+
+    return groupRoles;
+  }
+
+
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/PermissionService.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/PermissionService.java
new file mode 100644
index 0000000..c546400
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/PermissionService.java
@@ -0,0 +1,319 @@
+package com.supwisdom.institute.backend.base.domain.service;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import lombok.extern.slf4j.Slf4j;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleMatcher;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Service;
+
+import com.supwisdom.institute.backend.base.domain.entity.Permission;
+import com.supwisdom.institute.backend.base.domain.entity.PermissionResource;
+import com.supwisdom.institute.backend.base.domain.entity.Role;
+import com.supwisdom.institute.backend.base.domain.model.PermissionRoleSet;
+import com.supwisdom.institute.backend.base.domain.model.PermissionTreeNode;
+import com.supwisdom.institute.backend.base.domain.repo.PermissionRepository;
+import com.supwisdom.institute.backend.base.domain.repo.PermissionResourceRepository;
+import com.supwisdom.institute.backend.base.domain.repo.RoleRepository;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.service.ABaseService;
+import com.supwisdom.institute.backend.common.util.MapBeanUtils;
+
+@Slf4j
+@Service
+public class PermissionService extends ABaseService<Permission, PermissionRepository> {
+  
+  @Override
+  public PermissionRepository getRepo() {
+    return permissionRepository;
+  }
+
+  @Autowired
+  private PermissionRepository permissionRepository;
+
+  @Autowired
+  private PermissionResourceRepository permissionResourceRepository;
+
+  @Autowired
+  private RoleRepository roleRepository;
+
+  @Override
+  public Permission insert(Permission entity) {
+    Permission parentPermission = permissionRepository.selectById(entity.getParentId());
+    if (parentPermission != null) {
+      entity.setApplicationId(parentPermission.getApplicationId());
+    }
+    
+    return super.insert(entity);
+  }
+
+  public void deleteBatch(List<String> ids) {
+    
+    ids.stream().forEach(id -> {
+      this.deleteById(id);
+    });
+  }
+  
+  
+
+  public Page<PermissionResource> selectPermissionResources(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean) {
+    
+    if (loadAll) {
+      pageIndex = 0;
+      pageSize = Integer.MAX_VALUE;
+    }
+
+    PermissionResource probe = new PermissionResource();
+    if (mapBean != null) {
+      probe.setPermissionId(MapBeanUtils.getString(mapBean, "permissionId"));
+      probe.setResourceId(MapBeanUtils.getString(mapBean, "resourceId"));
+    }
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("permissionId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("resourceId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<PermissionResource> example = Example.of(probe, matcher);
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+
+    Page<PermissionResource> page = permissionResourceRepository.findAll(example, pageRequest); // FIXME: 多表关联查询
+
+    return page;
+  }
+
+  public void relatePermissionResources(Permission permission, List<PermissionResource> permissionResources) {
+
+    List<PermissionResource> existPermissionResources = this.selectPermissionResourcesByPermissionId(permission.getId());
+
+    Map<String, PermissionResource> existMapPermissionResources = new LinkedHashMap<String, PermissionResource>();
+    for (PermissionResource permissionResource : existPermissionResources) {
+      String k = String.format("%s", permissionResource.getPermissionId());
+      existMapPermissionResources.put(k, permissionResource);
+    }
+
+    for (PermissionResource permissionResource : permissionResources) {
+      String k = String.format("%s", permissionResource.getPermissionId());
+
+      if (existMapPermissionResources.containsKey(k)) {
+        existMapPermissionResources.remove(k);
+      } else {
+        permissionResource.setCompanyId(permission.getCompanyId());
+        permissionResource.setPermissionId(permission.getId());
+
+        permissionResourceRepository.insert(permissionResource);
+      }
+    }
+
+    for (PermissionResource permissionResource : existMapPermissionResources.values()) {
+      permissionResourceRepository.deleteById(permissionResource.getId());
+    }
+  }
+
+  public List<PermissionResource> selectPermissionResourcesByPermissionId(String permissionId) {
+
+    PermissionResource probe = new PermissionResource();
+    probe.setPermissionId(permissionId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("permissionId",
+        ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<PermissionResource> example = Example.of(probe, matcher);
+
+    List<PermissionResource> permissionResources = permissionResourceRepository.findAll(example);
+
+    return permissionResources;
+  }
+  
+  
+  private Permission selectApplicationPermission() {
+    Permission permission = permissionRepository.selectById(Permission.APPLICATION_ID);
+    
+    return permission;
+  }
+  
+  private List<Permission> selectApplicationPermissionList() {
+    Map<String, Object> mapBean = new HashMap<String, Object>();
+    mapBean.put("type", Permission.TYPE_APPLICATION);
+    
+    return permissionRepository.selectList(true, -1, -1, mapBean, null);
+  }
+
+  public PermissionTreeNode selectPermissionTree(String applicationId, String type) {
+
+    Map<String, Object> mapBean = new HashMap<String, Object>();
+    if (StringUtils.isNotBlank(type)) {
+      mapBean.put("type", type);
+    }
+    
+    String rootTreeNodeId = "0";
+    
+    List<Permission> permissions = new ArrayList<Permission>();
+    
+    if (StringUtils.isBlank(applicationId)) {
+      Permission rootPermission = new Permission();
+      rootPermission.setId(Permission.ROOT_PARENT_ID);
+      rootPermission.setCode("root");
+      rootPermission.setName("根");
+      
+      permissions.add(rootPermission);
+      rootTreeNodeId = rootPermission.getId();
+
+      List<Permission> applicationPermissions = this.selectApplicationPermissionList();
+      permissions.addAll(applicationPermissions);
+    } else {
+      Permission applicationPermission = permissionRepository.selectById(applicationId); // this.selectApplicationPermissionByCode(applicationCode);
+      if (applicationPermission == null) {
+        return null;
+      }
+      
+      permissions.add(applicationPermission);
+      rootTreeNodeId = applicationPermission.getId();
+    }
+    
+    List<Permission> menuPermissions = permissionRepository.selectList(true, -1, -1, mapBean, null);
+    permissions.addAll(menuPermissions);
+
+    return convertPermissionTree(permissions, rootTreeNodeId);
+  }
+  
+  private PermissionTreeNode convertPermissionTree(List<Permission> permissions, String rootTreeNodeId) {
+    if (permissions == null || permissions.size() == 0) {
+      return null;
+    }
+
+    Map<String, PermissionTreeNode> parentTreeNode = new LinkedHashMap<String, PermissionTreeNode>();
+
+    for (Permission permission : permissions) {
+      PermissionTreeNode treeNode = EntityUtils.copy(permission, new PermissionTreeNode());
+      treeNode.setChildren(new ArrayList<PermissionTreeNode>());
+      
+      if (parentTreeNode.containsKey(treeNode.getId())) {
+        continue;
+      }
+      parentTreeNode.put(treeNode.getId(), treeNode);
+    }
+    
+    for (PermissionTreeNode treeNode : parentTreeNode.values()) {
+      if (!parentTreeNode.containsKey(treeNode.getParentId())) {
+        continue;
+      }
+      
+      parentTreeNode.get(treeNode.getParentId()).getChildren().add(treeNode);
+    }
+    
+    return parentTreeNode.get(rootTreeNodeId);
+  }
+
+  
+
+//  public Permission selectApplicationPermissionByCode(String code) {
+//    Permission probe = new Permission();
+//    probe.setCode(code);
+//    probe.setType("1");
+//    
+//    ExampleMatcher matcher = ExampleMatcher.matching()
+//        .withMatcher("code", ExampleMatcher.GenericPropertyMatchers.exact())
+//        .withMatcher("type", ExampleMatcher.GenericPropertyMatchers.exact());
+//    
+//    Example<Permission> example = Example.of(probe, matcher);
+//    
+//    Optional<Permission> o = permissionRepository.findOne(example);
+//    
+//    if (o.isPresent()) {
+//      return o.get();
+//    }
+//    
+//    return null;
+//  }
+  
+
+  public List<Permission> selectByUsername(String username, String applicationId, String type) {
+    List<Permission> permissions = new ArrayList<Permission>();
+    
+//    Permission applicationPermission = permissionRepository.selectById(applicationId);
+//    if (applicationPermission == null) {
+//      return permissions;
+//    }
+//    
+//    int lft = applicationPermission.getLft();
+//    int rgt = applicationPermission.getRgt();
+    
+    if (StringUtils.isBlank(applicationId)) {
+      List<Permission> accountRolePermissions = permissionRepository.selectAccountRolePermissionByUsername(username, type);
+      permissions.addAll(accountRolePermissions);
+      
+      List<Permission> accountGroupRolePermissions = permissionRepository.selectAccountGroupRolePermissionByUsername(username, type);
+      permissions.addAll(accountGroupRolePermissions);
+    } else {
+      List<Permission> accountRolePermissions = permissionRepository.selectAccountRolePermissionByUsername(username, applicationId, type);
+      permissions.addAll(accountRolePermissions);
+      
+      List<Permission> accountGroupRolePermissions = permissionRepository.selectAccountGroupRolePermissionByUsername(username, applicationId, type);
+      permissions.addAll(accountGroupRolePermissions);
+    }
+    
+    return EntityUtils.distinctList(permissions);
+  }
+  
+  @Deprecated
+  public PermissionTreeNode selectPermissionTreeByUsername(String username, String applicationId, String type) {
+    String rootTreeNodeId = "0";
+    
+    List<Permission> permissions = new ArrayList<Permission>();
+    
+    if (StringUtils.isBlank(applicationId)) {
+      Permission rootPermission = new Permission();
+      rootPermission.setId(Permission.ROOT_PARENT_ID);
+      rootPermission.setCode("root");
+      rootPermission.setName("根");
+      
+      permissions.add(rootPermission);
+      rootTreeNodeId = rootPermission.getId();
+
+      List<Permission> applicationPermissions = this.selectApplicationPermissionList();
+      permissions.addAll(applicationPermissions);
+    } else {
+      Permission applicationPermission = permissionRepository.selectById(applicationId); // this.selectApplicationPermissionByCode(applicationCode);
+      if (applicationPermission == null) {
+        return null;
+      }
+      
+      permissions.add(applicationPermission);
+      rootTreeNodeId = applicationPermission.getId();
+    }
+    
+    List<Permission> menuPermissions = this.selectByUsername(username, applicationId, type);
+    permissions.addAll(menuPermissions);
+
+    return convertPermissionTree(permissions, rootTreeNodeId);
+  }
+
+  public List<PermissionRoleSet> selectPermissionRoleSet(Map<String, Object> mapBean) {
+    
+    List<PermissionRoleSet> prsList = new ArrayList<>();
+    
+    List<Permission> resourceList = permissionRepository.selectList(mapBean, null);
+    
+    for (Permission permission : resourceList) {
+      
+      List<Role> roleList = roleRepository.selectPermissionRolesByPermission(permission.getId());
+      
+      PermissionRoleSet prs = new PermissionRoleSet(permission, roleList);
+      
+      prsList.add(prs);
+    }
+    
+    return prsList;
+  }
+  
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/ResourceService.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/ResourceService.java
new file mode 100644
index 0000000..eea0422
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/ResourceService.java
@@ -0,0 +1,58 @@
+package com.supwisdom.institute.backend.base.domain.service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.supwisdom.institute.backend.base.domain.entity.Resource;
+import com.supwisdom.institute.backend.base.domain.entity.Role;
+import com.supwisdom.institute.backend.base.domain.model.ResourceRoleSet;
+import com.supwisdom.institute.backend.base.domain.repo.ResourceRepository;
+import com.supwisdom.institute.backend.base.domain.repo.RoleRepository;
+import com.supwisdom.institute.backend.common.framework.service.ABaseService;
+
+@Service
+public class ResourceService extends ABaseService<Resource, ResourceRepository> {
+  
+  @Override
+  public ResourceRepository getRepo() {
+    return resourceRepository;
+  }
+
+  @Autowired
+  private ResourceRepository resourceRepository;
+
+  @Autowired
+  private RoleRepository roleRepository;
+
+  
+  public void deleteBatch(List<String> ids) {
+    
+    ids.stream().forEach(id -> {
+      this.deleteById(id);
+    });
+  }
+
+
+  public List<ResourceRoleSet> selectResourceRoleSet(Map<String, Object> mapBean) {
+    
+    List<ResourceRoleSet> rrsList = new ArrayList<>();
+    
+    List<Resource> resourceList = resourceRepository.selectList(mapBean, null);
+    
+    for (Resource resource : resourceList) {
+      
+      List<Role> roleList = roleRepository.selectResourceRolesByResource(resource.getId());
+      
+      ResourceRoleSet rrs = new ResourceRoleSet(resource, roleList);
+      
+      rrsList.add(rrs);
+    }
+    
+    return rrsList;
+  }
+  
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/RoleService.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/RoleService.java
new file mode 100644
index 0000000..c66e3ff
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/RoleService.java
@@ -0,0 +1,268 @@
+package com.supwisdom.institute.backend.base.domain.service;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleMatcher;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Service;
+
+import com.supwisdom.institute.backend.base.domain.entity.AccountRole;
+import com.supwisdom.institute.backend.base.domain.entity.GroupRole;
+import com.supwisdom.institute.backend.base.domain.entity.Role;
+import com.supwisdom.institute.backend.base.domain.entity.RolePermission;
+import com.supwisdom.institute.backend.base.domain.repo.AccountRoleRepository;
+import com.supwisdom.institute.backend.base.domain.repo.GroupRoleRepository;
+import com.supwisdom.institute.backend.base.domain.repo.RolePermissionRepository;
+import com.supwisdom.institute.backend.base.domain.repo.RoleRepository;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.service.ABaseService;
+import com.supwisdom.institute.backend.common.util.MapBeanUtils;
+
+@Service
+public class RoleService extends ABaseService<Role, RoleRepository> {
+  
+  @Override
+  public RoleRepository getRepo() {
+    return roleRepository;
+  }
+
+  @Autowired
+  private RoleRepository roleRepository;
+
+  @Autowired
+  private AccountRoleRepository accountRoleRepository;
+
+  @Autowired
+  private GroupRoleRepository groupRoleRepository;
+
+  @Autowired
+  private RolePermissionRepository rolePermissionRepository;
+
+
+  public void deleteBatch(List<String> ids) {
+    
+    ids.stream().forEach(id -> {
+      this.deleteById(id);
+    });
+  }
+
+  
+  public Page<AccountRole> selectRoleAccounts(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+
+    AccountRole probe = new AccountRole();
+    if (mapBean != null) {
+      probe.setAccountId(MapBeanUtils.getString(mapBean, "accountId"));
+      probe.setRoleId(MapBeanUtils.getString(mapBean, "roleId"));
+    }
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("accountId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("roleId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<AccountRole> example = Example.of(probe, matcher);
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+
+    Page<AccountRole> page = accountRoleRepository.findAll(example, pageRequest); // FIXME: 多表关联查询
+
+    return page;
+  }
+
+  public void relateRoleAccounts(Role role, List<AccountRole> roleAccounts) {
+
+    List<AccountRole> existRoleAccounts = this.selectRoleAccountsByRoleId(role.getId());
+
+    Map<String, AccountRole> existMapRoleAccounts = new LinkedHashMap<String, AccountRole>();
+    for (AccountRole accountRole : existRoleAccounts) {
+      String k = String.format("%s", accountRole.getAccountId());
+      existMapRoleAccounts.put(k, accountRole);
+    }
+
+    for (AccountRole accountRole : roleAccounts) {
+      String k = String.format("%s", accountRole.getAccountId());
+
+      if (existMapRoleAccounts.containsKey(k)) {
+        existMapRoleAccounts.remove(k);
+      } else {
+        accountRole.setCompanyId(role.getCompanyId());
+        accountRole.setRoleId(role.getId());
+
+        accountRoleRepository.insert(accountRole);
+      }
+    }
+
+    for (AccountRole accountRole : existMapRoleAccounts.values()) {
+      accountRoleRepository.deleteById(accountRole.getId());
+    }
+  }
+
+  public List<AccountRole> selectRoleAccountsByRoleId(String roleId) {
+
+    AccountRole probe = new AccountRole();
+    probe.setRoleId(roleId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("roleId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<AccountRole> example = Example.of(probe, matcher);
+
+    List<AccountRole> accountRoles = accountRoleRepository.findAll(example);
+
+    return accountRoles;
+  }
+  
+  
+
+  public Page<GroupRole> selectRoleGroups(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+
+    GroupRole probe = new GroupRole();
+    if (mapBean != null) {
+      probe.setGroupId(MapBeanUtils.getString(mapBean, "groupId"));
+      probe.setRoleId(MapBeanUtils.getString(mapBean, "roleId"));
+    }
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("groupId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("roleId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<GroupRole> example = Example.of(probe, matcher);
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+
+    Page<GroupRole> page = groupRoleRepository.findAll(example, pageRequest);  // FIXME: 多表关联查询
+
+    return page;
+  }
+
+  public void relateRoleGroups(Role role, List<GroupRole> groupRoles) {
+
+    List<GroupRole> existRoleGroups = this.selectRoleGroupsByRoleId(role.getCode());
+
+    Map<String, GroupRole> existMapRoleGroups = new LinkedHashMap<String, GroupRole>();
+    for (GroupRole groupRole : existRoleGroups) {
+      String k = String.format("%s", groupRole.getGroupId());
+      existMapRoleGroups.put(k, groupRole);
+    }
+
+    for (GroupRole groupRole : groupRoles) {
+      String k = String.format("%s", groupRole.getGroupId());
+
+      if (existMapRoleGroups.containsKey(k)) {
+        existMapRoleGroups.remove(k);
+      } else {
+        groupRole.setCompanyId(role.getCompanyId());
+        groupRole.setRoleId(role.getId());
+
+        groupRoleRepository.insert(groupRole);
+      }
+    }
+
+    for (GroupRole groupRole : existMapRoleGroups.values()) {
+      groupRoleRepository.deleteById(groupRole.getId());
+    }
+  }
+
+  public List<GroupRole> selectRoleGroupsByRoleId(String roleId) {
+
+    GroupRole probe = new GroupRole();
+    probe.setRoleId(roleId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("roleId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<GroupRole> example = Example.of(probe, matcher);
+
+    List<GroupRole> groupRoles = groupRoleRepository.findAll(example);
+
+    return groupRoles;
+  }
+
+
+  public Page<RolePermission> selectRolePermissions(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+
+    RolePermission probe = new RolePermission();
+    if (mapBean != null) {
+      probe.setRoleId(MapBeanUtils.getString(mapBean, "roleId"));
+      probe.setPermissionId(MapBeanUtils.getString(mapBean, "permissionId"));
+    }
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("roleId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("permissionId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<RolePermission> example = Example.of(probe, matcher);
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+
+    Page<RolePermission> page = rolePermissionRepository.findAll(example, pageRequest); // FIXME: 多表关联查询
+
+    return page;
+  }
+
+  public void relateRolePermissions(Role role, List<RolePermission> rolePermissions) {
+
+    List<RolePermission> existRolePermissions = this.selectRolePermissionsByRoleId(role.getId());
+
+    Map<String, RolePermission> existMapRolePermissions = new LinkedHashMap<String, RolePermission>();
+    for (RolePermission rolePermission : existRolePermissions) {
+      String k = String.format("%s", rolePermission.getPermissionId());
+      existMapRolePermissions.put(k, rolePermission);
+    }
+
+    for (RolePermission rolePermission : rolePermissions) {
+      String k = String.format("%s", rolePermission.getPermissionId());
+
+      if (existMapRolePermissions.containsKey(k)) {
+        existMapRolePermissions.remove(k);
+      } else {
+        rolePermission.setCompanyId(role.getCompanyId());
+        rolePermission.setRoleId(role.getId());
+
+        rolePermissionRepository.insert(rolePermission);
+      }
+    }
+
+    for (RolePermission rolePermission : existMapRolePermissions.values()) {
+      rolePermissionRepository.deleteById(rolePermission.getId());
+    }
+  }
+
+  public List<RolePermission> selectRolePermissionsByRoleId(String roleId) {
+
+    RolePermission probe = new RolePermission();
+    probe.setRoleId(roleId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("roleId",
+        ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<RolePermission> example = Example.of(probe, matcher);
+
+    List<RolePermission> rolePermissions = rolePermissionRepository.findAll(example);
+
+    return rolePermissions;
+  }
+
+  
+  
+
+  public List<Role> selectByUsername(String username) {
+    List<Role> roles = new ArrayList<Role>();
+    
+    List<Role> userRoles = roleRepository.selectAccountRoleByUsername(username);
+    roles.addAll(userRoles);
+    
+    List<Role> userGroupRoles = roleRepository.selectAccountGroupRoleByUsername(username);
+    roles.addAll(userGroupRoles);
+    
+    roles = EntityUtils.distinctList(roles);
+    
+    return roles;
+  }
+  
+}
diff --git a/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/RouteService.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/RouteService.java
new file mode 100644
index 0000000..f93595f
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/RouteService.java
@@ -0,0 +1,32 @@
+package com.supwisdom.institute.backend.base.domain.service;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.supwisdom.institute.backend.base.domain.entity.Route;
+import com.supwisdom.institute.backend.base.domain.repo.RouteRepository;
+import com.supwisdom.institute.backend.common.framework.service.ABaseService;
+
+@Service
+public class RouteService extends ABaseService<Route, RouteRepository> {
+
+  @Override
+  public RouteRepository getRepo() {
+    return routeRepository;
+  }
+  
+  @Autowired
+  private RouteRepository routeRepository;
+
+
+  public void deleteBatch(List<String> ids) {
+    
+    ids.stream().forEach(id -> {
+      this.deleteById(id);
+    });
+  }
+
+  
+}
diff --git a/base/pom.xml b/base/pom.xml
new file mode 100644
index 0000000..66b99b3
--- /dev/null
+++ b/base/pom.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  
+  <parent>
+    <groupId>com.supwisdom.institute</groupId>
+    <artifactId>sw-backend-parent</artifactId>
+    <version>0.0.1</version>
+  </parent>
+  
+  <groupId>com.supwisdom.institute</groupId>
+  <artifactId>sw-backend-base-aggregator</artifactId>
+  <version>0.0.1</version>
+  <packaging>pom</packaging>
+
+  <name>Supwisdom Backend Framework Base Aggregator</name>
+  <description>Supwisdom Backend Framework Base Aggregator project</description>
+
+  <modules>
+    <module>domain</module>
+    <module>api</module>
+  </modules>
+
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-deploy-plugin</artifactId>
+        <configuration>
+          <skip>true</skip>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/bff/admin/.gitignore b/bff/admin/.gitignore
new file mode 100644
index 0000000..b83d222
--- /dev/null
+++ b/bff/admin/.gitignore
@@ -0,0 +1 @@
+/target/
diff --git a/bff/admin/Dockerfile b/bff/admin/Dockerfile
new file mode 100644
index 0000000..b91c9f4
--- /dev/null
+++ b/bff/admin/Dockerfile
@@ -0,0 +1,21 @@
+FROM harbor.supwisdom.com/institute/openjdk:8-jre-alpine
+
+ENV ENABLE_JMX_SSL=false
+ENV JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -Dspring.profiles.active=docker
+ENV SPRING_PROFILES_ACTIVE=docker
+
+ARG NAME
+ARG VERSION
+ARG JAR_FILE
+
+LABEL name=$NAME \
+      version=$VERSION
+
+EXPOSE 8080
+
+EXPOSE 8443
+
+COPY --chown=java-app:java-app target/${JAR_FILE} /home/java-app/lib/app.jar
+
+# COPY --chown=java-app:java-app target/doc /home/java-app/doc
+# COPY --chown=java-app:java-app target/api-docs /home/java-app/api-docs
diff --git a/bff/admin/pom.xml b/bff/admin/pom.xml
new file mode 100644
index 0000000..c6ce4cb
--- /dev/null
+++ b/bff/admin/pom.xml
@@ -0,0 +1,223 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.supwisdom.institute</groupId>
+    <artifactId>sw-backend-parent</artifactId>
+    <version>0.0.1</version>
+    <relativePath>../../</relativePath>
+  </parent>
+
+  <groupId>com.supwisdom.institute</groupId>
+  <artifactId>sw-backend-admin-bff</artifactId>
+  <version>0.0.1</version>
+  <packaging>jar</packaging>
+
+  <name>Supwisdom Backend Framework Admin Backend for Frontend</name>
+  <description>Supwisdom Backend Framework Admin Backend for Frontend project</description>
+
+  <properties>
+    <start-class>com.supwisdom.institute.backend.admin.bff.Application</start-class>
+  </properties>
+
+  <dependencies>
+
+    <dependency>
+      <groupId>org.projectlombok</groupId>
+      <artifactId>lombok</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter</artifactId>
+    </dependency>
+
+    <!-- 微服务 健康监控 -->
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-actuator</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-web</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>org.springframework.cloud</groupId>
+      <artifactId>spring-cloud-starter-openfeign</artifactId>
+    </dependency>
+    <!-- openfeign's dependency -->
+    <dependency>
+      <groupId>com.netflix.feign</groupId>
+      <artifactId>feign-httpclient</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpclient</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpcore</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-online-doc</artifactId>
+    </dependency>
+
+
+    <!-- <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-mvc</artifactId>
+    </dependency> -->
+
+    <!-- <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-object-mapper</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-i18n</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-lang</artifactId>
+    </dependency> -->
+
+
+    <dependency>
+      <groupId>com.supwisdom.institute</groupId>
+      <artifactId>sw-backend-common-framework</artifactId>
+    </dependency>
+    
+    
+    <dependency>
+      <groupId>io.springfox</groupId>
+      <artifactId>springfox-swagger2</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.springfox</groupId>
+      <artifactId>springfox-swagger-ui</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>mysql</groupId>
+      <artifactId>mysql-connector-java</artifactId>
+      <scope>runtime</scope>
+    </dependency>
+
+    <!-- 热部署,无需重启项目 -->
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-devtools</artifactId>
+      <scope>runtime</scope>
+    </dependency>
+
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+  </dependencies>
+
+  <build>
+    <finalName>${project.artifactId}</finalName>
+
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-failsafe-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-release-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+      </plugin>
+
+
+      <!-- <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-resources-plugin</artifactId>
+        <version>2.4.3</version>
+        <configuration>
+          <encoding>${project.build.sourceEncoding}</encoding>
+        </configuration>
+        <executions>
+          <execution>
+            <id>copy-doc-resources</id>
+            <phase>package</phase>
+            <goals>
+              <goal>copy-resources</goal>
+            </goals>
+            <configuration>
+              <encoding>utf-8</encoding>
+              <outputDirectory>${basedir}/target/doc</outputDirectory>
+              <overwrite>true</overwrite>
+              <resources>
+                <resource>
+                  <directory>${basedir}/../doc</directory>
+                </resource>
+              </resources>
+            </configuration>
+          </execution>
+          <execution>
+            <id>copy-api-docs-resources</id>
+            <phase>package</phase>
+            <goals>
+              <goal>copy-resources</goal>
+            </goals>
+            <configuration>
+              <encoding>utf-8</encoding>
+              <outputDirectory>${basedir}/target/api-docs</outputDirectory>
+              <overwrite>true</overwrite>
+              <resources>
+                <resource>
+                  <directory>${basedir}/../api-docs</directory>
+                </resource>
+              </resources>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin> -->
+
+
+      <plugin>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-maven-plugin</artifactId>
+      </plugin>
+
+      <plugin>
+        <groupId>com.spotify</groupId>
+        <artifactId>dockerfile-maven-plugin</artifactId>
+        <configuration>
+          <skip>false</skip>
+        </configuration>
+      </plugin>
+
+    </plugins>
+
+  </build>
+
+</project>
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/Application.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/Application.java
new file mode 100644
index 0000000..64ea622
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/Application.java
@@ -0,0 +1,28 @@
+package com.supwisdom.institute.backend.admin.bff;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+import com.supwisdom.infras.online.doc.configuration.EnableInfrasOnlineDoc;
+import com.supwisdom.institute.backend.common.core.transmit.annotation.EnableSimpleUserTransmit;
+import com.supwisdom.institute.backend.common.framework.exception.EnableCustomExceptionHandler;
+
+@SpringBootApplication
+@EnableFeignClients
+
+@EnableScheduling
+
+@EnableSimpleUserTransmit
+
+@EnableCustomExceptionHandler
+
+@EnableInfrasOnlineDoc
+public class Application {
+
+  public static void main(String[] args) {
+    SpringApplication.run(Application.class, args);
+  }
+  
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/autorefesh/base/InMemeryPermissionRoleSetAutoRefresh.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/autorefesh/base/InMemeryPermissionRoleSetAutoRefresh.java
new file mode 100644
index 0000000..d149eee
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/autorefesh/base/InMemeryPermissionRoleSetAutoRefresh.java
@@ -0,0 +1,26 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.autorefesh.base;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import com.supwisdom.institute.backend.admin.bff.api.v1.service.base.AuthnService;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Component
+public class InMemeryPermissionRoleSetAutoRefresh {
+
+  @Autowired
+  private AuthnService authnService;
+  
+  @Scheduled(initialDelayString = "${inMemeryPermissionRoleSetAutoRefresh.schedule.startDelay:500}",
+      fixedDelayString = "${inMemeryPermissionRoleSetAutoRefresh.schedule.repeatInterval:20000}")
+  public void refresh() {
+    log.info("InMemeryPermissionRoleSetAutoRefresh, refresh permissionRoleSets");
+    
+    authnService.loadPermissionRoleSets();
+  }
+
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/controller/.gitkeep b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/controller/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/controller/.gitkeep
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/controller/base/AdminBaseController.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/controller/base/AdminBaseController.java
new file mode 100644
index 0000000..6224970
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/controller/base/AdminBaseController.java
@@ -0,0 +1,19 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.controller.base;
+
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.supwisdom.institute.backend.common.core.transmit.user.UserContext;
+
+@RestController
+@RequestMapping(value = "/v1/admin/base")
+public class AdminBaseController {
+  
+  @RequestMapping(method = RequestMethod.GET, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  public String hello() {
+    return "hello, " + UserContext.getUsername();
+  }
+
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/controller/biz/AdminBizController.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/controller/biz/AdminBizController.java
new file mode 100644
index 0000000..8c46261
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/controller/biz/AdminBizController.java
@@ -0,0 +1,26 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.controller.biz;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.supwisdom.institute.backend.admin.bff.api.v1.service.biz.BizService;
+
+@RestController
+@RequestMapping(value = "/v1/admin/biz")
+public class AdminBizController {
+  
+  @Autowired
+  BizService bizService;
+
+  @RequestMapping(method = RequestMethod.GET, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  public String biz() {
+    
+    bizService.query(true, -1, -1);
+    
+    return "biz";
+  }
+
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/controller/me/MeController.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/controller/me/MeController.java
new file mode 100644
index 0000000..c88e7c5
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/controller/me/MeController.java
@@ -0,0 +1,79 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.controller.me;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.supwisdom.institute.backend.admin.bff.api.v1.model.base.Permission;
+import com.supwisdom.institute.backend.admin.bff.api.v1.model.me.CurrentUser;
+import com.supwisdom.institute.backend.admin.bff.api.v1.model.me.GrantedMenu;
+import com.supwisdom.institute.backend.admin.bff.api.v1.service.base.AuthnService;
+import com.supwisdom.institute.backend.admin.bff.api.v1.vo.me.response.data.CurrentUserResponseData;
+import com.supwisdom.institute.backend.admin.bff.api.v1.vo.me.response.data.GrantedMenusResponseData;
+import com.supwisdom.institute.backend.admin.bff.utils.CurrentUserUtil;
+import com.supwisdom.institute.backend.common.core.transmit.user.User;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.DefaultApiResponse;
+
+
+@Api(value = "BFFMe", tags = { "me" }, description = "登录用户相关信息的接口")
+@RestController
+@RequestMapping(value = "/v1/me")
+public class MeController {
+  
+  @Autowired
+  private AuthnService authnService;
+  
+  @ApiOperation(
+      tags = { "me" },
+      value = "获取登录用户的基本信息", notes = "获取登录用户的基本信息", nickname = "user"
+  )
+  @RequestMapping(method = RequestMethod.GET, path = "/user", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  public DefaultApiResponse<CurrentUserResponseData> user() {
+    
+    User user = CurrentUserUtil.currentUser();
+    
+    CurrentUser cu = new CurrentUser();
+    cu.setUsername(user.getUsername());
+    cu.setRoles(user.getRoles());
+    cu.setAttributes(user.getAttributes());
+    
+    // XXX: 如果需要放入其他信息,可以扩展CurrentUser,并从服务接口中获取后写入数据
+    
+    CurrentUserResponseData data = CurrentUserResponseData.of(cu);
+    return new DefaultApiResponse<CurrentUserResponseData>(data);
+  }
+  
+  @ApiOperation(
+      tags = { "me" },
+      value = "获取登录用户的访问菜单", notes = "获取登录用户的访问菜单", nickname = "accountMenus"
+  )
+  @RequestMapping(method = RequestMethod.GET, path = "/menus", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  public DefaultApiResponse<GrantedMenusResponseData> accountMenus() {
+    
+    String username = CurrentUserUtil.currentUsername();
+    
+    List<Permission> menus =  authnService.menus(username, "1");  // XXX: applicationId
+    
+    List<GrantedMenu> grantedMenus = new ArrayList<>();
+    
+    menus.forEach(m -> {
+      GrantedMenu grantedMenu = new GrantedMenu();
+      grantedMenu = EntityUtils.copy(m, grantedMenu);
+      grantedMenus.add(grantedMenu);
+    });
+    
+    GrantedMenusResponseData data = GrantedMenusResponseData.of(grantedMenus);
+    return new DefaultApiResponse<GrantedMenusResponseData>(data);
+    
+  }
+  
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/controller/open/OpenController.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/controller/open/OpenController.java
new file mode 100644
index 0000000..1b04e65
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/controller/open/OpenController.java
@@ -0,0 +1,53 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.controller.open;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.supwisdom.institute.backend.admin.bff.api.v1.model.base.PermissionRoleSet;
+import com.supwisdom.institute.backend.admin.bff.api.v1.model.open.Menu;
+import com.supwisdom.institute.backend.admin.bff.api.v1.service.base.AuthnService;
+import com.supwisdom.institute.backend.admin.bff.api.v1.vo.open.response.data.MenusResponseData;
+import com.supwisdom.institute.backend.common.framework.vo.response.DefaultApiResponse;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+@Api(value = "BFFOpen", tags = { "open" }, description = "公开接口")
+@RestController
+@RequestMapping(value = "/v1/open")
+public class OpenController {
+  
+  @Autowired
+  private AuthnService authnService;
+  
+  @ApiOperation(
+      tags = { "open" },
+      value = "获取菜单", notes = "获取菜单", nickname = "openMenus"
+  )
+  @RequestMapping(method = RequestMethod.GET, path = "/menus", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  public DefaultApiResponse<MenusResponseData> menus() {
+    
+    List<Menu> menus = new ArrayList<>();
+
+    List<PermissionRoleSet> permissionRoleSets = authnService.getPermissionRoleSets("1"); // XXX: applicationId
+    if (permissionRoleSets != null) {
+      for (PermissionRoleSet permissionRoleSet : permissionRoleSets) {
+        Menu menu = new Menu();
+        BeanUtils.copyProperties(permissionRoleSet, menu);
+        menus.add(menu);
+      }
+    }
+    
+    MenusResponseData data = MenusResponseData.of(menus);
+    return new DefaultApiResponse<MenusResponseData>(data);
+  }
+  
+
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/controller/open/OpenSyncController.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/controller/open/OpenSyncController.java
new file mode 100644
index 0000000..ab6da53
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/controller/open/OpenSyncController.java
@@ -0,0 +1,55 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.controller.open;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.supwisdom.institute.backend.admin.bff.api.v1.model.base.Role;
+import com.supwisdom.institute.backend.admin.bff.api.v1.model.open.SyncRoleModel;
+import com.supwisdom.institute.backend.admin.bff.api.v1.service.base.AuthnService;
+import com.supwisdom.institute.backend.admin.bff.api.v1.vo.open.response.data.OpenSyncRolesResponseData;
+import com.supwisdom.institute.backend.common.framework.vo.response.DefaultApiResponse;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+@Api(value = "BFFOpen", tags = { "open" }, description = "公开接口")
+@RestController
+@RequestMapping(value = "/v1/open/sync")
+public class OpenSyncController {
+
+  @Autowired
+  private AuthnService authnService;
+
+  @ApiOperation(
+      tags = { "open" },
+      value = "获取角色", notes = "获取角色", nickname = "openRoles"
+  )
+  @RequestMapping(method = RequestMethod.GET, path = "/roles", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  public DefaultApiResponse<OpenSyncRolesResponseData> roles(
+      @RequestParam(name = "applicationId", required = true) String applicationId) {
+    
+    List<SyncRoleModel> syncRoleModels = new ArrayList<>();
+
+    List<Role> systemRoles = authnService.roles();
+    for (Role systemRole : systemRoles) {
+      SyncRoleModel syncRoleModel = new SyncRoleModel(
+          systemRole.getId(), 
+          systemRole.getCode(),
+          systemRole.getName(),
+          systemRole.getMemo());
+      syncRoleModels.add(syncRoleModel);
+    }
+    
+    OpenSyncRolesResponseData data = OpenSyncRolesResponseData.of(applicationId, syncRoleModels);
+    return new DefaultApiResponse<OpenSyncRolesResponseData>(data);
+  }
+  
+
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/.gitkeep b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/.gitkeep
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/base/Account.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/base/Account.java
new file mode 100644
index 0000000..ebb0513
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/base/Account.java
@@ -0,0 +1,70 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.model.base;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.model.ABaseModel;
+
+@Getter
+@Setter
+public class Account extends ABaseModel {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -4889952442290543101L;
+
+  private String id;
+
+  /**
+   * 用户名
+   */
+  private String username;
+  
+  /**
+   * 密码
+   */
+  private String password;
+
+  /**
+   * 是否可用,1 可用,0 不可用,默认:1
+   */
+  private Boolean enabled;
+  /**
+   * 账号未过期,1 未过期,0 过期,默认:1
+   */
+  private Boolean accountNonExpired;
+  /**
+   * 账号未锁定,1 未锁定,0 锁定,默认:1
+   */
+  private Boolean accountNonLocked;
+  /**
+   * 密码未过期,1 未过期,0 过期,默认:1
+   */
+  private Boolean credentialsNonExpired;
+
+  /**
+   * 姓名
+   */
+  private String name;
+
+  /**
+   * 备注
+   */
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  private String status;
+
+  /**
+   * 登录手机
+   */
+  private String mobile;
+  /**
+   * 登录邮箱
+   */
+  private String email;
+
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/base/Application.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/base/Application.java
new file mode 100644
index 0000000..b0f6c48
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/base/Application.java
@@ -0,0 +1,39 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.model.base;
+
+import com.supwisdom.institute.backend.common.framework.model.ABaseModel;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class Application extends ABaseModel {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 7687658763529677076L;
+
+  private String id;
+  
+  /**
+   * 代码
+   */
+  private String code;
+
+  /**
+   * 名称
+   */
+  private String name;
+
+  /**
+   * 备注
+   */
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  private String status;
+
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/base/Permission.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/base/Permission.java
new file mode 100644
index 0000000..5877bf9
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/base/Permission.java
@@ -0,0 +1,69 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.model.base;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.model.ABaseModel;
+
+@Getter
+@Setter
+public class Permission extends ABaseModel {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -3042842657207449148L;
+
+  private String id;
+
+  /**
+   * 代码
+   */
+  private String code;
+
+  /**
+   * 名称
+   */
+  private String name;
+
+  /**
+   * 备注
+   */
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  private String status;
+
+  /**
+   * 菜单图标
+   */
+  private String icon;
+
+  /**
+   * 类型(1 应用,2 菜单,3 操作)
+   */
+  private String type;
+
+  /**
+   * URL地址
+   */
+  private String url;
+
+  /**
+   * 系统ID
+   */
+  private String applicationId;
+
+  /**
+   * 父级ID
+   */
+  private String parentId;
+
+  /**
+   * 排序
+   */
+  private Integer order;
+
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/base/PermissionRoleSet.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/base/PermissionRoleSet.java
new file mode 100644
index 0000000..6080107
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/base/PermissionRoleSet.java
@@ -0,0 +1,114 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.model.base;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+
+import com.supwisdom.institute.backend.common.framework.model.ABaseModel;
+
+import lombok.Getter;
+import lombok.Setter;
+
+public class PermissionRoleSet extends ABaseModel {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -1482739465611090145L;
+  
+  @Getter
+  @Setter
+  private String id;
+
+  /**
+   * 代码
+   */
+  @Getter
+  @Setter
+  private String code;
+
+  /**
+   * 名称
+   */
+  @Getter
+  @Setter
+  private String name;
+
+  /**
+   * 备注
+   */
+  @Getter
+  @Setter
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  @Getter
+  @Setter
+  private String status;
+
+  /**
+   * 类型(1 应用,2 菜单,3 操作)
+   */
+  @Getter
+  @Setter
+  private String type;
+
+  /**
+   * 菜单图标
+   */
+  @Getter
+  @Setter
+  private String icon;
+
+  /**
+   * URL地址
+   */
+  @Getter
+  @Setter
+  private String url;
+
+  /**
+   * 系统ID
+   */
+  @Getter
+  @Setter
+  private String applicationId;
+
+  /**
+   * 父级ID
+   */
+  @Getter
+  @Setter
+  private String parentId;
+
+  /**
+   * 排序
+   */
+  @Getter
+  @Setter
+  private Integer order;
+
+  @Getter
+  @Setter
+  private Collection<Role> roles;
+  
+  private Collection<String> permissionRoles = null;
+  public boolean matches(Collection<String> userRoles) {
+    if (permissionRoles == null) {
+      permissionRoles = new ArrayList<String>();
+      Iterator<Role> it = roles.iterator();
+      while (it.hasNext()) {
+        Role r = it.next();
+        permissionRoles.add(r.getCode());
+      }
+    }
+    
+    userRoles.retainAll(permissionRoles);
+    
+    return !userRoles.isEmpty();
+  }
+  
+  
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/base/ResourceRoleSet.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/base/ResourceRoleSet.java
new file mode 100644
index 0000000..665154d
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/base/ResourceRoleSet.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.model.base;
+
+import java.util.Collection;
+
+import com.supwisdom.institute.backend.common.framework.model.ABaseModel;
+
+import lombok.Getter;
+import lombok.Setter;
+
+public class ResourceRoleSet extends ABaseModel {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -683204173918706673L;
+
+  public static final String ACCESS_ANONYMOUS = "anonymous"; // 匿名访问anonymous
+  public static final String ACCESS_AUTHENTICATE = "authenticate"; // 认证访问authenticate
+  public static final String ACCESS_AUTHORIZE = "authorize"; // 授权访问authorize
+  public static final String ACCESS_PERMIT_ALL = "permitAll"; // 允许所有permitAll
+  public static final String ACCESS_DENY_ALL = "denyAll"; // 拒绝所有denyAll
+
+  @Getter
+  @Setter
+  private String id;
+
+  /**
+   * 代码
+   */
+  @Getter
+  @Setter
+  private String code;
+
+  /**
+   * 名称
+   */
+  @Getter
+  @Setter
+  private String name;
+
+  /**
+   * 备注
+   */
+  @Getter
+  @Setter
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  @Getter
+  @Setter
+  private String status;
+
+  /**
+   * 请求方式(GET、POST、PUT、DELETE 等)
+   */
+  @Getter
+  @Setter
+  private String method;
+
+  /**
+   * 请求路径
+   */
+  @Getter
+  @Setter
+  private String path;
+
+  /**
+   * 访问规则(匿名访问anonymous、认证访问authenticate、授权访问authorize、允许所有permitAll、拒绝所有denyAll)
+   */
+  @Getter
+  @Setter
+  private String access;
+
+  @Getter
+  @Setter
+  Collection<Role> roles;
+  
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/base/Role.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/base/Role.java
new file mode 100644
index 0000000..d8c5144
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/base/Role.java
@@ -0,0 +1,39 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.model.base;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.model.ABaseModel;
+
+@Getter
+@Setter
+public class Role extends ABaseModel {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -8551951601186240995L;
+
+  private String id;
+
+  /**
+   * 代码
+   */
+  private String code;
+
+  /**
+   * 名称
+   */
+  private String name;
+
+  /**
+   * 备注
+   */
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  private String status;
+
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/biz/Biz.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/biz/Biz.java
new file mode 100644
index 0000000..a290125
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/biz/Biz.java
@@ -0,0 +1,37 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.model.biz;
+
+import java.util.Date;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.model.ABaseModel;
+
+public class Biz extends ABaseModel {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 8755876583168251137L;
+  
+  @Getter
+  @Setter
+  private String id;
+
+  @Getter
+  @Setter
+  private String name;
+  
+  @Getter
+  @Setter
+  private Boolean bool;
+  
+  @Getter
+  @Setter
+  private Date date;
+
+  @Getter
+  @Setter
+  private Integer num;
+
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/me/CurrentUser.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/me/CurrentUser.java
new file mode 100644
index 0000000..3563b30
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/me/CurrentUser.java
@@ -0,0 +1,30 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.model.me;
+
+import java.util.List;
+import java.util.Map;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.model.ABaseModel;
+
+public class CurrentUser extends ABaseModel {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 2049212615500816301L;
+  
+  @Getter
+  @Setter
+  private String username;
+  
+  @Getter
+  @Setter
+  private List<String> roles;
+  
+  @Getter
+  @Setter
+  private Map<String, Object> attributes;
+
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/me/GrantedMenu.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/me/GrantedMenu.java
new file mode 100644
index 0000000..5d8cd70
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/me/GrantedMenu.java
@@ -0,0 +1,64 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.model.me;
+
+import com.supwisdom.institute.backend.common.framework.model.ABaseModel;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class GrantedMenu extends ABaseModel {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -1261755008133562980L;
+
+  private String id;
+
+  /**
+   * 代码
+   */
+  private String code;
+
+  /**
+   * 名称
+   */
+  private String name;
+
+  /**
+   * 备注
+   */
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  private String status;
+
+  /**
+   * 菜单图标
+   */
+  private String icon;
+
+  /**
+   * URL地址
+   */
+  private String url;
+
+  /**
+   * 系统ID
+   */
+  private String applicationId;
+
+  /**
+   * 父级ID
+   */
+  private String parentId;
+
+  /**
+   * 排序
+   */
+  private Integer order;
+
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/open/Menu.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/open/Menu.java
new file mode 100644
index 0000000..b3bdc99
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/open/Menu.java
@@ -0,0 +1,64 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.model.open;
+
+import com.supwisdom.institute.backend.common.framework.model.ABaseModel;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class Menu extends ABaseModel {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 4206010261706882919L;
+  
+  private String id;
+
+  /**
+   * 代码
+   */
+  private String code;
+
+  /**
+   * 名称
+   */
+  private String name;
+
+  /**
+   * 备注
+   */
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  private String status;
+
+  /**
+   * 菜单图标
+   */
+  private String icon;
+
+  /**
+   * URL地址
+   */
+  private String url;
+
+  /**
+   * 系统ID
+   */
+  private String applicationId;
+
+  /**
+   * 父级ID
+   */
+  private String parentId;
+
+  /**
+   * 排序
+   */
+  private Integer order;
+
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/open/SyncRoleModel.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/open/SyncRoleModel.java
new file mode 100644
index 0000000..72986bc
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/model/open/SyncRoleModel.java
@@ -0,0 +1,39 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.model.open;
+
+import java.io.Serializable;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@AllArgsConstructor
+public class SyncRoleModel implements Serializable {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -1916611618482825238L;
+  
+  /**
+   * ID
+   */
+  private String id;
+
+  /**
+   * 代码
+   */
+  private String code;
+
+  /**
+   * 名称
+   */
+  private String name;
+
+  /**
+   * 描述
+   */
+  private String memo;
+
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/remote/.gitkeep b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/remote/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/remote/.gitkeep
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/remote/base/configuration/BaseFeignClientConfiguration.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/remote/base/configuration/BaseFeignClientConfiguration.java
new file mode 100644
index 0000000..da7040b
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/remote/base/configuration/BaseFeignClientConfiguration.java
@@ -0,0 +1,36 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.remote.base.configuration;
+
+import org.apache.http.conn.HttpClientConnectionManager;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
+import org.springframework.context.annotation.Bean;
+
+import com.supwisdom.institute.backend.common.core.feign.FeignClientBuilder;
+
+import feign.Client;
+
+public class BaseFeignClientConfiguration {
+
+  @Bean
+  public Client feignClient(
+      @Value("${sw-backend-base-api.client-auth.enabled:false}") boolean enabled,
+      @Value("${sw-backend-base-api.client-auth.key-password:}") String keyPassword,
+      @Value("${sw-backend-base-api.client-auth.key-store:}") String keyStore,
+      @Value("${sw-backend-base-api.client-auth.key-store-password:}") String keyStorePassword,
+      @Value("${sw-backend-base-api.client-auth.trust-store:}") String trustStore,
+      @Value("${sw-backend-base-api.client-auth.trust-store-password:}") String trustStorePassword,
+      ApacheHttpClientFactory httpClientFactory,
+      HttpClientConnectionManager httpClientConnectionManager) {
+    
+    return FeignClientBuilder.builder()
+        .enabled(enabled)
+        .keyPassword(keyPassword)
+        .keyStore(keyStore)
+        .keyStorePassword(keyStorePassword)
+        .trustStore(trustStore)
+        .trustStorePassword(trustStorePassword)
+        .build()
+        .apacheHttpClient(httpClientFactory, httpClientConnectionManager);
+  }
+
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/remote/base/v1/authn/AuthnRemoteFallbackFactory.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/remote/base/v1/authn/AuthnRemoteFallbackFactory.java
new file mode 100644
index 0000000..e965dab
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/remote/base/v1/authn/AuthnRemoteFallbackFactory.java
@@ -0,0 +1,101 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.remote.base.v1.authn;
+
+import org.springframework.stereotype.Component;
+
+import com.alibaba.fastjson.JSONObject;
+import com.supwisdom.institute.backend.admin.bff.api.v1.remote.exception.FallbackError;
+
+import feign.hystrix.FallbackFactory;
+
+@Component
+public class AuthnRemoteFallbackFactory implements FallbackFactory<AuthnRemoteFeignClient> {
+
+  @Override
+  public AuthnRemoteFeignClient create(Throwable cause) {
+    return new AuthnRemoteFeignClient() {
+
+      @Override
+      public JSONObject account(String username) {
+        if (cause != null) {
+          cause.printStackTrace();
+        }
+        return FallbackError.defaultErrorJson(cause);
+      }
+
+      @Override
+      public JSONObject roles(String username) {
+        if (cause != null) {
+          cause.printStackTrace();
+        }
+        return FallbackError.defaultErrorJson(cause);
+      }
+
+      @Override
+      public JSONObject applications(String username, String applicationId) {
+        if (cause != null) {
+          cause.printStackTrace();
+        }
+        return FallbackError.defaultErrorJson(cause);
+      }
+
+      @Override
+      public JSONObject menus(String username, String applicationId) {
+        if (cause != null) {
+          cause.printStackTrace();
+        }
+        return FallbackError.defaultErrorJson(cause);
+      }
+
+      @Override
+      public JSONObject operations(String username, String applicationId) {
+        if (cause != null) {
+          cause.printStackTrace();
+        }
+        return FallbackError.defaultErrorJson(cause);
+      }
+
+      @Override
+      public JSONObject resources(String username, String applicationId) {
+        if (cause != null) {
+          cause.printStackTrace();
+        }
+        return FallbackError.defaultErrorJson(cause);
+      }
+      
+
+      @Override
+      public JSONObject applications() {
+        if (cause != null) {
+          cause.printStackTrace();
+        }
+        return FallbackError.defaultErrorJson(cause);
+      }
+
+      @Override
+      public JSONObject roles() {
+        if (cause != null) {
+          cause.printStackTrace();
+        }
+        return FallbackError.defaultErrorJson(cause);
+      }
+
+      @Override
+      public JSONObject permissionRoleSets(String applicationId) {
+        if (cause != null) {
+          cause.printStackTrace();
+        }
+        return FallbackError.defaultErrorJson(cause);
+      }
+
+      @Override
+      public JSONObject resourceRoleSets(String applicationId) {
+        if (cause != null) {
+          cause.printStackTrace();
+        }
+        return FallbackError.defaultErrorJson(cause);
+      }
+      
+    };
+  }
+
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/remote/base/v1/authn/AuthnRemoteFeignClient.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/remote/base/v1/authn/AuthnRemoteFeignClient.java
new file mode 100644
index 0000000..0486ffd
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/remote/base/v1/authn/AuthnRemoteFeignClient.java
@@ -0,0 +1,64 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.remote.base.v1.authn;
+
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import com.alibaba.fastjson.JSONObject;
+import com.supwisdom.institute.backend.admin.bff.api.v1.remote.base.configuration.BaseFeignClientConfiguration;
+
+@FeignClient(
+    configuration = {BaseFeignClientConfiguration.class},
+    name = "base-admin-account-remote-feign-client",
+    url = "${sw-backend-base-api.uri}/v1/authn",
+    fallbackFactory = AuthnRemoteFallbackFactory.class
+)
+public interface AuthnRemoteFeignClient {
+  
+  @RequestMapping(method = RequestMethod.GET, path = "/{username}/account")
+  JSONObject account(
+      @PathVariable(name = "username") String username);
+
+  @RequestMapping(method = RequestMethod.GET, path = "/{username}/roles")
+  JSONObject roles(
+      @PathVariable(name = "username") String username);
+
+  @RequestMapping(method = RequestMethod.GET, path = "/{username}/applications")
+  JSONObject applications(
+      @PathVariable(name = "username") String username,
+      @RequestParam(name = "applicationId", required = false) String applicationId);
+
+  @RequestMapping(method = RequestMethod.GET, path = "/{username}/menus")
+  JSONObject menus(
+      @PathVariable(name = "username") String username,
+      @RequestParam(name = "applicationId", required = false) String applicationId);
+
+  @RequestMapping(method = RequestMethod.GET, path = "/{username}/operations")
+  JSONObject operations(
+      @PathVariable(name = "username") String username,
+      @RequestParam(name = "applicationId", required = false) String applicationId);
+
+
+  @RequestMapping(method = RequestMethod.GET, path = "/{username}/resources")
+  JSONObject resources(
+      @PathVariable(name = "username") String username,
+      @RequestParam(name = "applicationId", required = false) String applicationId);
+
+
+  @RequestMapping(method = RequestMethod.GET, path = "/applications")
+  JSONObject applications();
+  
+  @RequestMapping(method = RequestMethod.GET, path = "/roles")
+  JSONObject roles();
+
+  @RequestMapping(method = RequestMethod.GET, path = "/permissionRoleSets")
+  JSONObject permissionRoleSets(
+      @RequestParam(name = "applicationId", required = false) String applicationId);
+
+  @RequestMapping(method = RequestMethod.GET, path = "/resourceRoleSets")
+  JSONObject resourceRoleSets(
+      @RequestParam(name = "applicationId", required = false) String applicationId);
+
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/remote/biz/configuration/BizFeignClientConfiguration.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/remote/biz/configuration/BizFeignClientConfiguration.java
new file mode 100644
index 0000000..1e031d0
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/remote/biz/configuration/BizFeignClientConfiguration.java
@@ -0,0 +1,36 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.remote.biz.configuration;
+
+import org.apache.http.conn.HttpClientConnectionManager;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
+import org.springframework.context.annotation.Bean;
+
+import com.supwisdom.institute.backend.common.core.feign.FeignClientBuilder;
+
+import feign.Client;
+
+public class BizFeignClientConfiguration {
+
+  @Bean
+  public Client feignClient(
+      @Value("${sw-backend-biz-api.client-auth.enabled:false}") boolean enabled,
+      @Value("${sw-backend-biz-api.client-auth.key-password:}") String keyPassword,
+      @Value("${sw-backend-biz-api.client-auth.key-store:}") String keyStore,
+      @Value("${sw-backend-biz-api.client-auth.key-store-password:}") String keyStorePassword,
+      @Value("${sw-backend-biz-api.client-auth.trust-store:}") String trustStore,
+      @Value("${sw-backend-biz-api.client-auth.trust-store-password:}") String trustStorePassword,
+      ApacheHttpClientFactory httpClientFactory,
+      HttpClientConnectionManager httpClientConnectionManager) {
+    
+    return FeignClientBuilder.builder()
+      .enabled(enabled)
+      .keyPassword(keyPassword)
+      .keyStore(keyStore)
+      .keyStorePassword(keyStorePassword)
+      .trustStore(trustStore)
+      .trustStorePassword(trustStorePassword)
+      .build()
+      .apacheHttpClient(httpClientFactory, httpClientConnectionManager);
+  }
+
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/remote/biz/v1/admin/AdminBizRemoteFallbackFactory.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/remote/biz/v1/admin/AdminBizRemoteFallbackFactory.java
new file mode 100644
index 0000000..c73d3ee
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/remote/biz/v1/admin/AdminBizRemoteFallbackFactory.java
@@ -0,0 +1,61 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.remote.biz.v1.admin;
+
+import org.springframework.stereotype.Component;
+
+import com.alibaba.fastjson.JSONObject;
+import com.supwisdom.institute.backend.admin.bff.api.v1.model.biz.Biz;
+import com.supwisdom.institute.backend.admin.bff.api.v1.remote.exception.FallbackError;
+
+import feign.hystrix.FallbackFactory;
+
+@Component
+public class AdminBizRemoteFallbackFactory implements FallbackFactory<AdminBizRemoteFeignClient> {
+
+  @Override
+  public AdminBizRemoteFeignClient create(Throwable cause) {
+    return new AdminBizRemoteFeignClient() {
+
+      @Override
+      public JSONObject query(boolean loadAll, int pageIndex, int pageSize) {
+        if (cause != null) {
+          cause.printStackTrace();
+        }
+        return FallbackError.defaultErrorJson(cause);
+      }
+
+      @Override
+      public JSONObject load(String id) {
+        if (cause != null) {
+          cause.printStackTrace();
+        }
+        return FallbackError.defaultErrorJson(cause);
+      }
+
+      @Override
+      public JSONObject create(Biz biz) {
+        if (cause != null) {
+          cause.printStackTrace();
+        }
+        return FallbackError.defaultErrorJson(cause);
+      }
+
+      @Override
+      public JSONObject update(String id, Biz biz) {
+        if (cause != null) {
+          cause.printStackTrace();
+        }
+        return FallbackError.defaultErrorJson(cause);
+      }
+
+      @Override
+      public JSONObject delete(String id) {
+        if (cause != null) {
+          cause.printStackTrace();
+        }
+        return FallbackError.defaultErrorJson(cause);
+      }
+      
+    };
+  }
+
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/remote/biz/v1/admin/AdminBizRemoteFeignClient.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/remote/biz/v1/admin/AdminBizRemoteFeignClient.java
new file mode 100644
index 0000000..7c25a01
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/remote/biz/v1/admin/AdminBizRemoteFeignClient.java
@@ -0,0 +1,51 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.remote.biz.v1.admin;
+
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import com.alibaba.fastjson.JSONObject;
+import com.supwisdom.institute.backend.admin.bff.api.v1.model.biz.Biz;
+import com.supwisdom.institute.backend.admin.bff.api.v1.remote.biz.configuration.BizFeignClientConfiguration;
+
+@FeignClient(
+    configuration = {BizFeignClientConfiguration.class},
+    name = "biz-admin-biz-remote-feign-client",
+    url = "${sw-backend-biz-api.uri}/v1/admin/biz",
+    fallbackFactory = AdminBizRemoteFallbackFactory.class
+)
+public interface AdminBizRemoteFeignClient {
+  
+  @RequestMapping(method = RequestMethod.GET)
+  JSONObject query(
+      @RequestParam(name = "loadAll") boolean loadAll,
+      @RequestParam(name = "pageIndex") int pageIndex,
+      @RequestParam(name = "pageSize") int pageSize
+      
+  );
+  
+  @RequestMapping(method = RequestMethod.GET, path = "/{id}")
+  JSONObject load(
+      @PathVariable(name = "id") String id
+  );
+
+  @RequestMapping(method = RequestMethod.POST)
+  JSONObject create(
+      @RequestBody Biz biz
+  );
+
+  @RequestMapping(method = RequestMethod.PUT, path = "/{id}")
+  JSONObject update(
+      @PathVariable(name = "id") String id,
+      @RequestBody Biz biz
+  );
+  
+  @RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
+  JSONObject delete(
+      @PathVariable(name = "id") String id
+  );
+
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/remote/exception/FallbackError.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/remote/exception/FallbackError.java
new file mode 100644
index 0000000..14f8326
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/remote/exception/FallbackError.java
@@ -0,0 +1,21 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.remote.exception;
+
+import com.alibaba.fastjson.JSONObject;
+
+public class FallbackError {
+  
+  private FallbackError() {
+    
+  }
+  
+  public static JSONObject defaultErrorJson(Throwable cause) {
+    JSONObject error = new JSONObject();
+    
+    error.put("code", -1);
+    error.put("message", cause.getMessage());
+    error.put("error", cause.getMessage());
+    
+    return error;
+  }
+
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/service/.gitkeep b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/service/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/service/.gitkeep
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/service/base/AuthnService.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/service/base/AuthnService.java
new file mode 100644
index 0000000..8f6d9df
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/service/base/AuthnService.java
@@ -0,0 +1,202 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.service.base;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import lombok.extern.slf4j.Slf4j;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.alibaba.fastjson.JSONObject;
+import com.supwisdom.institute.backend.admin.bff.api.v1.model.base.Account;
+import com.supwisdom.institute.backend.admin.bff.api.v1.model.base.Application;
+import com.supwisdom.institute.backend.admin.bff.api.v1.model.base.Permission;
+import com.supwisdom.institute.backend.admin.bff.api.v1.model.base.PermissionRoleSet;
+import com.supwisdom.institute.backend.admin.bff.api.v1.model.base.ResourceRoleSet;
+import com.supwisdom.institute.backend.admin.bff.api.v1.model.base.Role;
+import com.supwisdom.institute.backend.admin.bff.api.v1.remote.base.v1.authn.AuthnRemoteFeignClient;
+
+@Slf4j
+@Service
+public class AuthnService {
+  
+  @Autowired
+  private AuthnRemoteFeignClient authnRemote;
+  
+  public Account account(String username) {
+    
+    JSONObject jsonObject = authnRemote.account(username);
+    if (jsonObject == null) {
+      return null;
+    }
+    
+    if (jsonObject.getIntValue("code") == 0) {
+      JSONObject data = jsonObject.getJSONObject("data");
+      
+      return data.toJavaObject(Account.class);
+    }
+    
+    return null;
+  }
+
+  public List<Role> roles(String username) {
+    
+    JSONObject jsonObject = authnRemote.roles(username);
+    if (jsonObject == null) {
+      return null;
+    }
+    
+    if (jsonObject.getIntValue("code") == 0) {
+      JSONObject data = jsonObject.getJSONObject("data");
+      
+      return data.getJSONArray("roles").toJavaList(Role.class);
+    }
+    
+    return null;
+  }
+
+  public List<Permission> menus(String username, String applicationId) {
+    
+    JSONObject jsonObject = authnRemote.menus(username, applicationId);
+    if (jsonObject == null) {
+      return null;
+    }
+    
+    if (jsonObject.getIntValue("code") == 0) {
+      JSONObject data = jsonObject.getJSONObject("data");
+      
+      return data.getJSONArray("permissions").toJavaList(Permission.class);
+    }
+    
+    return null;
+  }
+
+  
+  public List<Application> applications() {
+    
+    try {
+      JSONObject jsonObject = authnRemote.applications();
+      if (jsonObject == null) {
+        return null;
+      }
+      log.debug("{}", jsonObject.toJSONString());
+      
+      if (jsonObject.containsKey("code") && jsonObject.getIntValue("code") == 0) {
+        JSONObject data = jsonObject.getJSONObject("data");
+        
+        return data.getJSONArray("applications").toJavaList(Application.class);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+    
+    return null;
+  }
+  
+  public List<Role> roles() {
+    
+    try {
+      JSONObject jsonObject = authnRemote.roles();
+      if (jsonObject == null) {
+        return null;
+      }
+      log.debug("{}", jsonObject.toJSONString());
+      
+      if (jsonObject.containsKey("code") && jsonObject.getIntValue("code") == 0) {
+        JSONObject data = jsonObject.getJSONObject("data");
+        
+        return data.getJSONArray("roles").toJavaList(Role.class);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+    
+    return null;
+  }
+
+  
+  
+  
+
+  public static volatile Map<String, List<PermissionRoleSet>> mapPermissionRoleSets = 
+      new HashMap<String, List<PermissionRoleSet>>();
+  
+  public void putPermissionRoleSets(String applicationId, List<PermissionRoleSet> permissionRoleSets) {
+    AuthnService.mapPermissionRoleSets.put(applicationId, permissionRoleSets);
+  }
+  
+  public List<PermissionRoleSet> getPermissionRoleSets(String applicationId) {
+    if (StringUtils.isNotEmpty(applicationId)) {
+      return AuthnService.mapPermissionRoleSets.get(applicationId);
+    }
+    
+    List<PermissionRoleSet> allPermissionRoleSets = new ArrayList<>();
+    for (List<PermissionRoleSet> permissionRoleSets : AuthnService.mapPermissionRoleSets.values()) {
+      allPermissionRoleSets.addAll(permissionRoleSets);
+    }
+    
+    return allPermissionRoleSets;
+  }
+
+  public void loadPermissionRoleSets() {
+    
+    List<Application> applications = applications();
+    if (applications != null) {
+      for (Application application : applications) {
+        String applicationId = application.getId();
+        List<PermissionRoleSet> permissionRoleSets = this.permissionRoleSets(applicationId);
+        if (permissionRoleSets != null) {
+          this.putPermissionRoleSets(applicationId, permissionRoleSets);
+        }
+      }
+    }
+  }
+  
+  
+  public List<PermissionRoleSet> permissionRoleSets(String applicationId) {
+    
+    try {
+      JSONObject jsonObject = authnRemote.permissionRoleSets(applicationId);
+      if (jsonObject == null) {
+        return null;
+      }
+      log.debug("{}", jsonObject.toJSONString());
+      
+      if (jsonObject.containsKey("code") && jsonObject.getIntValue("code") == 0) {
+        JSONObject data = jsonObject.getJSONObject("data");
+        
+        return data.getJSONArray("permissionRoleSets").toJavaList(PermissionRoleSet.class);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+    
+    return null;
+  }
+
+  public List<ResourceRoleSet> resourceRoleSets() {
+    
+    try {
+      JSONObject jsonObject = authnRemote.resourceRoleSets(null);
+      if (jsonObject == null) {
+        return null;
+      }
+      log.debug("{}", jsonObject.toJSONString());
+      
+      if (jsonObject.containsKey("code") && jsonObject.getIntValue("code") == 0) {
+        JSONObject data = jsonObject.getJSONObject("data");
+        
+        return data.getJSONArray("resourceRoleSets").toJavaList(ResourceRoleSet.class);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+    
+    return null;
+  }
+
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/service/biz/BizService.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/service/biz/BizService.java
new file mode 100644
index 0000000..0fa9bdf
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/service/biz/BizService.java
@@ -0,0 +1,32 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.service.biz;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.alibaba.fastjson.JSONObject;
+import com.supwisdom.institute.backend.admin.bff.api.v1.remote.biz.v1.admin.AdminBizRemoteFeignClient;
+import com.supwisdom.institute.backend.admin.bff.api.v1.vo.biz.response.data.BizQueryResponseData;
+
+@Service
+public class BizService {
+  
+  @Autowired
+  private AdminBizRemoteFeignClient bizRemote;
+  
+  public BizQueryResponseData query(boolean loadAll, int pageIndex, int pageSize) {
+    
+    JSONObject jsonObject = bizRemote.query(loadAll, pageIndex, pageSize);
+    if (jsonObject == null) {
+      return null;
+    }
+    
+    if (jsonObject.getIntValue("code") == 0) {
+      JSONObject data = jsonObject.getJSONObject("data");
+      
+      return data.toJavaObject(BizQueryResponseData.class);
+    }
+    
+    return null;
+  }
+
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/vo/.gitkeep b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/vo/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/vo/.gitkeep
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/vo/biz/response/data/BizQueryResponseData.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/vo/biz/response/data/BizQueryResponseData.java
new file mode 100644
index 0000000..8b3891b
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/vo/biz/response/data/BizQueryResponseData.java
@@ -0,0 +1,45 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.vo.biz.response.data;
+
+import java.util.List;
+import java.util.Map;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.admin.bff.api.v1.model.biz.Biz;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiQueryResponseData;
+
+public class BizQueryResponseData implements IApiQueryResponseData<Biz> {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -380945463584664943L;
+
+  @Getter
+  private boolean loadAll;
+  @Getter
+  private int pageIndex;
+  @Getter
+  private int pageSize;
+  @Getter
+  private Map<String, Object> mapBean;
+  @Getter
+  private Map<String, String> orderBy;
+  
+  @Getter
+  @Setter
+  private int pageCount;
+  @Getter
+  @Setter
+  private long recordCount;
+  
+  @Getter
+  @Setter
+  private int currentItemCount;
+  
+  @Getter
+  @Setter
+  private List<Biz> items;
+  
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/vo/me/response/data/CurrentUserResponseData.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/vo/me/response/data/CurrentUserResponseData.java
new file mode 100644
index 0000000..97443d3
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/vo/me/response/data/CurrentUserResponseData.java
@@ -0,0 +1,23 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.vo.me.response.data;
+
+import org.springframework.beans.BeanUtils;
+
+import com.supwisdom.institute.backend.admin.bff.api.v1.model.me.CurrentUser;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+public class CurrentUserResponseData extends CurrentUser implements IApiResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 2296310299015658064L;
+
+  public static CurrentUserResponseData of(CurrentUser currentUser) {
+    CurrentUserResponseData data = new CurrentUserResponseData();
+    
+    BeanUtils.copyProperties(currentUser, data);
+    
+    return data;
+  }
+
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/vo/me/response/data/GrantedMenusResponseData.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/vo/me/response/data/GrantedMenusResponseData.java
new file mode 100644
index 0000000..2aacc52
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/vo/me/response/data/GrantedMenusResponseData.java
@@ -0,0 +1,30 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.vo.me.response.data;
+
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.admin.bff.api.v1.model.me.GrantedMenu;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+public class GrantedMenusResponseData implements IApiResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -7495122676647463043L;
+  
+  @Getter
+  @Setter
+  private List<GrantedMenu> menus;
+
+  public static GrantedMenusResponseData of(List<GrantedMenu> grantedMenus) {
+    GrantedMenusResponseData data = new GrantedMenusResponseData();
+    
+    data.setMenus(grantedMenus);
+    
+    return data;
+  }
+
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/vo/open/response/data/MenusResponseData.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/vo/open/response/data/MenusResponseData.java
new file mode 100644
index 0000000..6beabd2
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/vo/open/response/data/MenusResponseData.java
@@ -0,0 +1,30 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.vo.open.response.data;
+
+import java.util.List;
+
+import com.supwisdom.institute.backend.admin.bff.api.v1.model.open.Menu;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+public class MenusResponseData implements IApiResponseData {
+  
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -3516729316884770800L;
+
+  @Getter
+  @Setter
+  private List<Menu> menus;
+
+  public static MenusResponseData of(List<Menu> menus) {
+    MenusResponseData data = new MenusResponseData();
+    
+    data.setMenus(menus);
+    
+    return data;
+  }
+
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/vo/open/response/data/OpenSyncRolesResponseData.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/vo/open/response/data/OpenSyncRolesResponseData.java
new file mode 100644
index 0000000..69453b6
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/api/v1/vo/open/response/data/OpenSyncRolesResponseData.java
@@ -0,0 +1,35 @@
+package com.supwisdom.institute.backend.admin.bff.api.v1.vo.open.response.data;
+
+import java.util.List;
+
+import com.supwisdom.institute.backend.admin.bff.api.v1.model.open.SyncRoleModel;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+public class OpenSyncRolesResponseData implements IApiResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 2739730369123803327L;
+  
+  @Getter
+  @Setter
+  private String applicationId;
+
+  @Getter
+  @Setter
+  private List<SyncRoleModel> roles;
+
+  public static OpenSyncRolesResponseData of(String applicationId, List<SyncRoleModel> roles) {
+    OpenSyncRolesResponseData data = new OpenSyncRolesResponseData();
+    
+    data.setApplicationId(applicationId);
+    data.setRoles(roles);
+    
+    return data;
+  }
+
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/configuration/FeignBasicAuthRequestInterceptor.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/configuration/FeignBasicAuthRequestInterceptor.java
new file mode 100644
index 0000000..191e1e8
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/configuration/FeignBasicAuthRequestInterceptor.java
@@ -0,0 +1,77 @@
+package com.supwisdom.institute.backend.admin.bff.configuration;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import feign.RequestInterceptor;
+import feign.RequestTemplate;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.util.StringUtils;
+
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * feign请求参数转化
+ * @author fengpy
+ */
+//@Configuration
+public class FeignBasicAuthRequestInterceptor implements RequestInterceptor {
+    @Autowired
+    private ObjectMapper objectMapper;
+
+    public FeignBasicAuthRequestInterceptor() {}
+
+    @Override
+    public void apply(RequestTemplate template) {
+        ///**get-pojo贯穿*/
+        if (template.method().equals("GET") && template.request().body() != null) {
+            try {
+                JsonNode jsonNode = objectMapper.readTree(template.request().body());
+                //template.body(null);
+                Map<String, Collection<String>> queries = new HashMap<>();
+                //feign 不支持 GET 方法传 POJO, json body转query
+                buildQuery(jsonNode, "", queries);
+                template.queries(queries);
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    //处理 get-pojo贯穿
+    private void buildQuery(JsonNode jsonNode, String path, Map<String, Collection<String>> queries) {
+        if (!jsonNode.isContainerNode()) { //叶子节点
+            if (jsonNode.isNull()) {
+                return;
+            }
+            Collection<String> values = queries.get(path);
+            if (null == values) {
+                values = new ArrayList<>();
+                queries.put(path, values);
+            }
+            values.add(jsonNode.asText());
+            return;
+        }
+        if (jsonNode.isArray()) { //数组节点
+            Iterator<JsonNode> it = jsonNode.elements();
+            while (it.hasNext()) {
+                buildQuery(it.next(), path, queries);
+            }
+        } else {
+            Iterator<Map.Entry<String, JsonNode>> it = jsonNode.fields();
+            while (it.hasNext()) {
+                Map.Entry<String, JsonNode> entry = it.next();
+                if (StringUtils.hasText(path)) {
+                    if ("mapBean".equals(path)||"orderBy".equals(path)||"sequence".equals(path)) {
+                        buildQuery(entry.getValue(), path + "[" + entry.getKey() + "]", queries);
+                    } else {
+                        buildQuery(entry.getValue(), path + "." + entry.getKey(), queries);
+                    }
+                } else { //根节点
+                    buildQuery(entry.getValue(), entry.getKey(), queries);
+                }
+            }
+        }
+    }
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/configuration/Swagger2Config.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/configuration/Swagger2Config.java
new file mode 100644
index 0000000..5a1022a
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/configuration/Swagger2Config.java
@@ -0,0 +1,132 @@
+package com.supwisdom.institute.backend.admin.bff.configuration;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.AuthorizationScopeBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.ApiKey;
+import springfox.documentation.service.AuthorizationScope;
+import springfox.documentation.service.BasicAuth;
+import springfox.documentation.service.Contact;
+import springfox.documentation.service.SecurityReference;
+import springfox.documentation.service.SecurityScheme;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.service.contexts.SecurityContext;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger.web.UiConfiguration;
+import springfox.documentation.swagger.web.UiConfigurationBuilder;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+import static com.google.common.collect.Lists.*;
+
+@Configuration
+@EnableSwagger2
+public class Swagger2Config {
+
+  @Value("${swagger2.apis.basePackage:com.supwisdom.institute}")
+  private String basePackage;
+
+  @Bean
+  public Docket createRestApi() {
+    return new Docket(DocumentationType.SWAGGER_2)
+        .securitySchemes(securitySchemes())
+        .securityContexts(securityContexts())
+        .apiInfo(apiInfo())
+        .select()
+        .apis(RequestHandlerSelectors.basePackage(basePackage))
+        .paths(PathSelectors.any())
+        .build()
+    ;
+  }
+
+  private ApiInfo apiInfo() {
+    Contact contact = new Contact("Backend Admin BFF", "https://sw-backend-api.supwisdom.com/swagger-ui.html", ""); // name, url, email
+    return new ApiInfoBuilder()
+        .title("Backend Admin BFF APIs")
+        .description("管理后台 - 后端接口<br /><br />"
+            + "X-FORWARD-USER(测试用):<br /><br />"
+            + "明文:{\"attributes\":{\"accountId\":\"1\"},\"roles\":[\"ROLE_ADMIN\",\"administrator\",\"user\"],\"username\":\"swadmin\"}<br /><br />"
+            + "Base64:eyJhdHRyaWJ1dGVzIjp7ImFjY291bnRJZCI6IjEifSwicm9sZXMiOlsiUk9MRV9BRE1JTiIsImFkbWluaXN0cmF0b3IiLCJ1c2VyIl0sInVzZXJuYW1lIjoic3dhZG1pbiJ9<br /><br />"
+            + "使用 Base64字符串 进行 Authorize,然后进行接口测试<br /><br />"
+            + "若需要其他帐号,请自行拼接明文,再进行 Base64 编码<br /><br />"
+            + ""
+        )
+        .termsOfServiceUrl("http://www.supwisdom.com/")
+        .contact(contact)
+        .version("1.0")
+        .build();
+  }
+  
+  private List<SecurityScheme> securitySchemes() {
+    //return newArrayList(new BasicAuth("sample"));
+    return newArrayList(
+        //new BasicAuth("Basic"),
+        //new ApiKey("JWTToken", "Authorization", "header"), 
+        new ApiKey("SimpleUserTransmit", "X-FORWARD-USER", "header"));
+  }
+  
+  private List<SecurityContext> securityContexts() {
+
+    List<SecurityReference> globalSecurityReference = newArrayList(
+        new SecurityReference("SimpleUserTransmit", new AuthorizationScope[]{new AuthorizationScope("global", "accessEverything")}));
+    
+//    AuthorizationScope[] authScopes = new AuthorizationScope[1];
+//    authScopes[0] = new AuthorizationScopeBuilder()
+//            .scope("read")
+//            .description("read access")
+//            .build();
+//    SecurityReference securityReference = SecurityReference.builder()
+//            .reference("sample")
+//            .scopes(authScopes)
+//            .build();
+
+    return newArrayList(
+        SecurityContext.builder()
+          .securityReferences(newArrayList(globalSecurityReference))
+          .build());
+  }
+
+  @Bean
+  public UiConfiguration uiConfig() {
+    
+    return UiConfigurationBuilder.builder().build();
+    
+//    return new UiConfiguration(null, // url
+//        "none", // docExpansion => none | list
+//        "alpha", // apiSorter => alpha
+//        "schema", // defaultModelRendering => schema
+//        UiConfiguration.Constants.DEFAULT_SUBMIT_METHODS,
+//        false, // enableJsonEditor => true || false
+//        true, // showRequestHeaders => true | false
+//        60000L); // requestTimeout => in milliseconds, defaults to null
+//    // (uses jquery xh timeout)
+  }
+  
+  
+//  @Bean
+//  public SecurityConfiguration oauth2() {
+//      return SecurityConfigurationBuilder.builder()
+//          .clientId("common")
+//          .clientSecret("secret")
+//          .scopeSeparator(" ")
+//          .useBasicAuthenticationWithAccessCodeGrant(true)
+//          .build();
+//  }
+//  
+//  @Bean
+//  public SecurityConfiguration basic() {
+//      return SecurityConfigurationBuilder.builder()
+//          .clientId("common")
+//          .clientSecret("secret")
+//          .scopeSeparator(" ")
+//          .useBasicAuthenticationWithAccessCodeGrant(true)
+//          .build();
+//  }
+
+}
diff --git a/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/utils/CurrentUserUtil.java b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/utils/CurrentUserUtil.java
new file mode 100644
index 0000000..b97e5ae
--- /dev/null
+++ b/bff/admin/src/main/java/com/supwisdom/institute/backend/admin/bff/utils/CurrentUserUtil.java
@@ -0,0 +1,26 @@
+package com.supwisdom.institute.backend.admin.bff.utils;
+
+import com.supwisdom.institute.backend.common.core.transmit.user.User;
+import com.supwisdom.institute.backend.common.core.transmit.user.UserContext;
+
+public class CurrentUserUtil {
+  
+  public static User currentUser() {
+    User user = UserContext.getUser();
+    
+    if (user == null) {
+      throw new RuntimeException("current user is null");
+    }
+    
+    return user;
+  }
+  
+  public static String currentUsername() {
+    return currentUser().getUsername();
+  }
+
+  public static String currentUserAccountId() {
+    return String.valueOf(currentUser().getAttributes().get("accountId"));
+  }
+
+}
diff --git a/bff/admin/src/main/resources/application-docker.yml b/bff/admin/src/main/resources/application-docker.yml
new file mode 100644
index 0000000..81e78c9
--- /dev/null
+++ b/bff/admin/src/main/resources/application-docker.yml
@@ -0,0 +1,74 @@
+server:
+  port: ${SERVER_PORT:8443}
+  ssl:
+    enabled: ${SSL_ENABLED:true}
+    clientAuth: NEED
+    key-store: ${SSL_KEYSTORE_FILE:file:/certs/server/server.keystore}
+    key-store-password: ${SSL_KEYSTORE_PASSWORD:}
+    trust-store: ${SSL_TRUSTSTORE_FILE:file:/certs/server/server.truststore}
+    trust-store-password: ${SSL_TRUSTSTORE_PASSWORD:}
+  tomcat: 
+    accesslog: 
+      enabled: ${TOMCAT_ACCESSLOG_ENABLED:false}
+      buffered: ${TOMCAT_ACCESSLOG_BUFFERED:true}
+      directory: ${TOMCAT_ACCESSLOG_DIR:log}
+      prefix: ${TOMCAT_ACCESSLOG_PREFIX:sa-api-accesslog}
+      suffix: ${TOMCAT_ACCESSLOG_SUFFIX:.log}
+      file-date-format: ${TOMCAT_ACCESSLOG_FILE_DATE_FORMAT:.yyyy-MM-dd}
+      rotate: ${TOMCAT_ACCESSLOG_ROTATE:true}
+
+
+##
+# logging
+#
+logging:
+  level:
+    root: INFO
+    com.supwisdom: INFO
+
+
+spring:
+  jackson:
+    time-zone: ${JACKSON_TIME_ZONE:Asia/Shanghai}
+
+
+##
+# online-doc
+#
+infras.online-doc.enabled: ${INFRAS_ONLINE_DOC_ENABLED:false}
+infras.online-doc.md-docs.staitc.path: ${INFRAS_ONLINE_DOC_MD_DOCS_STATIC_PATH:/doc/}
+infras.online-doc.api-docs.staitc.path: ${INFRAS_ONLINE_DOC_API_DOCS_STATIC_PATH:/api-docs/}
+
+
+##
+# server url for feign
+#
+sw-backend-base-api: 
+  uri: ${SW_BACKEND_BASE_API_URI:https://sw-backend-admin-sa}
+  client-auth:
+    enabled: ${SW_BACKEND_BASE_API_CLIENT_AUTH_ENABLED:true}
+    key-password: ${SW_BACKEND_BASE_API_CLIENT_AUTH_KEY_PASSWORD:}
+    key-store: ${SW_BACKEND_BASE_API_CLIENT_AUTH_KEYSTORE_FILE:file:/certs/common/common.keystore}
+    key-store-password: ${SW_BACKEND_BASE_API_CLIENT_AUTH_KEYSTORE_PASSWORD:}
+    trust-store: ${SW_BACKEND_BASE_API_CLIENT_AUTH_TRUSTSTORE_FILE:file:/certs/common/common.truststore}
+    trust-store-password: ${SW_BACKEND_BASE_API_CLIENT_AUTH_TRUSTSTORE_PASSWORD:}
+
+sw-backend-system-api: 
+  uri: ${SW_BACKEND_SYSTEM_API_URI:https://sw-backend-admin-sa}
+  client-auth:
+    enabled: ${SW_BACKEND_SYSTEM_API_CLIENT_AUTH_ENABLED:true}
+    key-password: ${SW_BACKEND_SYSTEM_API_CLIENT_AUTH_KEY_PASSWORD:}
+    key-store: ${SW_BACKEND_SYSTEM_API_CLIENT_AUTH_KEYSTORE_FILE:file:/certs/common/common.keystore}
+    key-store-password: ${SW_BACKEND_SYSTEM_API_CLIENT_AUTH_KEYSTORE_PASSWORD:}
+    trust-store: ${SW_BACKEND_SYSTEM_API_CLIENT_AUTH_TRUSTSTORE_FILE:file:/certs/common/common.truststore}
+    trust-store-password: ${SW_BACKEND_SYSTEM_API_CLIENT_AUTH_TRUSTSTORE_PASSWORD:}
+
+sw-backend-biz-api: 
+  uri: ${SW_BACKEND_BIZ_API_URI:https://sw-backend-biz-sa}
+  client-auth:
+    enabled: ${SW_BACKEND_BIZ_API_CLIENT_AUTH_ENABLED:true}
+    key-password: ${SW_BACKEND_BIZ_API_CLIENT_AUTH_KEY_PASSWORD:}
+    key-store: ${SW_BACKEND_BIZ_API_CLIENT_AUTH_KEYSTORE_FILE:file:/certs/common/common.keystore}
+    key-store-password: ${SW_BACKEND_BIZ_API_CLIENT_AUTH_KEYSTORE_PASSWORD:}
+    trust-store: ${SW_BACKEND_BIZ_API_CLIENT_AUTH_TRUSTSTORE_FILE:file:/certs/common/common.truststore}
+    trust-store-password: ${SW_BACKEND_BIZ_API_CLIENT_AUTH_TRUSTSTORE_PASSWORD:}
diff --git a/bff/admin/src/main/resources/application.yml b/bff/admin/src/main/resources/application.yml
new file mode 100644
index 0000000..8ad7215
--- /dev/null
+++ b/bff/admin/src/main/resources/application.yml
@@ -0,0 +1,68 @@
+server:
+  port: 8081
+  ssl:
+    enabled: false
+
+
+##
+# logging
+#
+logging:
+  level:
+    root: INFO
+    com.supwisdom: DEBUG
+#    org.springframework.web: INFO
+#    org.springframework.cloud.openfeign: INFO
+
+
+swagger2.apis.basePackage: com.supwisdom.institute
+
+
+spring:
+  jackson:
+    time-zone: Asia/Shanghai
+
+
+feign:
+  client:
+    config:
+      default:
+        #errorDecoder: com.supwisdom.leaveschool.common.config.BaseExceptionErrorDecoder
+        connectTimeout: 12000
+        readTimeout: 12000
+        loggerLevel: full
+  hystrix:
+    enabled: true
+  httpclient:
+    enabled: true
+
+hystrix:
+  command:
+    default:
+      execution:
+        timeout:
+          enabled: true
+        isolation:
+          thread:
+            timeoutInMilliseconds: 12000
+
+
+##
+# infras.online-doc
+#
+infras.online-doc.enabled: true
+infras.online-doc.md-docs.staitc.path: /Users/loie/c/work/git/institute/sw-backend/doc/
+infras.online-doc.api-docs.staitc.path: /Users/loie/c/work/git/institute/sw-backend/api-docs/
+
+
+##
+# server url for feign
+#
+sw-backend-base-api:
+  uri: http://localhost:8082
+
+sw-backend-system-api: 
+  uri: http://localhost:8082
+
+sw-backend-biz-api: 
+  uri: http://localhost:8082
diff --git a/bff/admin/src/main/resources/bootstrap.yml b/bff/admin/src/main/resources/bootstrap.yml
new file mode 100644
index 0000000..b2a926b
--- /dev/null
+++ b/bff/admin/src/main/resources/bootstrap.yml
@@ -0,0 +1,3 @@
+spring:
+  application:
+    name: sw-backend-admin-aggr
diff --git a/bff/pom.xml b/bff/pom.xml
new file mode 100644
index 0000000..a25e6d7
--- /dev/null
+++ b/bff/pom.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  
+  <parent>
+    <groupId>com.supwisdom.institute</groupId>
+    <artifactId>sw-backend-parent</artifactId>
+    <version>0.0.1</version>
+  </parent>
+  
+  <groupId>com.supwisdom.institute</groupId>
+  <artifactId>sw-backend-bff-parent</artifactId>
+  <version>0.0.1</version>
+  <packaging>pom</packaging>
+
+  <name>Supwisdom Backend Framework Backend for Frontend</name>
+  <description>Supwisdom Backend Framework Backend for Frontend project</description>
+
+  <modules>
+    <module>admin</module>
+  </modules>
+
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-deploy-plugin</artifactId>
+        <configuration>
+          <skip>true</skip>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/biz/api/pom.xml b/biz/api/pom.xml
new file mode 100644
index 0000000..3ee3c79
--- /dev/null
+++ b/biz/api/pom.xml
@@ -0,0 +1,110 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.supwisdom.institute</groupId>
+    <artifactId>sw-backend-parent</artifactId>
+    <version>0.0.1</version>
+    <relativePath>../../</relativePath>
+  </parent>
+
+  <groupId>com.supwisdom.institute</groupId>
+  <artifactId>sw-backend-biz-api</artifactId>
+  <version>0.0.1</version>
+  <packaging>jar</packaging>
+
+  <name>Supwisdom Backend Framework BIZ API</name>
+  <description>Supwisdom Backend Framework BIZ API project</description>
+
+  <dependencies>
+
+    <dependency>
+      <groupId>org.projectlombok</groupId>
+      <artifactId>lombok</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>javax.servlet-api</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-web</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>com.supwisdom.institute</groupId>
+      <artifactId>sw-backend-common-core</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.supwisdom.institute</groupId>
+      <artifactId>sw-backend-common-utils</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.supwisdom.institute</groupId>
+      <artifactId>sw-backend-common-framework</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>com.supwisdom.institute</groupId>
+      <artifactId>sw-backend-biz-domain</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-release-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-failsafe-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/biz/api/src/main/java/com/supwisdom/institute/backend/biz/api/v1/admin/AdminBizController.java b/biz/api/src/main/java/com/supwisdom/institute/backend/biz/api/v1/admin/AdminBizController.java
new file mode 100644
index 0000000..aa324c6
--- /dev/null
+++ b/biz/api/src/main/java/com/supwisdom/institute/backend/biz/api/v1/admin/AdminBizController.java
@@ -0,0 +1,175 @@
+package com.supwisdom.institute.backend.biz.api.v1.admin;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.http.HttpStatus;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.supwisdom.institute.backend.biz.api.vo.request.BizCreateRequest;
+import com.supwisdom.institute.backend.biz.api.vo.request.BizQueryRequest;
+import com.supwisdom.institute.backend.biz.api.vo.request.BizUpdateRequest;
+import com.supwisdom.institute.backend.biz.api.vo.response.BizCreateResponseData;
+import com.supwisdom.institute.backend.biz.api.vo.response.BizLoadResponseData;
+import com.supwisdom.institute.backend.biz.api.vo.response.BizQueryResponseData;
+import com.supwisdom.institute.backend.biz.api.vo.response.BizRemoveResponseData;
+import com.supwisdom.institute.backend.biz.api.vo.response.BizUpdateResponseData;
+import com.supwisdom.institute.backend.biz.domain.entity.Biz;
+import com.supwisdom.institute.backend.biz.domain.exception.BizException;
+import com.supwisdom.institute.backend.biz.domain.service.BizService;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.DefaultApiResponse;
+
+@Api(value = "BizAdminBiz", tags = { "BizAdminBiz" }, description = "Biz示例接口")
+@RestController
+@RequestMapping("/v1/admin/biz")
+public class AdminBizController {
+  
+  @Autowired
+  private BizService bizService;
+
+  /**
+   * @param bizQueryRequest
+   * @return
+   */
+  @ApiOperation(value = "查询配置列表", notes = "查询配置列表", nickname = "systemAdminBizQuery")
+  @ApiImplicitParams({
+    @ApiImplicitParam(name = "loadAll", value = "是否加载全部", required = true, dataType = "boolean", paramType = "query", defaultValue = "false"),
+    @ApiImplicitParam(name = "pageIndex", value = "分页 - 页码", required = true, dataType = "int", paramType = "query", defaultValue = "0", example = "0"),
+    @ApiImplicitParam(name = "pageSize", value = "分页 - 每页记录数", required = true, dataType = "int", paramType = "query", defaultValue = "20", example = "20"),
+    @ApiImplicitParam(name = "mapBean[deleted]", value = "查询条件 - 删除状态 (精确)", required = false, dataType = "boolean", paramType = "query"),
+    @ApiImplicitParam(name = "mapBean[categoryCode]", value = "查询条件 - 分类代码 (精确)", required = false, dataType = "string", paramType = "query"),
+    @ApiImplicitParam(name = "mapBean[categoryName]", value = "查询条件 - 分类名称 (模糊)", required = false, dataType = "string", paramType = "query"),
+    @ApiImplicitParam(name = "mapBean[name]", value = "查询条件 - 名称 (模糊)", required = false, dataType = "string", paramType = "query"),
+    @ApiImplicitParam(name = "mapBean[description]", value = "查询条件 - 描述 (模糊)", required = false, dataType = "string", paramType = "query"),
+    @ApiImplicitParam(name = "mapBean[bizKey]", value = "查询条件 - 配置Key (精确)", required = false, dataType = "string", paramType = "query"),
+  })
+  @RequestMapping(method = RequestMethod.GET, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<BizQueryResponseData> query(BizQueryRequest bizQueryRequest) {
+    
+    Page<Biz> page = bizService.selectPageList(
+        bizQueryRequest.isLoadAll(),
+        bizQueryRequest.getPageIndex(),
+        bizQueryRequest.getPageSize(),
+        bizQueryRequest.getMapBean(),
+        bizQueryRequest.getOrderBy());
+
+    BizQueryResponseData resp = BizQueryResponseData.of(bizQueryRequest).build(page);
+    
+    return new DefaultApiResponse<BizQueryResponseData>(resp);
+  }
+
+  /**
+   * @param id
+   * @return
+   */
+  @ApiOperation(value = "根据ID获取配置项", notes = "根据ID获取配置项", nickname="systemAdminBizLoad")
+  @RequestMapping(method = RequestMethod.GET, path = "/{id}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<BizLoadResponseData> load(
+      @PathVariable("id") String id) {
+    
+    if (id == null || id.length() == 0) {
+      throw BizException.newInstance("exception.get.id.must.not.empty");
+    }
+    
+    Biz biz = bizService.selectById(id);
+    
+    if (biz == null) {
+      throw BizException.newInstance("exception.get.domain.not.exist");
+    }
+
+    BizLoadResponseData resp = BizLoadResponseData.of(biz);
+    
+    return new DefaultApiResponse<BizLoadResponseData>(resp);
+  }
+  
+  /**
+   * @param bizCreateRequest
+   * @return
+   */
+  @ApiOperation(value = "创建配置项", notes = "创建配置项", nickname = "systemAdminBizCreate")
+  @RequestMapping(method = RequestMethod.POST, produces = MimeTypeUtils.APPLICATION_JSON_VALUE, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.CREATED)
+  @ResponseBody
+  public DefaultApiResponse<BizCreateResponseData> create(
+      @RequestBody BizCreateRequest bizCreateRequest) {
+
+    // FIXME: 验证数据有效性
+
+    Biz entity = EntityUtils.copy(bizCreateRequest, new Biz());
+
+    Biz ret = bizService.insert(entity);
+
+    BizCreateResponseData resp = BizCreateResponseData.of(ret);
+
+    return new DefaultApiResponse<BizCreateResponseData>(resp);
+  }
+
+  @ApiOperation(value = "更新", notes = "更新", nickname = "systemAdminBizUpdate")
+  @RequestMapping(method = RequestMethod.PUT, path = "/{id}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<BizUpdateResponseData> update(
+      @PathVariable("id") String id,
+      @RequestBody BizUpdateRequest bizUpdateRequest) {
+
+    if (id == null || id.length() == 0) {
+      throw BizException.newInstance("exception.update.id.must.not.empty");
+    }
+
+    Biz tmp = bizService.selectById(id);
+    if (tmp == null) {
+      throw BizException.newInstance("exception.update.domain.not.exist");
+    }
+
+    Biz entity = EntityUtils.copy(bizUpdateRequest, new Biz());
+    entity.setId(id);
+
+    entity = EntityUtils.merge(tmp, entity);
+    
+    Biz ret = bizService.update(entity);
+
+    BizUpdateResponseData resp = BizUpdateResponseData.of(ret);
+    
+    return new DefaultApiResponse<BizUpdateResponseData>(resp);
+  }
+  
+  @ApiOperation(value = "删除", notes = "删除", nickname = "systemAdminBizRemove")
+  @RequestMapping(method = RequestMethod.DELETE, path = "/{id}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<BizRemoveResponseData> remove(
+      @PathVariable("id") String id) {
+    
+    if (id == null || id.length() == 0) {
+      throw BizException.newInstance("exception.remove.id.must.not.empty");
+    }
+
+    Biz tmp = bizService.selectById(id);
+    if (tmp == null) {
+      throw BizException.newInstance("exception.remove.domain.not.exist");
+    }
+    
+    bizService.deleteById(id);
+    
+    BizRemoveResponseData resp = BizRemoveResponseData.of(tmp);
+    
+    return new DefaultApiResponse<BizRemoveResponseData>(resp);
+  }
+
+}
diff --git a/biz/api/src/main/java/com/supwisdom/institute/backend/biz/api/v1/admin/AdminHelloController.java b/biz/api/src/main/java/com/supwisdom/institute/backend/biz/api/v1/admin/AdminHelloController.java
new file mode 100644
index 0000000..a46c806
--- /dev/null
+++ b/biz/api/src/main/java/com/supwisdom/institute/backend/biz/api/v1/admin/AdminHelloController.java
@@ -0,0 +1,26 @@
+package com.supwisdom.institute.backend.biz.api.v1.admin;
+
+import io.swagger.annotations.Api;
+import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.supwisdom.institute.backend.common.core.transmit.user.User;
+import com.supwisdom.institute.backend.common.core.transmit.user.UserContext;
+
+@Api(value = "BizAdminHello", tags = { "BizAdminHello" }, description = "示例接口")
+@Slf4j
+@RestController
+@RequestMapping("/v1/admin/hello")
+public class AdminHelloController {
+  
+  @RequestMapping(method = RequestMethod.GET, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  public User biz() {
+    User user = UserContext.getUser(); log.debug("{}", user);
+    return user;
+  }
+
+}
diff --git a/biz/api/src/main/java/com/supwisdom/institute/backend/biz/api/vo/request/BizCreateRequest.java b/biz/api/src/main/java/com/supwisdom/institute/backend/biz/api/vo/request/BizCreateRequest.java
new file mode 100644
index 0000000..e6b6f0e
--- /dev/null
+++ b/biz/api/src/main/java/com/supwisdom/institute/backend/biz/api/vo/request/BizCreateRequest.java
@@ -0,0 +1,16 @@
+package com.supwisdom.institute.backend.biz.api.vo.request;
+
+import com.supwisdom.institute.backend.biz.domain.entity.Biz;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiCreateRequest;
+
+/**
+ * @author loie
+ */
+public class BizCreateRequest extends Biz implements IApiCreateRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -3512866840130579457L;
+
+}
diff --git a/biz/api/src/main/java/com/supwisdom/institute/backend/biz/api/vo/request/BizQueryRequest.java b/biz/api/src/main/java/com/supwisdom/institute/backend/biz/api/vo/request/BizQueryRequest.java
new file mode 100644
index 0000000..7f60870
--- /dev/null
+++ b/biz/api/src/main/java/com/supwisdom/institute/backend/biz/api/vo/request/BizQueryRequest.java
@@ -0,0 +1,44 @@
+package com.supwisdom.institute.backend.biz.api.vo.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Map;
+
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiQueryRequest;
+
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * @author loie
+ */
+public class BizQueryRequest implements IApiQueryRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -4345168235529389375L;
+
+  @Getter
+  @Setter
+  private boolean loadAll = false;
+
+  @Getter
+  @Setter
+  private int pageIndex = 0;
+
+  @Getter
+  @Setter
+  private int pageSize = 20;
+
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, Object> mapBean;
+
+  @Getter
+  @Setter
+  @ApiModelProperty(hidden = true)
+  private Map<String, String> orderBy;
+
+}
diff --git a/biz/api/src/main/java/com/supwisdom/institute/backend/biz/api/vo/request/BizUpdateRequest.java b/biz/api/src/main/java/com/supwisdom/institute/backend/biz/api/vo/request/BizUpdateRequest.java
new file mode 100644
index 0000000..74973ce
--- /dev/null
+++ b/biz/api/src/main/java/com/supwisdom/institute/backend/biz/api/vo/request/BizUpdateRequest.java
@@ -0,0 +1,23 @@
+package com.supwisdom.institute.backend.biz.api.vo.request;
+
+import com.supwisdom.institute.backend.biz.domain.entity.Biz;
+import com.supwisdom.institute.backend.common.framework.vo.request.IApiUpdateRequest;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class BizUpdateRequest extends Biz implements IApiUpdateRequest {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 6002556449210326472L;
+
+  @Getter
+  @Setter
+  private String id;
+
+}
diff --git a/biz/api/src/main/java/com/supwisdom/institute/backend/biz/api/vo/response/BizCreateResponseData.java b/biz/api/src/main/java/com/supwisdom/institute/backend/biz/api/vo/response/BizCreateResponseData.java
new file mode 100644
index 0000000..9a91fce
--- /dev/null
+++ b/biz/api/src/main/java/com/supwisdom/institute/backend/biz/api/vo/response/BizCreateResponseData.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.biz.api.vo.response;
+
+import com.supwisdom.institute.backend.biz.domain.entity.Biz;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiCreateResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class BizCreateResponseData extends Biz implements IApiCreateResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -7081539211184618366L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private BizCreateResponseData() {
+
+  }
+
+  public static BizCreateResponseData of(Biz entity) {
+    BizCreateResponseData data = new BizCreateResponseData();
+
+    return EntityUtils.copy(entity, data);
+  }
+
+}
diff --git a/biz/api/src/main/java/com/supwisdom/institute/backend/biz/api/vo/response/BizLoadResponseData.java b/biz/api/src/main/java/com/supwisdom/institute/backend/biz/api/vo/response/BizLoadResponseData.java
new file mode 100644
index 0000000..84c2864
--- /dev/null
+++ b/biz/api/src/main/java/com/supwisdom/institute/backend/biz/api/vo/response/BizLoadResponseData.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.biz.api.vo.response;
+
+import com.supwisdom.institute.backend.biz.domain.entity.Biz;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiLoadResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class BizLoadResponseData extends Biz implements IApiLoadResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -921871332091924834L;
+  
+  @Getter
+  @Setter
+  private String id;
+  
+  private BizLoadResponseData() {
+    
+  }
+  
+  public static BizLoadResponseData of(Biz entity) {
+    BizLoadResponseData data = new BizLoadResponseData();
+    
+    return EntityUtils.copy(entity, data);
+  }
+
+}
diff --git a/biz/api/src/main/java/com/supwisdom/institute/backend/biz/api/vo/response/BizQueryResponseData.java b/biz/api/src/main/java/com/supwisdom/institute/backend/biz/api/vo/response/BizQueryResponseData.java
new file mode 100644
index 0000000..3c19ad1
--- /dev/null
+++ b/biz/api/src/main/java/com/supwisdom/institute/backend/biz/api/vo/response/BizQueryResponseData.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.biz.api.vo.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.data.domain.Page;
+
+import com.supwisdom.institute.backend.biz.api.vo.request.BizQueryRequest;
+import com.supwisdom.institute.backend.biz.domain.entity.Biz;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiQueryResponseData;
+
+/**
+ * @author loie
+ */
+public class BizQueryResponseData implements IApiQueryResponseData<Biz> {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 5706417972093485886L;
+  
+  private BizQueryResponseData(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean, Map<String, String> orderBy) {
+    this.loadAll = loadAll;
+    this.pageIndex = pageIndex;
+    this.pageSize = pageSize;
+    this.mapBean = mapBean;
+    this.orderBy = orderBy;
+  }
+
+  public static BizQueryResponseData of(BizQueryRequest queryRequest) {
+    BizQueryResponseData data = new BizQueryResponseData(
+        queryRequest.isLoadAll(), 
+        queryRequest.getPageIndex(), 
+        queryRequest.getPageSize(), 
+        queryRequest.getMapBean(), 
+        queryRequest.getOrderBy()
+    );
+    
+    return data;
+  }
+  
+  public BizQueryResponseData build(Page<Biz> page) {
+    this.currentItemCount = page.getNumberOfElements();
+    this.pageCount = page.getTotalPages();
+    this.recordCount = page.getTotalElements();
+    this.items = page.getContent();
+
+    return this;
+  }
+
+  @Getter
+  private boolean loadAll;
+  @Getter
+  private int pageIndex;
+  @Getter
+  private int pageSize;
+  @Getter
+  private Map<String, Object> mapBean;
+  @Getter
+  private Map<String, String> orderBy;
+  
+  @Getter
+  @Setter
+  private int pageCount;
+  @Getter
+  @Setter
+  private long recordCount;
+  
+  @Getter
+  @Setter
+  private int currentItemCount;
+  
+  @Getter
+  @Setter
+  private List<Biz> items;
+  
+}
diff --git a/biz/api/src/main/java/com/supwisdom/institute/backend/biz/api/vo/response/BizRemoveResponseData.java b/biz/api/src/main/java/com/supwisdom/institute/backend/biz/api/vo/response/BizRemoveResponseData.java
new file mode 100644
index 0000000..0d3f019
--- /dev/null
+++ b/biz/api/src/main/java/com/supwisdom/institute/backend/biz/api/vo/response/BizRemoveResponseData.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.biz.api.vo.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.biz.domain.entity.Biz;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiRemoveResponseData;
+
+/**
+ * @author loie
+ */
+public class BizRemoveResponseData implements IApiRemoveResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 7920962172100289008L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  private BizRemoveResponseData() {
+
+  }
+
+  public static BizRemoveResponseData of(Biz entity) {
+    BizRemoveResponseData data = new BizRemoveResponseData();
+
+    return EntityUtils.copy(entity, data);
+  }
+
+}
diff --git a/biz/api/src/main/java/com/supwisdom/institute/backend/biz/api/vo/response/BizUpdateResponseData.java b/biz/api/src/main/java/com/supwisdom/institute/backend/biz/api/vo/response/BizUpdateResponseData.java
new file mode 100644
index 0000000..19bef62
--- /dev/null
+++ b/biz/api/src/main/java/com/supwisdom/institute/backend/biz/api/vo/response/BizUpdateResponseData.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.biz.api.vo.response;
+
+import com.supwisdom.institute.backend.biz.domain.entity.Biz;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiUpdateResponseData;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author loie
+ */
+public class BizUpdateResponseData extends Biz implements IApiUpdateResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 4502820318022107903L;
+  
+  @Getter
+  @Setter
+  private String id;
+
+  private BizUpdateResponseData() {
+    
+  }
+  
+  public static BizUpdateResponseData of(Biz entity) {
+    BizUpdateResponseData data = new BizUpdateResponseData();
+    
+    return EntityUtils.copy(entity, data);
+  }
+  
+}
diff --git a/biz/domain/pom.xml b/biz/domain/pom.xml
new file mode 100644
index 0000000..c003207
--- /dev/null
+++ b/biz/domain/pom.xml
@@ -0,0 +1,99 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.supwisdom.institute</groupId>
+    <artifactId>sw-backend-parent</artifactId>
+    <version>0.0.1</version>
+    <relativePath>../../</relativePath>
+  </parent>
+
+  <groupId>com.supwisdom.institute</groupId>
+  <artifactId>sw-backend-biz-domain</artifactId>
+  <version>0.0.1</version>
+  <packaging>jar</packaging>
+
+  <name>Supwisdom Backend Framework BIZ Domain</name>
+  <description>Supwisdom Backend Framework BIZ Domain project</description>
+
+  <dependencies>
+
+    <dependency>
+      <groupId>org.projectlombok</groupId>
+      <artifactId>lombok</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>com.supwisdom.institute</groupId>
+      <artifactId>sw-backend-common-core</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.supwisdom.institute</groupId>
+      <artifactId>sw-backend-common-utils</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.supwisdom.institute</groupId>
+      <artifactId>sw-backend-common-framework</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-data-jpa</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-release-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-failsafe-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/biz/domain/src/main/java/com/supwisdom/institute/backend/biz/domain/entity/Biz.java b/biz/domain/src/main/java/com/supwisdom/institute/backend/biz/domain/entity/Biz.java
new file mode 100644
index 0000000..553cc05
--- /dev/null
+++ b/biz/domain/src/main/java/com/supwisdom/institute/backend/biz/domain/entity/Biz.java
@@ -0,0 +1,43 @@
+package com.supwisdom.institute.backend.biz.domain.entity;
+
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.entity.ABaseEntity;
+
+@Entity
+@Table(name = "TB_BIZ")
+public class Biz extends ABaseEntity {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 5503233707196628811L;
+  
+  @Getter
+  @Setter
+  @Column(name = "NAME")
+  private String name;
+  
+  @Getter
+  @Setter
+  @Column(name = "BOOL")
+  private Boolean bool;
+  
+  @Getter
+  @Setter
+  @Column(name = "DATE")
+  private Date date;
+
+  @Getter
+  @Setter
+  @Column(name = "NUM")
+  private Integer num;
+
+}
diff --git a/biz/domain/src/main/java/com/supwisdom/institute/backend/biz/domain/exception/BizException.java b/biz/domain/src/main/java/com/supwisdom/institute/backend/biz/domain/exception/BizException.java
new file mode 100644
index 0000000..2ae5778
--- /dev/null
+++ b/biz/domain/src/main/java/com/supwisdom/institute/backend/biz/domain/exception/BizException.java
@@ -0,0 +1,12 @@
+package com.supwisdom.institute.backend.biz.domain.exception;
+
+import com.supwisdom.institute.backend.common.framework.exception.BaseException;
+
+public class BizException extends BaseException {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 8112079911386045865L;
+
+}
diff --git a/biz/domain/src/main/java/com/supwisdom/institute/backend/biz/domain/repo/BizRepository.java b/biz/domain/src/main/java/com/supwisdom/institute/backend/biz/domain/repo/BizRepository.java
new file mode 100644
index 0000000..cfeae8f
--- /dev/null
+++ b/biz/domain/src/main/java/com/supwisdom/institute/backend/biz/domain/repo/BizRepository.java
@@ -0,0 +1,114 @@
+package com.supwisdom.institute.backend.biz.domain.repo;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.domain.Sort.Order;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.stereotype.Repository;
+import org.springframework.util.StringUtils;
+
+import com.supwisdom.institute.backend.biz.domain.entity.Biz;
+import com.supwisdom.institute.backend.common.framework.repo.BaseJpaRepository;
+import com.supwisdom.institute.backend.common.util.DateUtil;
+import com.supwisdom.institute.backend.common.util.MapBeanUtils;
+
+@Repository
+public interface BizRepository extends BaseJpaRepository<Biz> {
+  
+  default Specification<Biz> convertSpecification(Map<String, Object> mapBean) {
+
+    Specification<Biz> spec = new Specification<Biz>() {
+
+      /**
+       * 
+       */
+      private static final long serialVersionUID = -1820403213133310124L;
+
+      @Override
+      public Predicate toPredicate(Root<Biz> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
+        List<Predicate> predicates = new ArrayList<>();
+        
+        if (mapBean != null) {
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "name"))) {
+            predicates.add(criteriaBuilder.like(root.get("name"), MapBeanUtils.getString(mapBean, "name")));
+          }
+
+          if (!StringUtils.isEmpty(MapBeanUtils.getBoolean(mapBean, "bool"))) {
+            predicates.add(criteriaBuilder.equal(root.get("bool"), MapBeanUtils.getBoolean(mapBean, "bool")));
+          }
+          
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "dateBegin"))) {
+            String grantTimeBegin = MapBeanUtils.getString(mapBean, "dateBegin");
+            Date d = DateUtil.parseDate(grantTimeBegin+" 00:00:00", "yyyy-MM-dd HH:mm:ss");
+            
+            if (d != null) {
+              predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("date"), d));
+            }
+          }
+
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "dateEnd"))) {
+            String grantTimeEnd = MapBeanUtils.getString(mapBean, "dateEnd");
+            Date d = DateUtil.parseDate(grantTimeEnd+" 23:59:59", "yyyy-MM-dd HH:mm:ss");
+            
+            if (d != null) {
+              predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("date"), d));
+            }
+          }
+        }
+        
+        return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
+      }
+      
+    };
+    
+    return spec;
+  }
+  
+  @Override
+  public default Page<Biz> selectPageList(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean, Map<String, String> orderBy) {
+    
+    Specification<Biz> spec = this.convertSpecification(mapBean);
+
+    if (loadAll) {
+      pageIndex = 0;
+      pageSize = Integer.MAX_VALUE;
+    }
+    
+    Sort sort = new Sort(Sort.Direction.DESC, "date");  // Sort.unsorted
+
+    if (orderBy != null) {
+      List<Order> orders = new ArrayList<>();
+      
+      orderBy.forEach((k, v) -> {
+        if ("asc".equalsIgnoreCase(v)) {
+          Order order = Order.asc(k);
+          orders.add(order);
+        } else if ("desc".equalsIgnoreCase(v)) {
+          Order order = Order.desc(k);
+          orders.add(order);
+        } else {
+          Order order = Order.by(k);
+          orders.add(order);
+        }
+      });
+      
+      sort = Sort.by(orders);
+    }
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize, sort);
+    
+    return this.findAll(spec, pageRequest);
+  }
+
+}
diff --git a/biz/domain/src/main/java/com/supwisdom/institute/backend/biz/domain/service/BizService.java b/biz/domain/src/main/java/com/supwisdom/institute/backend/biz/domain/service/BizService.java
new file mode 100644
index 0000000..43f1dd7
--- /dev/null
+++ b/biz/domain/src/main/java/com/supwisdom/institute/backend/biz/domain/service/BizService.java
@@ -0,0 +1,21 @@
+package com.supwisdom.institute.backend.biz.domain.service;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.supwisdom.institute.backend.biz.domain.entity.Biz;
+import com.supwisdom.institute.backend.biz.domain.repo.BizRepository;
+import com.supwisdom.institute.backend.common.framework.service.ABaseService;
+
+@Service
+public class BizService extends ABaseService<Biz, BizRepository> {
+  
+  @Autowired
+  private BizRepository bizRepository;
+
+  @Override
+  public BizRepository getRepo() {
+    return bizRepository;
+  }
+
+}
diff --git a/biz/pom.xml b/biz/pom.xml
new file mode 100644
index 0000000..89308d0
--- /dev/null
+++ b/biz/pom.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  
+  <parent>
+    <groupId>com.supwisdom.institute</groupId>
+    <artifactId>sw-backend-parent</artifactId>
+    <version>0.0.1</version>
+  </parent>
+  
+  <groupId>com.supwisdom.institute</groupId>
+  <artifactId>sw-backend-biz-aggregator</artifactId>
+  <version>0.0.1</version>
+  <packaging>pom</packaging>
+
+  <name>Supwisdom Backend Framework BIZ Aggregator</name>
+  <description>Supwisdom Backend Framework BIZ Aggregator project</description>
+
+  <modules>
+    <module>domain</module>
+    <module>api</module>
+  </modules>
+
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-deploy-plugin</artifactId>
+        <configuration>
+          <skip>true</skip>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/certs/jwt/jwt_private_key.pem b/certs/jwt/jwt_private_key.pem
new file mode 100644
index 0000000..0392b66
--- /dev/null
+++ b/certs/jwt/jwt_private_key.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQCgzXhvHLKypr+G+gJgOJNt8Lu8ygFFCU0eO4qJ4j2vDzpGwTOW
+KmD/u7dwIWKyHR43hUSN+FN4SSy1AmHjEKxz0btm7Cki+0YFw0BE4/mB/0wPD251
+wOS3w0CLsRTfoov9OaGaXApjVSMM74aIX8D46CbwHioLHdAj0/jlVU6gZQIDAQAB
+AoGALTcZckTjipCQsmeCW4QNEJDseT1j9gGTibi8T+Ep7xudC2AwHT11YPNrtT/t
+/J9ra7kxeaol7mDFDXDKDYsWslhGdO+JFUyPhZbizuNOcMDGB7xjyjiVRSFE7Cs/
+sZIOK+PEfetw+Q2emY6DyIy1r8S9c3a7IIvQDs/7JAJdMgECQQDTtMmnc9X2Ip0p
+mGtjtu5YBNQ6U72AVxpGNqhuw/0Cb9hcD+qXQcvXo8puOVF3HuMEdWK5b4fcLBKy
+OKe/dFzhAkEAwnI7Ytp2yLmT9w8uRI/e2OrCGyBLuTKSVvJAY1M1LLMYHRPbqwP8
+llc0/JZo9+IXvrlib+rPnvxwQ1ebXNLQBQJASvqtrOPmtIzf0rqL1j4UvhG/xISi
+ChFPWezDzK8XLcTFAR4V/Mot0srMyCr+wkyrJIoalAh/OqhVWs+cAb/JoQJAYJ0H
+mtd3m8ZxmZkmYRSnIlqKtZW3aj6RbbgSfAeU7S/TdGcwh2mEsGKGRlapszjYmYBd
+Q8SFArblROCbpmRszQJAeQdbFHWXy6OKt4zol+VEP2uLte6bXprxT0eDspx5LlZI
+MgH0yKPUgAfPyiJ7RzJa4HD9NxuNXIia2Hj+c7/MZg==
+-----END RSA PRIVATE KEY-----
diff --git a/certs/jwt/jwt_private_key_pkcs8.pem b/certs/jwt/jwt_private_key_pkcs8.pem
new file mode 100644
index 0000000..cc1e1b1
--- /dev/null
+++ b/certs/jwt/jwt_private_key_pkcs8.pem
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAKDNeG8csrKmv4b6
+AmA4k23wu7zKAUUJTR47ioniPa8POkbBM5YqYP+7t3AhYrIdHjeFRI34U3hJLLUC
+YeMQrHPRu2bsKSL7RgXDQETj+YH/TA8PbnXA5LfDQIuxFN+ii/05oZpcCmNVIwzv
+hohfwPjoJvAeKgsd0CPT+OVVTqBlAgMBAAECgYAtNxlyROOKkJCyZ4JbhA0QkOx5
+PWP2AZOJuLxP4SnvG50LYDAdPXVg82u1P+38n2truTF5qiXuYMUNcMoNixayWEZ0
+74kVTI+FluLO405wwMYHvGPKOJVFIUTsKz+xkg4r48R963D5DZ6ZjoPIjLWvxL1z
+drsgi9AOz/skAl0yAQJBANO0yadz1fYinSmYa2O27lgE1DpTvYBXGkY2qG7D/QJv
+2FwP6pdBy9ejym45UXce4wR1Yrlvh9wsErI4p790XOECQQDCcjti2nbIuZP3Dy5E
+j97Y6sIbIEu5MpJW8kBjUzUssxgdE9urA/yWVzT8lmj34he+uWJv6s+e/HBDV5tc
+0tAFAkBK+q2s4+a0jN/SuovWPhS+Eb/EhKIKEU9Z7MPMrxctxMUBHhX8yi3SyszI
+Kv7CTKskihqUCH86qFVaz5wBv8mhAkBgnQea13ebxnGZmSZhFKciWoq1lbdqPpFt
+uBJ8B5TtL9N0ZzCHaYSwYoZGVqmzONiZgF1DxIUCtuVE4JumZGzNAkB5B1sUdZfL
+o4q3jOiX5UQ/a4u17ptemvFPR4OynHkuVkgyAfTIo9SAB8/KIntHMlrgcP03G41c
+iJrYeP5zv8xm
+-----END PRIVATE KEY-----
diff --git a/certs/jwt/jwt_public_key.pem b/certs/jwt/jwt_public_key.pem
new file mode 100644
index 0000000..9892ef6
--- /dev/null
+++ b/certs/jwt/jwt_public_key.pem
@@ -0,0 +1,6 @@
+-----BEGIN PUBLIC KEY-----
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCgzXhvHLKypr+G+gJgOJNt8Lu8
+ygFFCU0eO4qJ4j2vDzpGwTOWKmD/u7dwIWKyHR43hUSN+FN4SSy1AmHjEKxz0btm
+7Cki+0YFw0BE4/mB/0wPD251wOS3w0CLsRTfoov9OaGaXApjVSMM74aIX8D46Cbw
+HioLHdAj0/jlVU6gZQIDAQAB
+-----END PUBLIC KEY-----
diff --git a/certs/jwt/readme.md b/certs/jwt/readme.md
new file mode 100644
index 0000000..0913d3f
--- /dev/null
+++ b/certs/jwt/readme.md
@@ -0,0 +1,84 @@
+# readme.md
+
+
+## 使用 openssl 生成 公私钥
+
+进入 certs/jwt 目录下
+
+1. 生成私钥 App Private Key
+
+必须为 RSA2(SHA256)
+
+```bash
+openssl genrsa -out jwt_private_key.pem 1024
+```
+
+2. 将私钥转换为 PKCS8 格式
+
+```bash
+openssl pkcs8 -topk8 -inform PEM -in jwt_private_key.pem -outform PEM -nocrypt -out jwt_private_key_pkcs8.pem
+```
+
+3. 导出公钥 App Public Key
+
+```bash
+openssl rsa -in jwt_private_key.pem -pubout -out jwt_public_key.pem
+```
+
+4. 将 jwt_public_key.pem 中的内容,去除换行和空格,转成字符串。
+
+处理前:
+```language
+-----BEGIN PUBLIC KEY-----
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBr5wUHXSlLSFU17T4wDX8ehAI
+2nnZxCc2SnpgfNwuR3jvViSVyr+Pd6JJEeMcl397qKjWqFD/CRlUSB/UEPQRxxbB
+XVlXRB289KE9xteDk04bU17ILgX8Vz/7LFRLn2CpaCSICfWENhoMRJm7xIAodrI3
+FugvRF/6jdTQis2LcQIDAQAB
+-----END PUBLIC KEY-----
+```
+处理后:
+```language
+-----BEGIN PUBLIC KEY-----
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBr5wUHXSlLSFU17T4wDX8ehAI2nnZxCc2SnpgfNwuR3jvViSVyr+Pd6JJEeMcl397qKjWqFD/CRlUSB/UEPQRxxbBXVlXRB289KE9xteDk04bU17ILgX8Vz/7LFRLn2CpaCSICfWENhoMRJm7xIAodrI3FugvRF/6jdTQis2LcQIDAQAB
+-----END PUBLIC KEY-----
+```
+
+4. 将 jwt_private_key_pkcs8.pem 中的内容,去除换行和空格,转成字符串。
+
+处理前:
+```language
+-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMGvnBQddKUtIVTX
+tPjANfx6EAjaednEJzZKemB83C5HeO9WJJXKv493okkR4xyXf3uoqNaoUP8JGVRI
+H9QQ9BHHFsFdWVdEHbz0oT3G14OTThtTXsguBfxXP/ssVEufYKloJIgJ9YQ2GgxE
+mbvEgCh2sjcW6C9EX/qN1NCKzYtxAgMBAAECgYBKBSjq7w7jCUpRuFYrMpnvMV7r
+Y0NqG/K4ZuI5+b3T2fC31v4IWQG4fIoCztky1hscUSqlTpIVxY5ujVnMm+YKMXs+
+qW2zyUdvoqUbFNAZstYatg6FQ7QlwXMDnIzlq6w5lEofsO46+0kH/d9IX+cPN0nH
+04J1UKwg0ugyjYVUAQJBAP8di+ECIJkVTbi96JWMCfK1eYdxwe+8DEd7kcW2P6qU
+/0fxP6qExkbFqPWQbJVNvOKmH5tVW5oi4Q7vaT4MzJECQQDCW4kMG7a6yBKRWZ1/
+hAixqumBv5FFCnL/yzqH6a5n8tb91vcQCwBGfu+YeQt8zVI56BTP4AJDF5KQu1vq
+kcDhAkEA+YaHu2QeSDzrEShG5obbcBaKMK1WmEqg5AX8FZrleM5VRqOztvA5Ex3f
+3ZgObJZlinYb8g2yE/fLk5UdpgBU0QJAFw+FU0p2g/L5QQXBCkBAR9RfoGV6dxam
+TnNunnG7n9nQaI35Ao5LmhG1nAHAuy4hc311+rQ5kHxbh5Czd0GUAQJBALxZpqPZ
+y7LrKmTbVLAdd0K1dQ3jWUsqk5HXwlxzrmmypn5ut41zwZQl0znyrv7XcfDZ6dqR
+hh20uoiJ/Hfky6A=
+-----END PRIVATE KEY-----
+```
+处理后:
+```language
+-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMGvnBQddKUtIVTXtPjANfx6EAjaednEJzZKemB83C5HeO9WJJXKv493okkR4xyXf3uoqNaoUP8JGVRIH9QQ9BHHFsFdWVdEHbz0oT3G14OTThtTXsguBfxXP/ssVEufYKloJIgJ9YQ2GgxEmbvEgCh2sjcW6C9EX/qN1NCKzYtxAgMBAAECgYBKBSjq7w7jCUpRuFYrMpnvMV7rY0NqG/K4ZuI5+b3T2fC31v4IWQG4fIoCztky1hscUSqlTpIVxY5ujVnMm+YKMXs+qW2zyUdvoqUbFNAZstYatg6FQ7QlwXMDnIzlq6w5lEofsO46+0kH/d9IX+cPN0nH04J1UKwg0ugyjYVUAQJBAP8di+ECIJkVTbi96JWMCfK1eYdxwe+8DEd7kcW2P6qU/0fxP6qExkbFqPWQbJVNvOKmH5tVW5oi4Q7vaT4MzJECQQDCW4kMG7a6yBKRWZ1/hAixqumBv5FFCnL/yzqH6a5n8tb91vcQCwBGfu+YeQt8zVI56BTP4AJDF5KQu1vqkcDhAkEA+YaHu2QeSDzrEShG5obbcBaKMK1WmEqg5AX8FZrleM5VRqOztvA5Ex3f3ZgObJZlinYb8g2yE/fLk5UdpgBU0QJAFw+FU0p2g/L5QQXBCkBAR9RfoGV6dxamTnNunnG7n9nQaI35Ao5LmhG1nAHAuy4hc311+rQ5kHxbh5Czd0GUAQJBALxZpqPZy7LrKmTbVLAdd0K1dQ3jWUsqk5HXwlxzrmmypn5ut41zwZQl0znyrv7XcfDZ6dqRhh20uoiJ/Hfky6A=
+-----END PRIVATE KEY-----
+```
+
+
+5. (可选)将pem内容进行 base64 编码后,配置到k8s
+
+echo -n '-----BEGIN PUBLIC KEY-----
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBr5wUHXSlLSFU17T4wDX8ehAI2nnZxCc2SnpgfNwuR3jvViSVyr+Pd6JJEeMcl397qKjWqFD/CRlUSB/UEPQRxxbBXVlXRB289KE9xteDk04bU17ILgX8Vz/7LFRLn2CpaCSICfWENhoMRJm7xIAodrI3FugvRF/6jdTQis2LcQIDAQAB
+-----END PUBLIC KEY-----' |base64
+
+
+echo -n '-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMGvnBQddKUtIVTXtPjANfx6EAjaednEJzZKemB83C5HeO9WJJXKv493okkR4xyXf3uoqNaoUP8JGVRIH9QQ9BHHFsFdWVdEHbz0oT3G14OTThtTXsguBfxXP/ssVEufYKloJIgJ9YQ2GgxEmbvEgCh2sjcW6C9EX/qN1NCKzYtxAgMBAAECgYBKBSjq7w7jCUpRuFYrMpnvMV7rY0NqG/K4ZuI5+b3T2fC31v4IWQG4fIoCztky1hscUSqlTpIVxY5ujVnMm+YKMXs+qW2zyUdvoqUbFNAZstYatg6FQ7QlwXMDnIzlq6w5lEofsO46+0kH/d9IX+cPN0nH04J1UKwg0ugyjYVUAQJBAP8di+ECIJkVTbi96JWMCfK1eYdxwe+8DEd7kcW2P6qU/0fxP6qExkbFqPWQbJVNvOKmH5tVW5oi4Q7vaT4MzJECQQDCW4kMG7a6yBKRWZ1/hAixqumBv5FFCnL/yzqH6a5n8tb91vcQCwBGfu+YeQt8zVI56BTP4AJDF5KQu1vqkcDhAkEA+YaHu2QeSDzrEShG5obbcBaKMK1WmEqg5AX8FZrleM5VRqOztvA5Ex3f3ZgObJZlinYb8g2yE/fLk5UdpgBU0QJAFw+FU0p2g/L5QQXBCkBAR9RfoGV6dxamTnNunnG7n9nQaI35Ao5LmhG1nAHAuy4hc311+rQ5kHxbh5Czd0GUAQJBALxZpqPZy7LrKmTbVLAdd0K1dQ3jWUsqk5HXwlxzrmmypn5ut41zwZQl0znyrv7XcfDZ6dqRhh20uoiJ/Hfky6A=
+-----END PRIVATE KEY-----' |base64
diff --git a/common/core/pom.xml b/common/core/pom.xml
new file mode 100644
index 0000000..82f8453
--- /dev/null
+++ b/common/core/pom.xml
@@ -0,0 +1,95 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.supwisdom.institute</groupId>
+    <artifactId>sw-backend-parent</artifactId>
+    <version>0.0.1</version>
+    <relativePath>../../</relativePath>
+  </parent>
+
+  <groupId>com.supwisdom.institute</groupId>
+  <artifactId>sw-backend-common-core</artifactId>
+  <version>0.0.1</version>
+  <packaging>jar</packaging>
+
+  <name>Supwisdom Backend Framework Common Core</name>
+  <description>Supwisdom Backend Framework Common Core project</description>
+
+  <dependencies>
+
+    <dependency>
+      <groupId>org.projectlombok</groupId>
+      <artifactId>lombok</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>javax.servlet-api</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+
+    <dependency>
+      <groupId>org.springframework.cloud</groupId>
+      <artifactId>spring-cloud-starter-openfeign</artifactId>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
+      <groupId>com.netflix.feign</groupId>
+      <artifactId>feign-httpclient</artifactId>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpclient</artifactId>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpcore</artifactId>
+      <optional>true</optional>
+    </dependency>
+    
+    <dependency>
+      <groupId>commons-codec</groupId>
+      <artifactId>commons-codec</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>com.alibaba</groupId>
+      <artifactId>fastjson</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-lang3</artifactId>
+    </dependency>
+
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-release-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/common/core/src/main/java/com/supwisdom/institute/backend/common/core/distributedlock/AbstractDistributedLockHandler.java b/common/core/src/main/java/com/supwisdom/institute/backend/common/core/distributedlock/AbstractDistributedLockHandler.java
new file mode 100644
index 0000000..8cbca67
--- /dev/null
+++ b/common/core/src/main/java/com/supwisdom/institute/backend/common/core/distributedlock/AbstractDistributedLockHandler.java
@@ -0,0 +1,30 @@
+package com.supwisdom.institute.backend.common.core.distributedlock;
+
+public abstract class AbstractDistributedLockHandler implements DistributedLockHandler {
+
+  @Override
+  public boolean lock(String key) {
+    return lock(key, TIMEOUT_MILLIS, RETRY_TIMES, SLEEP_MILLIS);
+  }
+
+  @Override
+  public boolean lock(String key, int retryTimes) {
+    return lock(key, TIMEOUT_MILLIS, retryTimes, SLEEP_MILLIS);
+  }
+
+  @Override
+  public boolean lock(String key, int retryTimes, long sleepMillis) {
+    return lock(key, TIMEOUT_MILLIS, retryTimes, sleepMillis);
+  }
+
+  @Override
+  public boolean lock(String key, long expire) {
+    return lock(key, expire, RETRY_TIMES, SLEEP_MILLIS);
+  }
+
+  @Override
+  public boolean lock(String key, long expire, int retryTimes) {
+    return lock(key, expire, retryTimes, SLEEP_MILLIS);
+  }
+
+}
diff --git a/common/core/src/main/java/com/supwisdom/institute/backend/common/core/distributedlock/DefaultDistributedLockHandler.java b/common/core/src/main/java/com/supwisdom/institute/backend/common/core/distributedlock/DefaultDistributedLockHandler.java
new file mode 100644
index 0000000..950a510
--- /dev/null
+++ b/common/core/src/main/java/com/supwisdom/institute/backend/common/core/distributedlock/DefaultDistributedLockHandler.java
@@ -0,0 +1,17 @@
+package com.supwisdom.institute.backend.common.core.distributedlock;
+
+public class DefaultDistributedLockHandler extends AbstractDistributedLockHandler {
+  
+  private ThreadLocal<String> lockFlag = new ThreadLocal<String>();
+
+  @Override
+  public boolean lock(String key, long expire, int retryTimes, long sleepMillis) {
+    return true;
+  }
+
+  @Override
+  public boolean releaseLock(String key) {
+    return true;
+  }
+
+}
diff --git a/common/core/src/main/java/com/supwisdom/institute/backend/common/core/distributedlock/DistributedLockAutoConfiguration.java b/common/core/src/main/java/com/supwisdom/institute/backend/common/core/distributedlock/DistributedLockAutoConfiguration.java
new file mode 100644
index 0000000..68df599
--- /dev/null
+++ b/common/core/src/main/java/com/supwisdom/institute/backend/common/core/distributedlock/DistributedLockAutoConfiguration.java
@@ -0,0 +1,21 @@
+package com.supwisdom.institute.backend.common.core.distributedlock;
+
+import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Slf4j
+@Configuration
+public class DistributedLockAutoConfiguration {
+  
+  @Bean
+  @ConditionalOnMissingBean(DistributedLockHandler.class)
+  public DistributedLockHandler defaultDistributedLockHandler() {
+    DistributedLockHandler defaultDistributedLockHandler = new DefaultDistributedLockHandler();
+    log.debug("DistributedLockAutoConfiguration.defaultDistributedLockHandler is {}", defaultDistributedLockHandler);
+
+    return defaultDistributedLockHandler;
+  }
+}
diff --git a/common/core/src/main/java/com/supwisdom/institute/backend/common/core/distributedlock/DistributedLockHandler.java b/common/core/src/main/java/com/supwisdom/institute/backend/common/core/distributedlock/DistributedLockHandler.java
new file mode 100644
index 0000000..e989da9
--- /dev/null
+++ b/common/core/src/main/java/com/supwisdom/institute/backend/common/core/distributedlock/DistributedLockHandler.java
@@ -0,0 +1,83 @@
+package com.supwisdom.institute.backend.common.core.distributedlock;
+
+public interface DistributedLockHandler {
+  public static final long TIMEOUT_MILLIS = 30000;
+
+  public static final int RETRY_TIMES = Integer.MIN_VALUE;
+
+  public static final long SLEEP_MILLIS = 500;
+
+  /**
+   * 获取锁<br>
+   * 超时:30000 ms (30 s) {@link DistributedLockHandler.TIMEOUT_MILLIS}<br>
+   * 重试次数:不重试 {@link DistributedLockHandler.RETRY_TIMES}<br>
+   * 重试等待:500 ms {@link DistributedLockHandler.SLEEP_MILLIS}<br>
+   * <br>
+   * @param key
+   * @return
+   */
+  public boolean lock(String key);
+
+  /**
+   * 获取锁<br>
+   * 超时:30000 ms (30 s) {@link DistributedLockHandler.TIMEOUT_MILLIS}<br>
+   * 重试等待:500 ms {@link DistributedLockHandler.SLEEP_MILLIS}<br>
+   * <br>
+   * @param key
+   * @param retryTimes 重试次数
+   * @return
+   */
+  public boolean lock(String key, int retryTimes);
+
+  /**
+   * 获取锁<br>
+   * 超时:30000 ms (30 s) {@link DistributedLockHandler.TIMEOUT_MILLIS}<br>
+   * <br>
+   * @param key
+   * @param retryTimes 重试次数
+   * @param sleepMillis 重试等待 单位 ms
+   * @return
+   */
+  public boolean lock(String key, int retryTimes, long sleepMillis);
+
+  /**
+   * 获取锁<br>
+   * 重试次数:不重试 {@link DistributedLockHandler.RETRY_TIMES}<br>
+   * 重试等待:500 ms {@link DistributedLockHandler.SLEEP_MILLIS}<br>
+   * <br>
+   * @param key
+   * @param expire 超时 单位 ms
+   * @return
+   */
+  public boolean lock(String key, long expire);
+
+  /**
+   * 获取锁<br>
+   * 重试等待:500 ms {@link DistributedLockHandler.SLEEP_MILLIS}<br>
+   * <br>
+   * @param key
+   * @param retryTimes 重试次数
+   * @param expire 超时 单位 ms
+   * @return
+   */
+  public boolean lock(String key, long expire, int retryTimes);
+
+  /**
+   * 获取锁<br>
+   * <br>
+   * @param key
+   * @param expire 超时 单位 ms
+   * @param retryTimes 重试次数
+   * @param sleepMillis 重试等待 单位 ms
+   * @return
+   */
+  public boolean lock(String key, long expire, int retryTimes, long sleepMillis);
+
+  /**
+   * 释放锁<br>
+   * <br>
+   * @param key
+   * @return
+   */
+  public boolean releaseLock(String key);
+}
diff --git a/common/core/src/main/java/com/supwisdom/institute/backend/common/core/distributedlock/EnableDistributedLock.java b/common/core/src/main/java/com/supwisdom/institute/backend/common/core/distributedlock/EnableDistributedLock.java
new file mode 100644
index 0000000..89053c9
--- /dev/null
+++ b/common/core/src/main/java/com/supwisdom/institute/backend/common/core/distributedlock/EnableDistributedLock.java
@@ -0,0 +1,17 @@
+package com.supwisdom.institute.backend.common.core.distributedlock;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.springframework.context.annotation.Import;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Import({DistributedLockAutoConfiguration.class})
+public @interface EnableDistributedLock {
+
+}
diff --git a/common/core/src/main/java/com/supwisdom/institute/backend/common/core/distributedlock/aop/DistributedLock.java b/common/core/src/main/java/com/supwisdom/institute/backend/common/core/distributedlock/aop/DistributedLock.java
new file mode 100644
index 0000000..52c2485
--- /dev/null
+++ b/common/core/src/main/java/com/supwisdom/institute/backend/common/core/distributedlock/aop/DistributedLock.java
@@ -0,0 +1,36 @@
+package com.supwisdom.institute.backend.common.core.distributedlock.aop;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface DistributedLock {
+
+  /** 锁的资源,redis的key */
+  String value() default "default";
+
+  /** 持锁时间,单位毫秒 */
+  long keepMills() default 30000;
+
+  /** 当获取失败时候动作 */
+  LockFailAction action() default LockFailAction.CONTINUE;
+
+  public enum LockFailAction {
+    /** 放弃 */
+    GIVEUP,
+    /** 继续 */
+    CONTINUE;
+  }
+
+  /** 重试的间隔时间,设置GIVEUP忽略此项 */
+  long sleepMills() default 200;
+
+  /** 重试次数 */
+  int retryTimes() default 5;
+
+}
diff --git a/common/core/src/main/java/com/supwisdom/institute/backend/common/core/distributedlock/aop/DistributedLockAspectConfiguration.java b/common/core/src/main/java/com/supwisdom/institute/backend/common/core/distributedlock/aop/DistributedLockAspectConfiguration.java
new file mode 100644
index 0000000..fb67688
--- /dev/null
+++ b/common/core/src/main/java/com/supwisdom/institute/backend/common/core/distributedlock/aop/DistributedLockAspectConfiguration.java
@@ -0,0 +1,67 @@
+package com.supwisdom.institute.backend.common.core.distributedlock.aop;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.util.StringUtils;
+
+import com.supwisdom.institute.backend.common.core.distributedlock.DistributedLockAutoConfiguration;
+import com.supwisdom.institute.backend.common.core.distributedlock.DistributedLockHandler;
+import com.supwisdom.institute.backend.common.core.distributedlock.aop.DistributedLock.LockFailAction;
+
+@Aspect
+@Configuration
+@ConditionalOnClass({ProceedingJoinPoint.class, DistributedLockHandler.class})
+@AutoConfigureAfter(DistributedLockAutoConfiguration.class)
+public class DistributedLockAspectConfiguration {
+  
+  private final Logger logger = LoggerFactory.getLogger(DistributedLockAspectConfiguration.class);
+  
+  @Autowired
+  private DistributedLockHandler distributedLockHandler;
+
+  @Pointcut("@annotation(com.supwisdom.institute.backend.common.core.distributedlock.aop.DistributedLock)")
+  private void lockPoint(){
+    
+  }
+  
+  @Around("lockPoint()")
+  public Object around(ProceedingJoinPoint pjp) throws Throwable{
+    Method method = ((MethodSignature) pjp.getSignature()).getMethod();
+    DistributedLock distributedLockAnnotation = method.getAnnotation(DistributedLock.class);
+    String key = distributedLockAnnotation.value();
+    if(StringUtils.isEmpty(key)){
+      Object[] args = pjp.getArgs();
+      key = Arrays.toString(args);
+    }
+    int retryTimes = distributedLockAnnotation.action().equals(LockFailAction.CONTINUE) ? distributedLockAnnotation.retryTimes() : 0;
+    boolean lock = distributedLockHandler.lock(key, distributedLockAnnotation.keepMills(), retryTimes, distributedLockAnnotation.sleepMills());
+    if(!lock) {
+      logger.debug("get lock failed : " + key);
+      return null;
+    }
+    
+    //得到锁,执行方法,释放锁
+    logger.debug("get lock success : " + key);
+    try {
+      return pjp.proceed();
+    } catch (Exception e) {
+      logger.error("execute locked method occured an exception", e);
+    } finally {
+      boolean releaseResult = distributedLockHandler.releaseLock(key);
+      logger.debug("release lock : " + key + (releaseResult ? " success" : " failed"));
+    }
+    return null;
+  }
+}
\ No newline at end of file
diff --git a/common/core/src/main/java/com/supwisdom/institute/backend/common/core/feign/FeignClientBuilder.java b/common/core/src/main/java/com/supwisdom/institute/backend/common/core/feign/FeignClientBuilder.java
new file mode 100644
index 0000000..d970094
--- /dev/null
+++ b/common/core/src/main/java/com/supwisdom/institute/backend/common/core/feign/FeignClientBuilder.java
@@ -0,0 +1,86 @@
+package com.supwisdom.institute.backend.common.core.feign;
+
+import javax.net.ssl.SSLContext;
+
+import lombok.extern.slf4j.Slf4j;
+
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.conn.HttpClientConnectionManager;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.apache.http.ssl.SSLContexts;
+import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
+import org.springframework.util.ResourceUtils;
+
+import feign.httpclient.ApacheHttpClient;
+
+@Slf4j
+@lombok.Builder
+public class FeignClientBuilder {
+  
+  private final boolean enabled;
+  
+  private String keyPassword;
+  private String keyStore;
+  private String keyStorePassword;
+  private String trustStore;
+  private String trustStorePassword;
+  
+  public ApacheHttpClient apacheHttpClient(
+      ApacheHttpClientFactory httpClientFactory,
+      HttpClientConnectionManager httpClientConnectionManager) {
+
+    if (!enabled) {
+      return new ApacheHttpClient();
+    }
+    
+    if (keyStore == null || keyStore.isEmpty()) {
+      return new ApacheHttpClient();
+    } else {
+      try {
+        SSLContextBuilder sslContextBuilder = SSLContexts.custom();
+        if (trustStore == null || trustStore.isEmpty()) {
+        } else {
+          sslContextBuilder
+//            .loadTrustMaterial(TrustAllStrategy.INSTANCE)
+            .loadTrustMaterial(
+                ResourceUtils.getFile(trustStore),
+                trustStorePassword.toCharArray()
+            );
+        }
+        
+        sslContextBuilder
+          .loadKeyMaterial(
+              ResourceUtils.getFile(keyStore),
+              keyStorePassword.toCharArray(),
+              keyPassword.toCharArray());
+        
+        SSLContext sslContext = sslContextBuilder.build();
+        
+        //SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
+        
+        RequestConfig defaultRequestConfig = RequestConfig.custom()
+            .setConnectTimeout(12000)
+            .setRedirectsEnabled(true)
+            .build();
+
+        CloseableHttpClient httpClient = httpClientFactory.createBuilder().
+            setSSLContext(sslContext).
+            setSSLHostnameVerifier(new NoopHostnameVerifier()).   // FIXME: HostnameVerifier
+            setConnectionManager(httpClientConnectionManager).
+            setDefaultRequestConfig(defaultRequestConfig).build();
+        
+
+        ApacheHttpClient trustSSLSockets = new ApacheHttpClient(httpClient);
+        log.info("feignClient load with ssl.");
+        return trustSSLSockets;
+      } catch (Exception e) {
+        e.printStackTrace();
+      }
+    }
+    
+    return new ApacheHttpClient();
+  }
+
+}
diff --git a/common/core/src/main/java/com/supwisdom/institute/backend/common/core/transmit/annotation/EnableSimpleUserTransmit.java b/common/core/src/main/java/com/supwisdom/institute/backend/common/core/transmit/annotation/EnableSimpleUserTransmit.java
new file mode 100644
index 0000000..4538483
--- /dev/null
+++ b/common/core/src/main/java/com/supwisdom/institute/backend/common/core/transmit/annotation/EnableSimpleUserTransmit.java
@@ -0,0 +1,20 @@
+package com.supwisdom.institute.backend.common.core.transmit.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.springframework.context.annotation.Import;
+
+import com.supwisdom.institute.backend.common.core.transmit.config.SimpleUserTransmitFeignConfiguration;
+import com.supwisdom.institute.backend.common.core.transmit.config.SimpleUserTransmitFilterConfiguration;
+
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Import({SimpleUserTransmitFilterConfiguration.class, SimpleUserTransmitFeignConfiguration.class})
+public @interface EnableSimpleUserTransmit {
+
+}
diff --git a/common/core/src/main/java/com/supwisdom/institute/backend/common/core/transmit/config/SimpleUserTransmitFeignConfiguration.java b/common/core/src/main/java/com/supwisdom/institute/backend/common/core/transmit/config/SimpleUserTransmitFeignConfiguration.java
new file mode 100644
index 0000000..509f48a
--- /dev/null
+++ b/common/core/src/main/java/com/supwisdom/institute/backend/common/core/transmit/config/SimpleUserTransmitFeignConfiguration.java
@@ -0,0 +1,23 @@
+package com.supwisdom.institute.backend.common.core.transmit.config;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import com.supwisdom.institute.backend.common.core.transmit.feign.SimpleUserTransmitRequestInterceptor;
+
+import feign.Feign;
+import lombok.extern.slf4j.Slf4j;
+
+@Configuration
+@ConditionalOnClass(Feign.class)
+@Slf4j
+public class SimpleUserTransmitFeignConfiguration {
+  
+  @Bean
+  public SimpleUserTransmitRequestInterceptor simpleUserTransmitRequestInterceptor() {
+    log.debug("-----SimpleUserTransmitRequestInterceptor");
+    return new SimpleUserTransmitRequestInterceptor();
+  }
+  
+}
diff --git a/common/core/src/main/java/com/supwisdom/institute/backend/common/core/transmit/config/SimpleUserTransmitFilterConfiguration.java b/common/core/src/main/java/com/supwisdom/institute/backend/common/core/transmit/config/SimpleUserTransmitFilterConfiguration.java
new file mode 100644
index 0000000..10a96a6
--- /dev/null
+++ b/common/core/src/main/java/com/supwisdom/institute/backend/common/core/transmit/config/SimpleUserTransmitFilterConfiguration.java
@@ -0,0 +1,20 @@
+package com.supwisdom.institute.backend.common.core.transmit.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import com.supwisdom.institute.backend.common.core.transmit.filter.SimpleUserTransmitFilter;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Configuration
+public class SimpleUserTransmitFilterConfiguration {
+  
+  @Bean
+  public SimpleUserTransmitFilter simpleUserTransmitFilter() {
+    log.debug("-----SimpleUserTransmitFilter");
+    return new SimpleUserTransmitFilter();
+  }
+  
+}
diff --git a/common/core/src/main/java/com/supwisdom/institute/backend/common/core/transmit/feign/SimpleUserTransmitRequestInterceptor.java b/common/core/src/main/java/com/supwisdom/institute/backend/common/core/transmit/feign/SimpleUserTransmitRequestInterceptor.java
new file mode 100644
index 0000000..a09322f
--- /dev/null
+++ b/common/core/src/main/java/com/supwisdom/institute/backend/common/core/transmit/feign/SimpleUserTransmitRequestInterceptor.java
@@ -0,0 +1,35 @@
+package com.supwisdom.institute.backend.common.core.transmit.feign;
+
+import lombok.extern.slf4j.Slf4j;
+
+import org.apache.commons.codec.binary.Base64;
+
+import com.alibaba.fastjson.JSONObject;
+import com.supwisdom.institute.backend.common.core.transmit.user.User;
+import com.supwisdom.institute.backend.common.core.transmit.user.UserContext;
+
+import feign.RequestInterceptor;
+import feign.RequestTemplate;
+
+@Slf4j
+public class SimpleUserTransmitRequestInterceptor implements RequestInterceptor {
+
+  @Override
+  public void apply(RequestTemplate template) {
+    User user = UserContext.getUser();
+    if (user != null) {
+      try {
+        String jsonUser = JSONObject.toJSONString(user);
+        
+        //String headerValue = new String(URLDecoder.decode(jsonUser,"UTF-8"));
+        String headerValue = Base64.encodeBase64URLSafeString(jsonUser.getBytes("UTF-8"));
+        
+        template.header(UserContext.KEY_USER_IN_HTTP_HEADER, headerValue);
+        log.debug("User set to feign header: ok");
+      } catch (Exception e) {
+        log.warn("User set to feign header: error", e);
+      }
+    }
+  }
+
+}
diff --git a/common/core/src/main/java/com/supwisdom/institute/backend/common/core/transmit/filter/SimpleUserTransmitFilter.java b/common/core/src/main/java/com/supwisdom/institute/backend/common/core/transmit/filter/SimpleUserTransmitFilter.java
new file mode 100644
index 0000000..644e4f6
--- /dev/null
+++ b/common/core/src/main/java/com/supwisdom/institute/backend/common/core/transmit/filter/SimpleUserTransmitFilter.java
@@ -0,0 +1,60 @@
+package com.supwisdom.institute.backend.common.core.transmit.filter;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang3.StringUtils;
+
+import com.alibaba.fastjson.JSON;
+import com.supwisdom.institute.backend.common.core.transmit.user.User;
+import com.supwisdom.institute.backend.common.core.transmit.user.UserContext;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class SimpleUserTransmitFilter implements Filter {
+
+  @Override
+  public void init(FilterConfig filterConfig) throws ServletException {
+    
+  }
+
+  @Override
+  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+    HttpServletRequest request = (HttpServletRequest) servletRequest;
+    
+    String headerValue = request.getHeader(UserContext.KEY_USER_IN_HTTP_HEADER);
+    log.debug("Header {} is: {}", UserContext.KEY_USER_IN_HTTP_HEADER, headerValue);
+    if (StringUtils.isNotBlank(headerValue)) {
+      try {
+        //String jsonUser = URLDecoder.decode(headerValue,"UTF-8");
+        String jsonUser = new String(Base64.decodeBase64(headerValue), "UTF-8");
+        log.debug("jsonUser is: {}", jsonUser);
+        
+        User user = JSON.parseObject(jsonUser, User.class);
+        log.debug("User is: {}", user);
+        
+        UserContext.setUser(user);
+        log.debug("User get from header: ok");
+      } catch (Exception e) {
+        log.warn("User get from header: error", e);
+      }
+    }
+    
+    filterChain.doFilter(servletRequest, servletResponse);
+  }
+
+  @Override
+  public void destroy() {
+    
+  }
+
+}
diff --git a/common/core/src/main/java/com/supwisdom/institute/backend/common/core/transmit/user/User.java b/common/core/src/main/java/com/supwisdom/institute/backend/common/core/transmit/user/User.java
new file mode 100644
index 0000000..4b24d52
--- /dev/null
+++ b/common/core/src/main/java/com/supwisdom/institute/backend/common/core/transmit/user/User.java
@@ -0,0 +1,19 @@
+package com.supwisdom.institute.backend.common.core.transmit.user;
+
+import java.util.List;
+import java.util.Map;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@Data
+@AllArgsConstructor
+public class User {
+  
+  private String username;
+  
+  private List<String> roles;
+  
+  private Map<String, Object> attributes;
+
+}
diff --git a/common/core/src/main/java/com/supwisdom/institute/backend/common/core/transmit/user/UserContext.java b/common/core/src/main/java/com/supwisdom/institute/backend/common/core/transmit/user/UserContext.java
new file mode 100644
index 0000000..13f4d25
--- /dev/null
+++ b/common/core/src/main/java/com/supwisdom/institute/backend/common/core/transmit/user/UserContext.java
@@ -0,0 +1,33 @@
+package com.supwisdom.institute.backend.common.core.transmit.user;
+
+public class UserContext {
+  
+  private static ThreadLocal<User> user = new ThreadLocal<User>();
+  
+  public static String KEY_USER_IN_HTTP_HEADER = "X-FORWARD-USER";
+  
+  private UserContext() {
+    
+  }
+  
+  public static User getUser() {
+    return user.get();
+  }
+  
+  public static void setUser(User value) {
+    user.set(value);
+  }
+  
+  
+  
+  public static String getUsername() {
+    User u = user.get();
+    
+    if (u != null) {
+      return u.getUsername();
+    }
+    
+    return null;
+  }
+
+}
diff --git a/common/core/src/main/resources/META-INF/spring.factories b/common/core/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..b94673d
--- /dev/null
+++ b/common/core/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,4 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+  com.supwisdom.institute.backend.common.core.distributedlock.DistributedLockAutoConfiguration,\
+  com.supwisdom.institute.backend.common.core.distributedlock.aop.DistributedLockAspectConfiguration
+
diff --git a/common/framework/pom.xml b/common/framework/pom.xml
new file mode 100644
index 0000000..60ff51c
--- /dev/null
+++ b/common/framework/pom.xml
@@ -0,0 +1,109 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.supwisdom.institute</groupId>
+    <artifactId>sw-backend-parent</artifactId>
+    <version>0.0.1</version>
+    <relativePath>../../</relativePath>
+  </parent>
+
+  <groupId>com.supwisdom.institute</groupId>
+  <artifactId>sw-backend-common-framework</artifactId>
+  <version>0.0.1</version>
+  <packaging>jar</packaging>
+
+  <name>Supwisdom Backend Framework Common Framework</name>
+  <description>Supwisdom Backend Framework Common Framework project</description>
+
+  <dependencies>
+
+    <dependency>
+      <groupId>org.projectlombok</groupId>
+      <artifactId>lombok</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>com.supwisdom.institute</groupId>
+      <artifactId>sw-backend-common-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.supwisdom.institute</groupId>
+      <artifactId>sw-backend-common-utils</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-lang3</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-web</artifactId>
+      <optional>true</optional>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-aop</artifactId>
+      <optional>true</optional>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-data-jpa</artifactId>
+      <optional>true</optional>
+    </dependency>
+    
+    
+    <!-- <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-data-redis</artifactId>
+      <optional>true</optional>
+    </dependency> -->
+    <dependency>
+      <groupId>org.springframework.data</groupId>
+      <artifactId>spring-data-redis</artifactId>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
+      <groupId>redis.clients</groupId>
+      <artifactId>jedis</artifactId>
+      <optional>true</optional>
+    </dependency>
+
+
+    <dependency>
+      <groupId>io.springfox</groupId>
+      <artifactId>springfox-swagger2</artifactId>
+    </dependency>
+
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-release-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/distributedlock/RedisDistributedLockAutoConfiguration.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/distributedlock/RedisDistributedLockAutoConfiguration.java
new file mode 100644
index 0000000..edf8505
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/distributedlock/RedisDistributedLockAutoConfiguration.java
@@ -0,0 +1,33 @@
+package com.supwisdom.institute.backend.common.framework.distributedlock;
+
+import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.core.RedisTemplate;
+
+import redis.clients.jedis.Jedis;
+
+import com.supwisdom.institute.backend.common.core.distributedlock.DistributedLockAutoConfiguration;
+import com.supwisdom.institute.backend.common.core.distributedlock.DistributedLockHandler;
+
+@Slf4j
+@Configuration
+@ConditionalOnClass({ Jedis.class })
+@AutoConfigureAfter(RedisAutoConfiguration.class)
+@AutoConfigureBefore(DistributedLockAutoConfiguration.class)
+public class RedisDistributedLockAutoConfiguration {
+
+  @Bean
+  public DistributedLockHandler redisDistributedLockHandler(RedisTemplate<Object, Object> redisTemplate) {
+    DistributedLockHandler redisDistributedLockHandler = new RedisDistributedLockHandler(redisTemplate);
+    log.debug("RedisDistributedLockAutoConfiguration.redisDistributedLockHandler is {}", redisDistributedLockHandler);
+    
+    return redisDistributedLockHandler;
+  }
+
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/distributedlock/RedisDistributedLockHandler.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/distributedlock/RedisDistributedLockHandler.java
new file mode 100644
index 0000000..59afa38
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/distributedlock/RedisDistributedLockHandler.java
@@ -0,0 +1,117 @@
+package com.supwisdom.institute.backend.common.framework.distributedlock;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.dao.DataAccessException;
+import org.springframework.data.redis.connection.RedisConnection;
+import org.springframework.data.redis.core.RedisCallback;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.util.StringUtils;
+
+import com.supwisdom.institute.backend.common.core.distributedlock.AbstractDistributedLockHandler;
+
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.JedisCluster;
+import redis.clients.jedis.JedisCommands;
+
+public class RedisDistributedLockHandler extends AbstractDistributedLockHandler {
+
+  private final Logger logger = LoggerFactory.getLogger(RedisDistributedLockHandler.class);
+
+  private RedisTemplate<Object, Object> redisTemplate;
+
+  private ThreadLocal<String> lockFlag = new ThreadLocal<String>();
+
+  public static final String UNLOCK_LUA;
+
+  static {
+    StringBuilder sb = new StringBuilder();
+    sb.append("if redis.call(\"get\",KEYS[1]) == ARGV[1] ");
+    sb.append("then ");
+    sb.append("    return redis.call(\"del\",KEYS[1]) ");
+    sb.append("else ");
+    sb.append("    return 0 ");
+    sb.append("end ");
+    UNLOCK_LUA = sb.toString();
+  }
+
+  public RedisDistributedLockHandler(RedisTemplate<Object, Object> redisTemplate) {
+    super();
+    this.redisTemplate = redisTemplate;
+  }
+
+  @Override
+  public boolean lock(String key, long expire, int retryTimes, long sleepMillis) {
+    boolean result = setRedis(key, expire);
+    // 如果获取锁失败,按照传入的重试次数进行重试
+    while ((!result) && retryTimes-- > 0) {
+      try {
+        logger.debug("lock failed, retrying..." + retryTimes);
+        Thread.sleep(sleepMillis);
+      } catch (InterruptedException e) {
+        return false;
+      }
+      result = setRedis(key, expire);
+    }
+    return result;
+  }
+
+  private boolean setRedis(String key, long expire) {
+    try {
+      String result = redisTemplate.execute(new RedisCallback<String>() {
+        @Override
+        public String doInRedis(RedisConnection connection) throws DataAccessException {
+          JedisCommands commands = (JedisCommands) connection.getNativeConnection();
+          String uuid = UUID.randomUUID().toString();
+          lockFlag.set(uuid);
+          return commands.set(key, uuid, "NX", "PX", expire);
+        }
+      });
+      return !StringUtils.isEmpty(result);
+    } catch (Exception e) {
+      logger.error("set redis occured an exception", e);
+    }
+    return false;
+  }
+
+  @Override
+  public boolean releaseLock(String key) {
+    // 释放锁的时候,有可能因为持锁之后方法执行时间大于锁的有效期,此时有可能已经被另外一个线程持有锁,所以不能直接删除
+    try {
+      List<String> keys = new ArrayList<String>();
+      keys.add(key);
+      List<String> args = new ArrayList<String>();
+      args.add(lockFlag.get());
+
+      // 使用lua脚本删除redis中匹配value的key,可以避免由于方法执行时间过长而redis锁自动过期失效的时候误删其他线程的锁
+      // spring自带的执行脚本方法中,集群模式直接抛出不支持执行脚本的异常,所以只能拿到原redis的connection来执行脚本
+
+      Long result = redisTemplate.execute(new RedisCallback<Long>() {
+        public Long doInRedis(RedisConnection connection) throws DataAccessException {
+          Object nativeConnection = connection.getNativeConnection();
+          // 集群模式和单机模式虽然执行脚本的方法一样,但是没有共同的接口,所以只能分开执行
+          // 集群模式
+          if (nativeConnection instanceof JedisCluster) {
+            return (Long) ((JedisCluster) nativeConnection).eval(UNLOCK_LUA, keys, args);
+          }
+
+          // 单机模式
+          else if (nativeConnection instanceof Jedis) {
+            return (Long) ((Jedis) nativeConnection).eval(UNLOCK_LUA, keys, args);
+          }
+          return 0L;
+        }
+      });
+
+      return result != null && result > 0;
+    } catch (Exception e) {
+      logger.error("release lock occured an exception", e);
+    }
+    return false;
+  }
+
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/entity/ABaseEntity.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/entity/ABaseEntity.java
new file mode 100644
index 0000000..3c14c25
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/entity/ABaseEntity.java
@@ -0,0 +1,79 @@
+package com.supwisdom.institute.backend.common.framework.entity;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.Id;
+import javax.persistence.MappedSuperclass;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@MappedSuperclass
+public abstract class ABaseEntity implements Serializable {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 2613136930230449335L;
+
+  @Id
+  @Column(name = "ID")
+  private String id;
+
+  /**
+   * 获取主键
+   */
+  public String getId() {
+    return id;
+  }
+
+  /**
+   * 设置ID属性,主要用于人工指定键值
+   */
+  public void setId(String id) {
+    this.id = id;
+  }
+
+  @Getter
+  @Setter
+  @Column(name = "COMPANY_ID", nullable = true)
+  private String companyId = null;
+
+  @Getter
+  @Setter
+  @Column(name = "DELETED")
+  private Boolean deleted = false;
+
+  @Getter
+  @Setter
+  @Column(name = "ADD_ACCOUNT", nullable = true)
+  private String addAccount = null;
+
+  @Getter
+  @Setter
+  @Column(name = "ADD_TIME", nullable = true)
+  private Date addTime = null;
+
+  @Getter
+  @Setter
+  @Column(name = "EDIT_ACCOUNT", nullable = true)
+  private String editAccount = null;
+
+  @Getter
+  @Setter
+  @Column(name = "EDIT_TIME", nullable = true)
+  private Date editTime = null;
+
+  @Getter
+  @Setter
+  @Column(name = "DELETE_ACCOUNT", nullable = true)
+  private String deleteAccount = null;
+
+  @Getter
+  @Setter
+  @Column(name = "DELETE_TIME", nullable = true)
+  private Date deleteTime = null;
+
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/entity/EntityUtils.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/entity/EntityUtils.java
new file mode 100644
index 0000000..b1363fb
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/entity/EntityUtils.java
@@ -0,0 +1,238 @@
+package com.supwisdom.institute.backend.common.framework.entity;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+import java.util.TreeSet;
+import java.util.stream.Collectors;
+
+import javax.persistence.Column;
+import javax.persistence.Id;
+
+import com.supwisdom.institute.backend.common.util.ReflectUtils;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * 对 entity 的操作 如:复制、合并、转换等
+ * 
+ * @author loie
+ *
+ */
+public class EntityUtils {
+  
+
+  public static <T extends ABaseEntity> List<T> distinctList(List<T> list) {
+    return list.stream()
+        .collect(
+            Collectors.collectingAndThen(
+                Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(T::getId))), ArrayList<T>::new
+            )
+        );
+  }
+
+  /**
+   * 合并 domain 中带有{@link Column}注解的字段值, 将 newEntity 中值为null的字段,使用 oldEntity 中的值
+   * 进行覆盖
+   * 
+   * @param oldEntity
+   *          ,覆盖的实体
+   * @param newEntity
+   *          ,待覆盖的实体
+   * @return 合并后的newEntity
+   */
+  public static <T extends ABaseEntity> T merge(T oldEntity, T newEntity) {
+
+    for (Class<?> clazz = oldEntity.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {
+      for (Field field : clazz.getDeclaredFields()) {
+        Column[] annotations = field.getAnnotationsByType(Column.class);
+        if (annotations == null || annotations.length == 0) {
+          Id[] idAnnotations = field.getAnnotationsByType(Id.class);
+          if (idAnnotations == null || idAnnotations.length == 0) {
+            continue;
+          }
+        }
+
+        String fieldName = field.getName();
+        Object newFieldValue = ReflectUtils.getFieldValue(newEntity, fieldName);
+
+        if (newFieldValue == null) {
+          Object oldFieldValue = ReflectUtils.getFieldValue(oldEntity, fieldName);
+          ReflectUtils.setFieldValue(newEntity, fieldName, oldFieldValue,field.getType());
+        }
+      }
+    }
+
+    return newEntity;
+  }
+
+  public static <S, T> T copy(S sourceEntity, T targetEntity) {
+    
+    for (Class<?> clazz = targetEntity.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {
+      for (Field field : clazz.getDeclaredFields()) {
+        
+//      Column[] annotations = field.getAnnotationsByType(Column.class);
+//      if (annotations == null || annotations.length == 0) {
+//        Id[] idAnnotations = field.getAnnotationsByType(Id.class);
+//        if (idAnnotations == null || idAnnotations.length == 0) {
+//          continue;
+//        }
+//      }
+        
+        if (Modifier.isStatic(field.getModifiers())) {
+          continue;
+        }
+
+        String fieldName = field.getName();
+        
+        if(fieldName.equals("serialVersionUID")){
+          continue;
+        }
+        if (!ReflectUtils.existField(sourceEntity, fieldName)) {
+          continue;
+        }
+        
+        Object sFieldValue = ReflectUtils.getFieldValue(sourceEntity, fieldName);
+
+        if (sFieldValue != null) {
+          ReflectUtils.setFieldValue(targetEntity, fieldName, sFieldValue, field.getType());
+        }
+      }
+    }
+
+    return targetEntity;
+  }
+
+  public static <F, C> C fatherToChild (F father, C child){
+    for (Class<?> clazz = child.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {
+      for (Field field : clazz.getDeclaredFields()) {
+        if (Modifier.isStatic(field.getModifiers())) {
+          continue;
+        }
+        
+        String fieldName = field.getName();
+        if(fieldName.equals("serialVersionUID")){
+          continue;
+        }
+        if (!ReflectUtils.existField(father, fieldName)) {
+          continue;
+        }
+        
+        Object sFieldValue = ReflectUtils.getFieldValue(father, fieldName);
+
+        if (sFieldValue != null) {
+          ReflectUtils.setFieldValue(child, fieldName, sFieldValue, field.getType());
+        }
+      }
+    }
+
+    return child;
+  }
+
+    public static void main(String[] args) {
+
+    Test target0 = new Test();
+    target0.setId("id0");
+    target0.setCode("code");
+    target0.setName("name");
+    target0.setDate(new Date());
+    target0.setEnabled(false);
+    target0.setStatus(1);
+
+    System.out.println("target0 == " + target0.toString());
+    System.out.println();
+
+    Test source1 = new Test();
+    // source1.setId("id1");
+    source1.setCode("code1");
+    // source1.setName("name");
+    // source1.setDate(new Date());
+    source1.setEnabled(true);
+    // source1.setStatus(1);
+    System.out.println("source1 == " + source1.toString());
+
+    Test target1 = EntityUtils.merge(source1, target0);
+    System.out.println("target0 == " + target0.toString());
+    System.out.println("target1 == " + target1.toString());
+    System.out.println();
+
+    Test source2 = new Test();
+    // source2.setId("id2");
+    source2.setCode("code2");
+    source2.setName("name2");
+    // source2.setDate(new Date());
+    // source2.setEnabled(true);
+    source2.setStatus(2);
+    System.out.println("source2 == " + source2.toString());
+
+    Test target2 = EntityUtils.merge(source2, target0);
+    System.out.println("target0 == " + target0.toString());
+    System.out.println("target2 == " + target2.toString());
+    System.out.println();
+
+
+    Test test = new Test();
+    test.setId("id0");
+    test.setCode("code");
+    test.setName("name");
+    test.setDate(new Date());
+    test.setEnabled(false);
+    test.setStatus(1);
+
+    Test2 test2 = new Test2();
+    test2 = EntityUtils.copy(test, test2);
+    System.out.println("test    == " + test.toString());
+    System.out.println("test2   == " + test2.toString());
+    System.out.println();
+
+  }
+  
+  @Getter
+  @Setter
+  @ToString
+  public static class Test extends ABaseEntity {
+    
+    /**
+     * 
+     */
+    private static final long serialVersionUID = -8348781653151879484L;
+    
+    @Column
+    private String code = null;
+    @Column
+    private String name = null;
+    @Column
+    private Date date = null;
+    @Column
+    private Boolean enabled = null;
+    @Column
+    private Integer status = null;
+    
+  }
+  
+  public static class Test2 extends ABaseEntity {
+
+    /**
+     * 
+     */
+    private static final long serialVersionUID = -5565959639168005384L;
+    
+    @Column
+    private String name = null;
+    @Column
+    private String memo = null;
+    @Column
+    private Date date = null;
+    @Column
+    private Boolean enabled = null;
+    @Column
+    private Integer status = null;
+
+  }
+
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/exception/BaseException.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/exception/BaseException.java
new file mode 100644
index 0000000..9e13ed0
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/exception/BaseException.java
@@ -0,0 +1,81 @@
+package com.supwisdom.institute.backend.common.framework.exception;
+
+public class BaseException extends RuntimeException {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 2278568118369300446L;
+  
+  public static final int DEFAULT_CODE = -1;
+
+  /**
+   * 具体异常码
+   */
+  protected final int code;
+  
+  private final String messageCode;
+
+  @Deprecated
+  public BaseException(int code, String msgFormat, Object... args) {
+      super(String.format(msgFormat, args));
+      this.code = code;
+      this.messageCode = null;
+  }
+  
+  public BaseException(int code, String messageCode, String msgFormat, Object... args) {
+    super(String.format(msgFormat, args));
+    this.code = code;
+    this.messageCode = messageCode;
+}
+
+  public BaseException() {
+    super();
+    this.code = DEFAULT_CODE;
+    this.messageCode = null;
+  }
+
+  public BaseException(String message, Throwable cause) {
+      super(message, cause);
+      this.code = DEFAULT_CODE;
+      this.messageCode = null;
+  }
+
+  public BaseException(Throwable cause) {
+      super(cause);
+      this.code = DEFAULT_CODE;
+      this.messageCode = null;
+  }
+
+  public BaseException(String message) {
+      super(message);
+      this.code = DEFAULT_CODE;
+      this.messageCode = null;
+  }
+
+  public int getCode() {
+      return code;
+  }
+
+  /**
+   * 实例化异常
+   * 
+   * @param msgFormat
+   * @param args
+   * @return
+   */
+  @Deprecated
+  public static BaseException newInstance(String msgFormat, Object... args) {
+      return new BaseException(DEFAULT_CODE, msgFormat, args);
+  }
+  
+  @Deprecated
+  public static BaseException newInstance(int code, String msgFormat, Object... args) {
+    return new BaseException(code, msgFormat, args);
+  }
+  
+  public static BaseException newInstance(int code, String messageCode, String msgFormat, Object... args) {
+    return new BaseException(code, messageCode, msgFormat, args);
+  }
+
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/exception/EnableCustomExceptionHandler.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/exception/EnableCustomExceptionHandler.java
new file mode 100644
index 0000000..28553c6
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/exception/EnableCustomExceptionHandler.java
@@ -0,0 +1,17 @@
+package com.supwisdom.institute.backend.common.framework.exception;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.springframework.context.annotation.Import;
+
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Import({ExceptionConfiguration.class})
+public @interface EnableCustomExceptionHandler {
+
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/exception/ErrorResponse.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/exception/ErrorResponse.java
new file mode 100644
index 0000000..e0e4235
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/exception/ErrorResponse.java
@@ -0,0 +1,38 @@
+package com.supwisdom.institute.backend.common.framework.exception;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+public class ErrorResponse implements IApiResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 5810078993746894780L;
+
+  /**
+   * 异常信息
+   */
+  @Getter
+  @Setter
+  private String message;
+
+  /**
+   * 具体异常码
+   */
+  @Getter
+  @Setter
+  private int code = -1;
+
+  private ErrorResponse(int code, String msgFormat, Object... args) {
+    this.code = code;
+    this.message = String.format(msgFormat, args);
+  }
+
+  public static ErrorResponse of(int code, String msgFormat, Object... args) {
+    return new ErrorResponse(code, msgFormat, args);
+  }
+
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/exception/ExceptionConfiguration.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/exception/ExceptionConfiguration.java
new file mode 100644
index 0000000..845e935
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/exception/ExceptionConfiguration.java
@@ -0,0 +1,14 @@
+package com.supwisdom.institute.backend.common.framework.exception;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class ExceptionConfiguration {
+  
+  @Bean
+  public GlobalExceptionHandler globalExceptionHandler() {
+    return new GlobalExceptionHandler();
+  }
+
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/exception/GlobalExceptionHandler.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/exception/GlobalExceptionHandler.java
new file mode 100644
index 0000000..d32199c
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/exception/GlobalExceptionHandler.java
@@ -0,0 +1,30 @@
+package com.supwisdom.institute.backend.common.framework.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+  
+  @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
+  @ExceptionHandler({Exception.class})
+  public ErrorResponse DefaultExceptionHandler(Exception e) {
+    
+    ErrorResponse error = ErrorResponse.of(BaseException.DEFAULT_CODE, e.getMessage());
+    
+    return error;
+  }
+  
+  
+  @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
+  @ExceptionHandler({BaseException.class})
+  public ErrorResponse BaseExceptionHandler(BaseException e) {
+    
+    ErrorResponse error = ErrorResponse.of(e.getCode(), e.getMessage());
+    
+    return error;
+  }
+  
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/model/ABaseModel.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/model/ABaseModel.java
new file mode 100644
index 0000000..efa3e25
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/model/ABaseModel.java
@@ -0,0 +1,10 @@
+package com.supwisdom.institute.backend.common.framework.model;
+
+public abstract class ABaseModel implements IModel {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 8717041105592152819L;
+
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/model/IModel.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/model/IModel.java
new file mode 100644
index 0000000..a6258d3
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/model/IModel.java
@@ -0,0 +1,7 @@
+package com.supwisdom.institute.backend.common.framework.model;
+
+import java.io.Serializable;
+
+public interface IModel extends Serializable {
+
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/rabbitmq/constants/ExchangeNames.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/rabbitmq/constants/ExchangeNames.java
new file mode 100644
index 0000000..1266710
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/rabbitmq/constants/ExchangeNames.java
@@ -0,0 +1,13 @@
+package com.supwisdom.institute.backend.common.framework.rabbitmq.constants;
+
+public class ExchangeNames {
+
+  public static final String EXCHANGE_NAME_FANOUT = "fanout-exchange";
+
+  public static final String EXCHANGE_NAME_DIRECT = "direct-exchange";
+
+  public static final String EXCHANGE_NAME_TOPIC = "topic-exchange";
+
+  public static final String EXCHANGE_NAME_HEADERS = "headers-exchange";
+
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/rabbitmq/constants/QueueNames.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/rabbitmq/constants/QueueNames.java
new file mode 100644
index 0000000..e2ed900
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/rabbitmq/constants/QueueNames.java
@@ -0,0 +1,21 @@
+package com.supwisdom.institute.backend.common.framework.rabbitmq.constants;
+
+public class QueueNames {
+
+  /**
+   * 授权添加事件
+   */
+  public static final String QUEUE_NAME_GRANTED_ADDED = "granted-added";
+
+  /**
+   * 授权移除事件
+   */
+  public static final String QUEUE_NAME_GRANTED_REMOVED = "granted-removed";
+
+  
+  /**
+   * 授权操作日志记录事件
+   */
+  public static final String QUEUE_NAME_GRANT_OPERATE_LOGGING = "granted-operate-logging";
+
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/rabbitmq/constants/RoutingKeys.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/rabbitmq/constants/RoutingKeys.java
new file mode 100644
index 0000000..dad0e02
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/rabbitmq/constants/RoutingKeys.java
@@ -0,0 +1,20 @@
+package com.supwisdom.institute.backend.common.framework.rabbitmq.constants;
+
+public class RoutingKeys {
+
+  /**
+   * 授权添加事件
+   */
+  public static final String ROUTING_KEY_GRANTED_ADDED = "granted.added";
+
+  /**
+   * 授权移除事件
+   */
+  public static final String ROUTING_KEY_GRANTED_REMOVED = "granted.removed";
+
+  
+  /**
+   * 授权操作日志记录事件
+   */
+  public static final String ROUTING_KEY_GRANT_OPERATE_LOGGING = "grant.operate.logging";
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/repo/ABaseJpaRepository.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/repo/ABaseJpaRepository.java
new file mode 100644
index 0000000..d787e59
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/repo/ABaseJpaRepository.java
@@ -0,0 +1,114 @@
+package com.supwisdom.institute.backend.common.framework.repo;
+
+import java.util.Calendar;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.persistence.EntityManager;
+import javax.transaction.Transactional;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.jpa.repository.support.JpaEntityInformation;
+import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
+import org.springframework.data.repository.NoRepositoryBean;
+
+import com.supwisdom.institute.backend.common.core.transmit.user.UserContext;
+import com.supwisdom.institute.backend.common.framework.entity.ABaseEntity;
+
+@Transactional
+@NoRepositoryBean
+public class ABaseJpaRepository<E extends ABaseEntity> extends SimpleJpaRepository<E, String> implements BaseJpaRepository<E> {
+
+  @SuppressWarnings("unused")
+  private final EntityManager em;
+
+  public ABaseJpaRepository(Class<E> domainClass, EntityManager em) {
+    super(domainClass, em);
+    this.em = em;
+  }
+
+  public ABaseJpaRepository(JpaEntityInformation<E, String> information, EntityManager em) {
+    super(information, em);
+    this.em = em;
+  }
+  
+  public Page<E> selectPageList(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+
+    Page<E> page = this.findAll(pageRequest);
+
+    return page;
+  }
+
+  public E selectById(String id) {
+
+    try {
+      Optional<E> entity = this.findById(id);
+
+      if (entity.isPresent()) {
+        return entity.get();
+      }
+    } catch (RuntimeException e) {
+      System.out.println("RuntimeException:" + e.getMessage());
+    } catch (Exception e) {
+      System.out.println("Exception:" + e.getMessage());
+    }
+
+    return null;
+  }
+
+  public E insert(E entity) {
+
+    if (entity.getCompanyId() == null || entity.getCompanyId().isEmpty()) {
+      entity.setCompanyId("1");
+    }
+
+    if (entity.getDeleted() == null) {
+      entity.setDeleted(false);
+    }
+    if (entity.getAddAccount() == null) {
+      entity.setAddAccount(UserContext.getUsername());
+    }
+    if (entity.getAddTime() == null) {
+      entity.setAddTime(Calendar.getInstance().getTime());
+    }
+
+    E e = this.save(entity);
+
+    return e;
+  }
+
+  public E update(E entity) {
+
+    if (entity.getEditAccount() == null) {
+      entity.setEditAccount(UserContext.getUsername());
+    }
+    if (entity.getEditTime() == null) {
+      entity.setEditTime(Calendar.getInstance().getTime());
+    }
+    
+    E e = this.save(entity);
+
+    return e;
+  }
+  
+  public E remove(E entity) {
+    
+    if (entity.getDeleted() == null) {
+      entity.setDeleted(true);
+    }
+    if (entity.getDeleteAccount() == null) {
+      entity.setDeleteAccount(UserContext.getUsername());
+    }
+    if (entity.getDeleteTime() == null) {
+      entity.setDeleteTime(Calendar.getInstance().getTime());
+    }
+    
+    E e = this.save(entity);
+    
+    return e;
+  }
+
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/repo/BaseJpaRepository.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/repo/BaseJpaRepository.java
new file mode 100644
index 0000000..a939a95
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/repo/BaseJpaRepository.java
@@ -0,0 +1,159 @@
+package com.supwisdom.institute.backend.common.framework.repo;
+
+import java.util.Calendar;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.repository.NoRepositoryBean;
+
+import com.supwisdom.institute.backend.common.core.transmit.user.UserContext;
+import com.supwisdom.institute.backend.common.framework.entity.ABaseEntity;
+import com.supwisdom.institute.backend.common.util.UUIDUtils;
+
+@NoRepositoryBean
+public interface BaseJpaRepository<E extends ABaseEntity> extends JpaRepository<E, String>, JpaSpecificationExecutor<E> {
+  
+
+  /**
+   * 生成主键值。 默认使用方法
+   * 如果需要生成主键,需要由子类重写此方法根据需要的方式生成主键值。 
+   * @param entity 要持久化的对象 
+   */
+  public default String generateId() {
+    return UUIDUtils.create();
+  }
+  
+  public default Specification<E> convertToSpec(Map<String, Object> mapBean) {
+    return null;
+  }
+  public default Sort convertToSort(Map<String, String> orderBy) {
+    return null;
+  }
+
+  public default List<E> selectList(Map<String, Object> mapBean, Map<String, String> orderBy) {
+    
+    Specification<E> spec = convertToSpec(mapBean);
+    
+    Sort sort = convertToSort(orderBy);
+    
+    if (sort != null) {
+      return this.findAll(spec, sort);
+    }
+    
+    return this.findAll(spec);
+  }
+
+  public default Page<E> selectPageList(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean, Map<String, String> orderBy) {
+    
+    Specification<E> spec = convertToSpec(mapBean);
+    
+    if (pageIndex == -1) {
+      loadAll = true;
+    }
+    
+    if (loadAll) {
+      pageIndex = 0;
+      pageSize = Integer.MAX_VALUE;
+    }
+    
+    Sort sort = convertToSort(orderBy);
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+    if (sort != null) {
+      pageRequest = PageRequest.of(pageIndex, pageSize, sort);
+    }
+
+    Page<E> page = this.findAll(spec, pageRequest);
+
+    return page;
+  }
+  
+
+
+
+  public default E selectById(String id) {
+
+    try {
+      Optional<E> entity = this.findById(id);
+
+      if (entity.isPresent()) {
+        return entity.get();
+      }
+    } catch (RuntimeException e) {
+      System.out.println("RuntimeException:" + e.getMessage());
+    } catch (Exception e) {
+      System.out.println("Exception:" + e.getMessage());
+    }
+
+    return null;
+  }
+  
+  public default E insert(E entity) {
+    
+    if (entity.getId() == null || entity.getId().isEmpty()) {
+      entity.setId(generateId());
+    }
+
+    if (entity.getCompanyId() == null || entity.getCompanyId().isEmpty()) {
+      entity.setCompanyId("1");
+    }
+
+    if (entity.getDeleted() == null) {
+      entity.setDeleted(false);
+    }
+    if (entity.getAddAccount() == null) {
+      entity.setAddAccount(UserContext.getUsername());
+    }
+    if (entity.getAddTime() == null) {
+      entity.setAddTime(Calendar.getInstance().getTime());
+    }
+    
+    E e = this.save(entity);
+
+    return e;
+  }
+  
+  public default E update(E entity) {
+
+    if (entity.getEditAccount() == null) {
+      entity.setEditAccount(UserContext.getUsername());
+    }
+    if (entity.getEditTime() == null) {
+      entity.setEditTime(Calendar.getInstance().getTime());
+    }
+    
+    E e = this.save(entity);
+
+    return e;
+  }
+  
+  public default E remove(E entity) {
+    
+    if (entity.getDeleted() == null) {
+      entity.setDeleted(true);
+    }
+    if (entity.getDeleteAccount() == null) {
+      entity.setDeleteAccount(UserContext.getUsername());
+    }
+    if (entity.getDeleteTime() == null) {
+      entity.setDeleteTime(Calendar.getInstance().getTime());
+    }
+    
+    E e = this.save(entity);
+    
+    return e;
+  }
+  
+  public default void delete(String id) {
+    
+    this.deleteById(id);
+  }
+
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/repo/resultTransformer/IgnoreCaseResultTransformer.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/repo/resultTransformer/IgnoreCaseResultTransformer.java
new file mode 100644
index 0000000..0736f3e
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/repo/resultTransformer/IgnoreCaseResultTransformer.java
@@ -0,0 +1,75 @@
+package com.supwisdom.institute.backend.common.framework.repo.resultTransformer;
+
+import com.google.common.collect.Lists;
+import com.supwisdom.institute.backend.common.util.ReflectUtils;
+
+import org.hibernate.HibernateException;
+import org.hibernate.transform.ResultTransformer;
+
+import java.lang.reflect.Field;
+import java.util.List;
+
+/**
+ * 修正hibernate返回自定义pojo类型时找不到属性的BUG
+ * 主要发生在使用oracle或高版本的mysql时,查询返回的字段默认是大写的(除非SQL中指定了别名),这导致返回自定义pojo类型时会报找不到属性的错误,该类用于修正此BUG。
+ * 使用该类时SQL返回的字段名大小写或者带"_"都会被忽略,如数据库字段为 USER_NAME,自定义pojo的属性名为username就可以使用  
+ * 
+ * @author feng
+ */
+public class IgnoreCaseResultTransformer implements ResultTransformer {
+  private static final long serialVersionUID = -3779317531110592988L;
+  private final Class<?> resultClass;
+  private Field[] fields;
+  private List<Class<?>> types = Lists.newArrayList();
+
+  public IgnoreCaseResultTransformer(final Class<?> resultClass) {
+    this.resultClass = resultClass;
+    List<Field> list = Lists.newArrayList();
+    for (Class<?> superClass = resultClass; superClass != Object.class; superClass = superClass.getSuperclass()) {
+      Field[] fs = superClass.getDeclaredFields();
+      List<Field> newFs = Lists.newArrayList();
+      for (int i = 0; i < fs.length; i++) {
+        if (fs[i].getName().equals("serialVersionUID")) {
+          continue;
+        }
+        types.add(fs[i].getType());
+        ReflectUtils.makeAccessible(fs[i]);
+        newFs.add(fs[i]);
+      }
+      list.addAll(newFs);
+    }
+    this.fields = list.toArray(new Field[list.size()]);
+  }
+
+  /**
+   * aliases为每条记录的数据库字段名,ORACLE字段名默认为大写
+   * tupe为与aliases对应的字段的值      
+   */
+  @Override
+  public Object transformTuple(final Object[] tuple, final String[] aliases) {
+    Object result;
+    try {
+      result = this.resultClass.newInstance();
+      for (int i = 0; i < aliases.length; i++) {
+        for (int j = 0; j < this.fields.length; j++) {
+          String fieldName = this.fields[j].getName();
+          // 数据库字段带下划线的时候也能保证使用,如数据库字段为 USER_NAME,自定义pojo的属性名为username就可以使用
+          if (fieldName.equalsIgnoreCase(aliases[i].replaceAll("_", ""))) {
+            ReflectUtils.invokeSetter(result, fieldName, tuple[i], this.types.get(j));
+            // beanUtilsBean.setProperty(result, fieldName, tuple[i]);
+            break;
+          }
+        }
+      }
+    } catch (Exception e) {
+      throw new HibernateException("Could not instantiate resultclass: " + this.resultClass.getName(), e);
+    }
+    return result;
+  }
+
+  @Override
+  @SuppressWarnings("rawtypes")
+  public List transformList(final List collection) {
+    return collection;
+  }
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/service/ABaseService.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/service/ABaseService.java
new file mode 100644
index 0000000..a36af40
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/service/ABaseService.java
@@ -0,0 +1,48 @@
+package com.supwisdom.institute.backend.common.framework.service;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.data.domain.Page;
+
+import com.supwisdom.institute.backend.common.framework.entity.ABaseEntity;
+import com.supwisdom.institute.backend.common.framework.repo.BaseJpaRepository;
+
+public abstract class ABaseService<E extends ABaseEntity, REPO extends BaseJpaRepository<E>> {
+  
+  public abstract REPO getRepo();
+
+
+  public List<E> selectList(Map<String, Object> mapBean, Map<String, String> orderBy) {
+    return getRepo().selectList(mapBean, orderBy);
+  }
+  
+  public Page<E> selectPageList(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean, Map<String, String> orderBy) {
+    
+    return getRepo().selectPageList(loadAll, pageIndex, pageSize, mapBean, orderBy);
+  }
+  
+  public E selectById(String id) {
+    
+    return getRepo().selectById(id);
+  }
+  
+  public E insert(E entity) {
+    
+    return getRepo().insert(entity);
+  }
+  
+  public E update(E entity) {
+    
+    E ret = getRepo().update(entity);
+    getRepo().flush();
+    
+    return ret;
+  }
+  
+  public void deleteById(String id) {
+    
+    getRepo().deleteById(id);
+  }
+
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/request/IApiCreateRequest.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/request/IApiCreateRequest.java
new file mode 100644
index 0000000..f118c4d
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/request/IApiCreateRequest.java
@@ -0,0 +1,5 @@
+package com.supwisdom.institute.backend.common.framework.vo.request;
+
+public interface IApiCreateRequest extends IApiRequest {
+
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/request/IApiLoadRequest.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/request/IApiLoadRequest.java
new file mode 100644
index 0000000..3cf62c4
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/request/IApiLoadRequest.java
@@ -0,0 +1,9 @@
+package com.supwisdom.institute.backend.common.framework.vo.request;
+
+public interface IApiLoadRequest extends IApiRequest {
+  
+  String getId();
+
+  void setId(String id);
+
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/request/IApiQueryRequest.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/request/IApiQueryRequest.java
new file mode 100644
index 0000000..da3b638
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/request/IApiQueryRequest.java
@@ -0,0 +1,27 @@
+package com.supwisdom.institute.backend.common.framework.vo.request;
+
+import java.util.Map;
+
+public interface IApiQueryRequest extends IApiRequest {
+
+  boolean isLoadAll();
+  
+  void setLoadAll(boolean loadAll);
+
+  int getPageIndex();
+  
+  void setPageIndex(int pageIndex);
+
+  int getPageSize();
+  
+  void setPageSize(int pageSize);
+  
+  Map<String, Object> getMapBean();
+  
+  void setMapBean(Map<String, Object> mapBean);
+
+  Map<String, String> getOrderBy();
+  
+  void setOrderBy(Map<String, String> orderBy);
+
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/request/IApiRemoveRequest.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/request/IApiRemoveRequest.java
new file mode 100644
index 0000000..bda1e59
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/request/IApiRemoveRequest.java
@@ -0,0 +1,10 @@
+package com.supwisdom.institute.backend.common.framework.vo.request;
+
+public interface IApiRemoveRequest extends IApiRequest {
+
+  String getId();
+
+
+  void setId(String id);
+  
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/request/IApiRequest.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/request/IApiRequest.java
new file mode 100644
index 0000000..71a4abd
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/request/IApiRequest.java
@@ -0,0 +1,7 @@
+package com.supwisdom.institute.backend.common.framework.vo.request;
+
+import java.io.Serializable;
+
+public interface IApiRequest extends Serializable {
+
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/request/IApiUpdateRequest.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/request/IApiUpdateRequest.java
new file mode 100644
index 0000000..9c491c4
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/request/IApiUpdateRequest.java
@@ -0,0 +1,9 @@
+package com.supwisdom.institute.backend.common.framework.vo.request;
+
+public interface IApiUpdateRequest extends IApiRequest {
+
+  String getId();
+
+  void setId(String id);
+
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/response/AbstractApiResponse.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/response/AbstractApiResponse.java
new file mode 100644
index 0000000..20de33c
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/response/AbstractApiResponse.java
@@ -0,0 +1,25 @@
+package com.supwisdom.institute.backend.common.framework.vo.response;
+
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+public abstract class AbstractApiResponse<T extends IApiResponseData> implements IApiResponse<T> {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 846108786006850165L;
+  
+  protected int code = 0;
+  protected String message = null;
+  
+  @Override
+  public int getCode() {
+    return code;
+  }
+  
+  @Override
+  public String getMessage() {
+    return message;
+  }
+
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/response/DefaultApiResponse.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/response/DefaultApiResponse.java
new file mode 100644
index 0000000..5ea45c5
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/response/DefaultApiResponse.java
@@ -0,0 +1,36 @@
+package com.supwisdom.institute.backend.common.framework.vo.response;
+
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+public class DefaultApiResponse<T extends IApiResponseData> extends AbstractApiResponse<T> {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 4380576799912565681L;
+
+  protected T data;
+
+  @Override
+  public T getData() {
+    return data;
+  }
+
+  public DefaultApiResponse(T data) {
+    this(0, null, data);
+  }
+
+  public DefaultApiResponse(int code, String message, T data) {
+    super.code = code;
+    super.message = message;
+    
+    this.data = data;
+  }
+
+  public static <T extends IApiResponseData> DefaultApiResponse<T> build(T data) {
+    DefaultApiResponse<T> defaultApiResponse = new DefaultApiResponse<T>(data);
+
+    return defaultApiResponse;
+  }
+
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/response/IApiResponse.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/response/IApiResponse.java
new file mode 100644
index 0000000..f8f2573
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/response/IApiResponse.java
@@ -0,0 +1,15 @@
+package com.supwisdom.institute.backend.common.framework.vo.response;
+
+import java.io.Serializable;
+
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+
+public interface IApiResponse<T extends IApiResponseData> extends Serializable {
+  
+  int getCode();
+  
+  String getMessage();
+  
+  T getData();
+  
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/response/data/IApiCreateResponseData.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/response/data/IApiCreateResponseData.java
new file mode 100644
index 0000000..05ab9d2
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/response/data/IApiCreateResponseData.java
@@ -0,0 +1,7 @@
+package com.supwisdom.institute.backend.common.framework.vo.response.data;
+
+public interface IApiCreateResponseData extends IApiResponseData {
+
+  String getId();
+
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/response/data/IApiLoadResponseData.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/response/data/IApiLoadResponseData.java
new file mode 100644
index 0000000..8741959
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/response/data/IApiLoadResponseData.java
@@ -0,0 +1,7 @@
+package com.supwisdom.institute.backend.common.framework.vo.response.data;
+
+public interface IApiLoadResponseData extends IApiResponseData {
+
+  String getId();
+
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/response/data/IApiQueryResponseData.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/response/data/IApiQueryResponseData.java
new file mode 100644
index 0000000..5789a96
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/response/data/IApiQueryResponseData.java
@@ -0,0 +1,57 @@
+package com.supwisdom.institute.backend.common.framework.vo.response.data;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+public interface IApiQueryResponseData<E extends Serializable> extends IApiResponseData {
+  
+  /**
+   * 当前页码
+   * @return
+   */
+  int getPageIndex();
+  
+  /**
+   * 每页记录数
+   * @return
+   */
+  int getPageSize();
+  
+  /**
+   * 查询条件
+   * @return
+   */
+  Map<String, Object> getMapBean();
+  
+  /**
+   * 排序字段
+   * @return
+   */
+  Map<String, String> getOrderBy();
+  
+  /**
+   * 总页数
+   * @return
+   */
+  int getPageCount();
+  
+  /**
+   * 总记录数
+   * @return
+   */
+  long getRecordCount();
+  
+  /**
+   * 返回记录数
+   * @return
+   */
+  int getCurrentItemCount();
+  
+  /**
+   * 返回记录
+   * @return
+   */
+  List<E> getItems();
+  
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/response/data/IApiRemoveResponseData.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/response/data/IApiRemoveResponseData.java
new file mode 100644
index 0000000..820c62d
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/response/data/IApiRemoveResponseData.java
@@ -0,0 +1,7 @@
+package com.supwisdom.institute.backend.common.framework.vo.response.data;
+
+public interface IApiRemoveResponseData extends IApiResponseData {
+
+  String getId();
+
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/response/data/IApiResponseData.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/response/data/IApiResponseData.java
new file mode 100644
index 0000000..56b9163
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/response/data/IApiResponseData.java
@@ -0,0 +1,7 @@
+package com.supwisdom.institute.backend.common.framework.vo.response.data;
+
+import java.io.Serializable;
+
+public interface IApiResponseData extends Serializable {
+
+}
diff --git a/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/response/data/IApiUpdateResponseData.java b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/response/data/IApiUpdateResponseData.java
new file mode 100644
index 0000000..211c437
--- /dev/null
+++ b/common/framework/src/main/java/com/supwisdom/institute/backend/common/framework/vo/response/data/IApiUpdateResponseData.java
@@ -0,0 +1,7 @@
+package com.supwisdom.institute.backend.common.framework.vo.response.data;
+
+public interface IApiUpdateResponseData extends IApiResponseData {
+
+  String getId();
+
+}
diff --git a/common/framework/src/main/resources/META-INF/spring.factories b/common/framework/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..53d2c7c
--- /dev/null
+++ b/common/framework/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+  com.supwisdom.institute.backend.common.framework.distributedlock.RedisDistributedLockAutoConfiguration
diff --git a/common/pom.xml b/common/pom.xml
new file mode 100644
index 0000000..eed0f6a
--- /dev/null
+++ b/common/pom.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  
+  <parent>
+    <groupId>com.supwisdom.institute</groupId>
+    <artifactId>sw-backend-parent</artifactId>
+    <version>0.0.1</version>
+  </parent>
+  
+  <groupId>com.supwisdom.institute</groupId>
+  <artifactId>sw-backend-common</artifactId>
+  <version>0.0.1</version>
+  <packaging>pom</packaging>
+
+  <name>Supwisdom Backend Framework Common</name>
+  <description>Supwisdom Backend Framework Common project</description>
+
+  <modules>
+    <module>core</module>
+    <module>utils</module>
+    <module>framework</module>
+  </modules>
+
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-deploy-plugin</artifactId>
+        <configuration>
+          <skip>true</skip>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/common/utils/pom.xml b/common/utils/pom.xml
new file mode 100644
index 0000000..d9b52d0
--- /dev/null
+++ b/common/utils/pom.xml
@@ -0,0 +1,68 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.supwisdom.institute</groupId>
+    <artifactId>sw-backend-parent</artifactId>
+    <version>0.0.1</version>
+    <relativePath>../../</relativePath>
+  </parent>
+
+  <groupId>com.supwisdom.institute</groupId>
+  <artifactId>sw-backend-common-utils</artifactId>
+  <version>0.0.1</version>
+  <packaging>jar</packaging>
+
+  <name>Supwisdom Backend Framework Common Utils</name>
+  <description>Supwisdom Backend Framework Common Utils project</description>
+
+  <dependencies>
+
+    <dependency>
+      <groupId>org.projectlombok</groupId>
+      <artifactId>lombok</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-lang3</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+    </dependency>
+
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-release-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/common/utils/src/main/java/com/supwisdom/institute/backend/common/util/DateUtil.java b/common/utils/src/main/java/com/supwisdom/institute/backend/common/util/DateUtil.java
new file mode 100644
index 0000000..0f08910
--- /dev/null
+++ b/common/utils/src/main/java/com/supwisdom/institute/backend/common/util/DateUtil.java
@@ -0,0 +1,45 @@
+package com.supwisdom.institute.backend.common.util;
+
+import java.text.ParseException;
+import java.util.Date;
+
+import org.apache.commons.lang3.time.DateFormatUtils;
+import org.apache.commons.lang3.time.DateUtils;
+
+public class DateUtil {
+  
+  public static void main(String[] args) {
+    Date d0 = DateUtil.parseDate("2019-07-23 00:00:00", "yyyy-MM-dd HH:mm:ss");
+
+    Date d1 = DateUtil.parseDate("2019-07-23 23:59:59", "yyyy-MM-dd HH:mm:ss");
+
+    System.out.println(d0);
+    System.out.println(d1);
+    
+    String s0 = DateUtil.formatDate(DateUtil.now(), "yyyyMMddHHmmss");
+    System.out.println(s0);
+  }
+  
+  public static Date parseDate(String source, String pattern) {
+    
+    try {
+      Date d = DateUtils.parseDate(source, pattern);
+      
+      return d;
+    } catch (ParseException e) {
+      e.printStackTrace();
+    }
+    
+    return null;
+  }
+
+  public static Date now() {
+    return new Date();
+  }
+  
+  
+  public static String formatDate(Date date, String pattern) {
+    return DateFormatUtils.format(date, pattern);
+  }
+
+}
diff --git a/common/utils/src/main/java/com/supwisdom/institute/backend/common/util/MapBeanUtils.java b/common/utils/src/main/java/com/supwisdom/institute/backend/common/util/MapBeanUtils.java
new file mode 100644
index 0000000..136f9de
--- /dev/null
+++ b/common/utils/src/main/java/com/supwisdom/institute/backend/common/util/MapBeanUtils.java
@@ -0,0 +1,218 @@
+package com.supwisdom.institute.backend.common.util;
+
+import java.util.Map;
+
+public class MapBeanUtils {
+
+  /**
+   * 判断 mapBean 中的 key 是否存在;若存在,则判断是否有值
+   * 
+   * @param mapBean
+   * @param key
+   * @return
+   */
+  public static boolean containsValue(Map<String, Object> mapBean, String key) {
+
+    if (!mapBean.containsKey(key)) {
+      return false;
+    }
+
+    if (mapBean.get(key) == null) {
+      return false;
+    }
+
+    if (String.valueOf(mapBean.get(key)).isEmpty()) {
+      return false;
+    }
+
+    return true;
+  }
+
+  /**
+   * 获取 mapBean 中 key 的 value,若不存在,则返回 null
+   * 
+   * @param mapBean
+   * @param key
+   * @return
+   */
+  public static String getString(Map<String, Object> mapBean, String key) {
+
+    return getString(mapBean, key, null);
+  }
+
+  /**
+   * 获取 mapBean 中 key 的 value,若不存在,则返回 defaultValue
+   * 
+   * @param mapBean
+   * @param key
+   * @param defaultValue
+   * @return
+   */
+  public static String getString(Map<String, Object> mapBean, String key, String defaultValue) {
+
+    if (containsValue(mapBean, key)) {
+      return String.valueOf(mapBean.get(key));
+    }
+
+    return defaultValue;
+  }
+
+  /**
+   * 获取 mapBean 中 key 的 value,若不存在,则返回 false
+   * 
+   * @param mapBean
+   * @param key
+   * @return
+   */
+  public static Boolean getBoolean(Map<String, Object> mapBean, String key) {
+
+    return getBoolean(mapBean, key, null);
+  }
+
+  /**
+   * 获取 mapBean 中 key 的 value,若不存在,则返回 defaultValue
+   * 
+   * @param mapBean
+   * @param key
+   * @param defaultValue
+   * @return
+   */
+  public static Boolean getBoolean(Map<String, Object> mapBean, String key, Boolean defaultValue) {
+
+    if (containsValue(mapBean, key)) {
+      Boolean b = Boolean.valueOf(String.valueOf(mapBean.get(key)));
+      return b == null ? defaultValue : b;
+    }
+
+    return defaultValue;
+  }
+
+  /**
+   * 获取 mapBean 中 key 的 value,若不存在,则返回 -1
+   * 
+   * @param mapBean
+   * @param key
+   * @return
+   */
+  public static Integer getInteger(Map<String, Object> mapBean, String key) {
+
+    return getInteger(mapBean, key, null);
+  }
+
+  /**
+   * 获取 mapBean 中 key 的 value,若不存在,则返回 defaultValue
+   * 
+   * @param mapBean
+   * @param key
+   * @param defaultValue
+   * @return
+   */
+  public static Integer getInteger(Map<String, Object> mapBean, String key, Integer defaultValue) {
+
+    if (containsValue(mapBean, key)) {
+      Integer i = Integer.valueOf(String.valueOf(mapBean.get(key)));
+      return i == null ? defaultValue : i;
+    }
+
+    return defaultValue;
+  }
+
+  /**
+   * 获取 mapBean 中 key 的 value,若不存在,则返回 -1L
+   * 
+   * @param mapBean
+   * @param key
+   * @return
+   */
+  public static Long getLong(Map<String, Object> mapBean, String key) {
+
+    return getLong(mapBean, key, null);
+  }
+
+  /**
+   * 获取 mapBean 中 key 的 value,若不存在,则返回 defaultValue
+   * 
+   * @param mapBean
+   * @param key
+   * @param defaultValue
+   * @return
+   */
+  public static Long getLong(Map<String, Object> mapBean, String key, Long defaultValue) {
+
+    if (containsValue(mapBean, key)) {
+      Long l = Long.valueOf(String.valueOf(mapBean.get(key)));
+      return l == null ? defaultValue : l;
+    }
+
+    return defaultValue;
+  }
+
+//  /**
+//   * 获取 mapBean 中 key 的 value,若不存在,则返回 -1L
+//   *
+//   * @param mapBean
+//   * @param key
+//   * @return
+//   */
+//  public static List getList(Map<String, Object> mapBean, String key) {
+//
+//    return getList(mapBean, key, Lists.newArrayList());
+//  }
+//
+//  /**
+//   * 获取 mapBean 中 key 的 value,若不存在,则返回 defaultValue
+//   *
+//   * @param mapBean
+//   * @param key
+//   * @param defaultValue
+//   * @return
+//   */
+//  public static List getList(Map<String, Object> mapBean, String key, List defaultValue) {
+//
+//    if (containsValue(mapBean, key)) {
+//      List l = (List)mapBean.get(key);
+//      return l == null ? defaultValue : l;
+//    }
+//
+//    return defaultValue;
+//  }
+//
+//  /**
+//   * 将一个 Map 对象转化为一个 JavaBean
+//   * @param obj 要转化的对象
+//   * @param map 包含属性值的 map
+//   * @return 转化出来的 JavaBean 对象
+//   * @throws IntrospectionException
+//   *             如果分析类属性失败
+//   * @throws IllegalAccessException
+//   *             如果实例化 JavaBean 失败
+//   * @throws InstantiationException
+//   *             如果实例化 JavaBean 失败
+//   * @throws InvocationTargetException
+//   *             如果调用属性的 setter 方法失败
+//   */
+//  public static Object convert2Bean(Object obj, Map map)
+//          throws IntrospectionException, IllegalAccessException,
+//          InstantiationException, InvocationTargetException {
+//    BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass()); // 获取类属性
+//
+//    // 给 JavaBean 对象的属性赋值
+//    PropertyDescriptor[] propertyDescriptors =  beanInfo.getPropertyDescriptors();
+//    for (int i = 0; i< propertyDescriptors.length; i++) {
+//      PropertyDescriptor descriptor = propertyDescriptors[i];
+//      String propertyName = descriptor.getName();
+//
+//      if (map.containsKey(propertyName)) {
+//        // 下面一句可以 try 起来,这样当一个属性赋值失败的时候就不会影响其他属性赋值。
+//        Object value = map.get(propertyName);
+//
+//        Object[] args = new Object[1];
+//        args[0] = value;
+//
+//        descriptor.getWriteMethod().invoke(obj, args);
+//      }
+//    }
+//    return obj;
+//  }
+
+}
diff --git a/common/utils/src/main/java/com/supwisdom/institute/backend/common/util/ReflectUtils.java b/common/utils/src/main/java/com/supwisdom/institute/backend/common/util/ReflectUtils.java
new file mode 100644
index 0000000..cddd545
--- /dev/null
+++ b/common/utils/src/main/java/com/supwisdom/institute/backend/common/util/ReflectUtils.java
@@ -0,0 +1,391 @@
+package com.supwisdom.institute.backend.common.util;
+
+import lombok.extern.slf4j.Slf4j;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Validate;
+import org.springframework.util.Assert;
+
+import java.lang.reflect.*;
+import java.util.Date;
+
+/**
+ * 利用反射进行操作的一个工具类
+ *
+ * @author fengpy
+ */
+@SuppressWarnings("rawtypes")
+@Slf4j
+public class ReflectUtils {
+
+  private static final String SETTER_PREFIX = "set";
+
+  private static final String GETTER_PREFIX = "get";
+
+  private static final String CGLIB_CLASS_SEPARATOR = "$$";
+
+  /**
+   * 利用反射获取指定对象里面的指定属性
+   *
+   * @param obj
+   *          目标对象
+   * @param fieldName
+   *          目标属性
+   * @return 目标字段
+   */
+  public static Field getField(Object obj, String fieldName) {
+    Field field = null;
+    for (Class<?> clazz = obj.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {
+      try {
+        field = clazz.getDeclaredField(fieldName);
+        break;
+      } catch (NoSuchFieldException e) {
+        // 这里不用做处理,子类没有该字段可能对应的父类有,都没有就返回null。
+      }
+    }
+    return field;
+  }
+  
+  public static boolean existField(Object obj, String fieldName) {
+    Field field = getField(obj, fieldName);
+    return field != null;
+  }
+
+  /**
+   * 调用Getter方法.
+   * 支持多级,如:对象名.对象名.方法
+   */
+  public static Object invokeGetter(Object obj, String propertyName) {
+    Object object = obj;
+    for (String name : StringUtils.split(propertyName, ".")){
+      String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name);
+      object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});
+    }
+    return object;
+  }
+
+  /**
+   * 调用Setter方法, 仅匹配方法名。
+   * 支持多级,如:对象名.对象名.方法
+   */
+  public static void invokeSetter(Object obj, String propertyName, Object value, Class valueCla) throws IllegalAccessException, InstantiationException {
+    Object object = obj;
+    String[] names = StringUtils.split(propertyName, ".");
+    for (int i=0; i<names.length; i++){
+      if(i<names.length-1){
+        String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]);
+        object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});
+      }else{
+        String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);
+        value = doSpecial(value,valueCla);
+        invokeMethodByName(object, setterMethodName, new Object[] { value });
+      }
+    }
+  }
+
+  /**
+   * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.
+   */
+  public static Object getFieldValue(final Object obj, final String fieldName) {
+    Field field = getAccessibleField(obj, fieldName);
+
+    if (field == null) {
+      throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]");
+    }
+
+    Object result = null;
+    try {
+      result = field.get(obj);
+    } catch (IllegalAccessException e) {
+      log.error("不可能抛出的异常{}", e.getMessage());
+    }
+    return result;
+  }
+
+  /**
+   * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.
+   */
+  public static void setFieldValue(final Object obj, final String fieldName, final Object value, Class valueCla) {
+    Field field = getAccessibleField(obj, fieldName);
+
+    if (field == null) {
+      throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]");
+    }
+
+    try {
+      doSpecial(value,valueCla);
+      field.set(obj, value);
+    } catch (IllegalAccessException e) {
+      log.error("不可能抛出的异常:{}", e.getMessage());
+    }
+  }
+
+  /**
+   * 直接调用对象方法, 无视private/protected修饰符.
+   * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用.
+   * 同时匹配方法名+参数类型,
+   */
+  public static Object invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes,
+                                    final Object[] args) {
+    Method method = getAccessibleMethod(obj, methodName, parameterTypes);
+    if (method == null) {
+      throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]");
+    }
+
+    try {
+      return method.invoke(obj, args);
+    } catch (Exception e) {
+      throw convertReflectionExceptionToUnchecked(e);
+    }
+  }
+
+  /**
+   * 直接调用对象方法, 无视private/protected修饰符,
+   * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用.
+   * 只匹配函数名,如果有多个同名函数调用第一个。
+   */
+  public static Object invokeMethodByName(final Object obj, final String methodName, final Object[] args) {
+    Method method = getAccessibleMethodByName(obj, methodName);
+    if (method == null) {
+      throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]");
+    }
+
+    try {
+      return method.invoke(obj, args);
+    } catch (Exception e) {
+      throw convertReflectionExceptionToUnchecked(e);
+    }
+  }
+
+  /**
+   * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问.
+   *
+   * 如向上转型到Object仍无法找到, 返回null.
+   */
+  public static Field getAccessibleField(final Object obj, final String fieldName) {
+    Validate.notNull(obj, "object can't be null");
+    Validate.notBlank(fieldName, "fieldName can't be blank");
+    for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
+      try {
+        Field field = superClass.getDeclaredField(fieldName);
+        makeAccessible(field);
+        return field;
+      } catch (NoSuchFieldException e) {//NOSONAR
+        // Field不在当前类定义,继续向上转型
+        continue;// new add
+      }
+    }
+    return null;
+  }
+
+  /**
+   * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
+   * 如向上转型到Object仍无法找到, 返回null.
+   * 匹配函数名+参数类型。
+   *
+   * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
+   */
+  public static Method getAccessibleMethod(final Object obj, final String methodName,
+                                           final Class<?>... parameterTypes) {
+    Validate.notNull(obj, "object can't be null");
+    Validate.notBlank(methodName, "methodName can't be blank");
+
+    for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {
+      try {
+        Method method = searchType.getDeclaredMethod(methodName, parameterTypes);
+        makeAccessible(method);
+        return method;
+      } catch (NoSuchMethodException e) {
+        // Method不在当前类定义,继续向上转型
+        continue;// new add
+      }
+    }
+    return null;
+  }
+
+  /**
+   * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
+   * 如向上转型到Object仍无法找到, 返回null.
+   * 只匹配函数名。
+   *
+   * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
+   */
+  public static Method getAccessibleMethodByName(final Object obj, final String methodName) {
+    Validate.notNull(obj, "object can't be null");
+    Validate.notBlank(methodName, "methodName can't be blank");
+
+    for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {
+      Method[] methods = searchType.getDeclaredMethods();
+      for (Method method : methods) {
+        if (method.getName().equals(methodName)) {
+          makeAccessible(method);
+          return method;
+        }
+      }
+    }
+    return null;
+  }
+
+  /**
+   * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
+   */
+  public static void makeAccessible(Method method) {
+    if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
+            && !method.isAccessible()) {
+      method.setAccessible(true);
+    }
+  }
+
+  /**
+   * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
+   */
+  public static void makeAccessible(Field field) {
+    if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || Modifier
+            .isFinal(field.getModifiers())) && !field.isAccessible()) {
+      field.setAccessible(true);
+    }
+  }
+
+  /**
+   * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处
+   * 如无法找到, 返回Object.class.
+   * eg.
+   * public UserDao extends HibernateDao<User>
+   *
+   * @param clazz The class to introspect
+   * @return the first generic declaration, or Object.class if cannot be determined
+   */
+  @SuppressWarnings("unchecked")
+  public static <T> Class<T> getClassGenricType(final Class clazz) {
+    return getClassGenricType(clazz, 0);
+  }
+
+  /**
+   * 通过反射, 获得Class定义中声明的父类的泛型参数的类型.
+   * 如无法找到, 返回Object.class.
+   *
+   * 如public UserDao extends HibernateDao<User,Long>
+   *
+   * @param clazz clazz The class to introspect
+   * @param index the Index of the generic ddeclaration,start from 0.
+   * @return the index generic declaration, or Object.class if cannot be determined
+   */
+  public static Class getClassGenricType(final Class clazz, final int index) {
+
+    Type genType = clazz.getGenericSuperclass();
+
+    if (!(genType instanceof ParameterizedType)) {
+      log.warn(clazz.getSimpleName() + "'s superclass not ParameterizedType");
+      return Object.class;
+    }
+
+    Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
+
+    if (index >= params.length || index < 0) {
+      log.warn("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
+              + params.length);
+      return Object.class;
+    }
+    if (!(params[index] instanceof Class)) {
+      log.warn(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
+      return Object.class;
+    }
+
+    return (Class) params[index];
+  }
+
+  public static Class<?> getUserClass(Object instance) {
+    Assert.notNull(instance, "Instance must not be null");
+    Class clazz = instance.getClass();
+    if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {
+      Class<?> superClass = clazz.getSuperclass();
+      if (superClass != null && !Object.class.equals(superClass)) {
+        return superClass;
+      }
+    }
+    return clazz;
+
+  }
+
+  /**
+   * 将反射时的checked exception转换为unchecked exception.
+   */
+  public static RuntimeException convertReflectionExceptionToUnchecked(Exception e) {
+    if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException
+            || e instanceof NoSuchMethodException) {
+      return new IllegalArgumentException(e);
+    } else if (e instanceof InvocationTargetException) {
+      return new RuntimeException(((InvocationTargetException) e).getTargetException());
+    } else if (e instanceof RuntimeException) {
+      return (RuntimeException) e;
+    }
+    return new RuntimeException("Unexpected Checked Exception.", e);
+  }
+
+  /**
+   * 类型转换
+   *
+   * @param clazz
+   *          :目标类型
+   * @param source
+   *          :待转换对象
+   * @return :目标对象
+   */
+  public static Object typeConversion(Class<?> clazz, String source) {
+
+    if (clazz == null) {
+      throw new IllegalArgumentException("clazz should not be null");
+    }
+
+    Object targetObj = null;
+    String nameType = clazz.getName();
+
+    if ("java.lang.Integer".equals(nameType) || "int".equals(nameType)) {
+      targetObj = Integer.valueOf(source);
+    } else if ("java.lang.String".equals(nameType) || "string".equals(nameType)) {
+      targetObj = source;
+    } else if ("java.lang.Float".equals(nameType) || "float".equals(nameType)) {
+      targetObj = Float.valueOf(source);
+    } else if ("java.lang.Double".equals(nameType) || "double".equals(nameType)) {
+      targetObj = Double.valueOf(source);
+    } else if ("java.lang.Boolean".equals(nameType) || "boolean".equals(nameType)) {
+      targetObj = Boolean.valueOf(source);
+    } else if ("java.lang.Long".equals(nameType) || "long".equals(nameType)) {
+      targetObj = Long.valueOf(source);
+    } else if ("java.lang.Short".equals(nameType) || "short".equals(nameType)) {
+      targetObj = Short.valueOf(source);
+    } else if ("java.lang.Character".equals(nameType) || "char".equals(nameType)) {
+      targetObj = source.charAt(1);
+    }else if ("java.util.Date".equals(nameType)) {
+      targetObj = new Date(source);
+    }
+
+    return targetObj;
+  }
+
+
+  /**
+   * 根据类的全路径获取class
+   * @param fullPath
+   *          :类的全路径
+   * @return :class
+   */
+  public static Class fullPath2Class(String fullPath) {
+    Class cl = null;
+    try {
+      ClassLoader loader = ClassLoader.getSystemClassLoader();
+      cl = loader.loadClass(fullPath);
+    } catch (ClassNotFoundException e) {
+      e.printStackTrace();
+    }
+    return cl;
+  }
+
+    private static Object doSpecial(Object value, Class valueCla){
+        //TODO 针对sql脚本的类型和实体entity的类型不一致做的特殊处理(有待优化)
+        if(valueCla.equals(Boolean.class)&&value.getClass().equals(Integer.class)){
+            value = typeConversion(Boolean.class,value.toString());
+        }
+        return value;
+    }
+}
diff --git a/common/utils/src/main/java/com/supwisdom/institute/backend/common/util/UUIDUtils.java b/common/utils/src/main/java/com/supwisdom/institute/backend/common/util/UUIDUtils.java
new file mode 100644
index 0000000..d348d91
--- /dev/null
+++ b/common/utils/src/main/java/com/supwisdom/institute/backend/common/util/UUIDUtils.java
@@ -0,0 +1,87 @@
+package com.supwisdom.institute.backend.common.util;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+import java.util.Random;
+import java.util.UUID;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class UUIDUtils {
+
+  private static boolean IS_THREADLOCALRANDOM_AVAILABLE = false;
+  private static Random random;
+  private static final long leastSigBits;
+  private static final ReentrantLock lock = new ReentrantLock();
+  private static long lastTime;
+
+  static {
+    try {
+      IS_THREADLOCALRANDOM_AVAILABLE = null != UUIDUtils.class.getClassLoader().loadClass(
+          "java.util.concurrent.ThreadLocalRandom");
+    } catch (ClassNotFoundException e) {
+    }
+
+    byte[] seed = new SecureRandom().generateSeed(8);
+    leastSigBits = new BigInteger(seed).longValue();
+    if (!IS_THREADLOCALRANDOM_AVAILABLE) {
+      random = new Random(leastSigBits);
+    }
+  }
+
+  private UUIDUtils() {}
+
+  /**
+   * 生成32位随机码
+   * @return
+   */
+  public static String random() {
+    byte[] randomBytes = new byte[16];
+    if (IS_THREADLOCALRANDOM_AVAILABLE) {
+      java.util.concurrent.ThreadLocalRandom.current().nextBytes(randomBytes);
+    } else {
+      random.nextBytes(randomBytes);
+    }
+
+    long mostSigBits = 0;
+    for (int i = 0; i < 8; i++) {
+      mostSigBits = (mostSigBits << 8) | (randomBytes[i] & 0xff);
+    }
+    long leastSigBits = 0;
+    for (int i = 8; i < 16; i++) {
+      leastSigBits = (leastSigBits << 8) | (randomBytes[i] & 0xff);
+    }
+
+    return new UUID(mostSigBits, leastSigBits).toString().replaceAll("-", "");
+  }
+
+  /**
+   * 生成32位随机码
+   * @return
+   */
+  public static String create() {
+    long timeMillis = (System.currentTimeMillis() * 10000) + 0x01B21DD213814000L;
+
+    lock.lock();
+    try {
+      if (timeMillis > lastTime) {
+        lastTime = timeMillis;
+      } else {
+        timeMillis = ++lastTime;
+      }
+    } finally {
+      lock.unlock();
+    }
+
+    long mostSigBits = timeMillis << 32;
+    mostSigBits |= (timeMillis & 0xFFFF00000000L) >> 16;
+    mostSigBits |= 0x1000 | ((timeMillis >> 48) & 0x0FFF); 
+    return new UUID(mostSigBits, leastSigBits).toString().replaceAll("-", "");
+    
+  }
+  
+  public static void main(String[] args){
+    System.out.println(random());
+    System.out.println(create());
+  }
+
+}
diff --git a/deploy-manifests/k8s/01-sw-backend-base.yaml b/deploy-manifests/k8s/01-sw-backend-base.yaml
new file mode 100644
index 0000000..ac92dd7
--- /dev/null
+++ b/deploy-manifests/k8s/01-sw-backend-base.yaml
@@ -0,0 +1,53 @@
+# sw-backend-base.yaml
+
+####################################################
+# namespace
+####################################################
+apiVersion: v1
+kind: Namespace
+metadata:
+  name: sw-admin-framework
+  # labels:
+  #   istio-injection: enabled
+
+####################################################
+# supwisdom harbor private docker registry
+####################################################
+---
+apiVersion: v1
+kind: Secret
+type: kubernetes.io/dockerconfigjson
+metadata:
+  namespace: sw-admin-framework
+  name: harbor-supwisdom
+data:
+  .dockerconfigjson: eyJhdXRocyI6eyJoYXJib3Iuc3Vwd2lzZG9tLmNvbSI6eyJwYXNzd29yZCI6IlBXTWdQODVxaUxGQyIsInVzZXJuYW1lIjoicmFuY2hlci5kZXZvcHMifX19
+
+####################################################
+# mysql-server
+####################################################
+---
+apiVersion: v1
+kind: Service
+metadata:
+  namespace: sw-admin-framework
+  name: mysql-server
+spec:
+  ports:
+  - name: tcp-mysql
+    port: 3306
+    protocol: TCP
+    targetPort: 10021
+---
+kind: Endpoints
+apiVersion: v1
+metadata:
+  namespace: sw-admin-framework
+  name: mysql-server
+subsets:
+  - addresses:
+      - ip: 101.231.81.202
+    ports:
+      - name: tcp-mysql
+        port: 10021
+        protocol: TCP
diff --git a/deploy-manifests/k8s/02-sw-backend-env.yaml b/deploy-manifests/k8s/02-sw-backend-env.yaml
new file mode 100644
index 0000000..6c060ff
--- /dev/null
+++ b/deploy-manifests/k8s/02-sw-backend-env.yaml
@@ -0,0 +1,33 @@
+# sw-backend-env.yaml
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  namespace: sw-admin-framework
+  name: jvm-env
+data:
+  MAX_RAM_PERCENTAGE: "75.0"
+
+---
+apiVersion: v1
+kind: Secret
+metadata:
+  namespace: sw-admin-framework
+  name: datasource-env-secret
+type: Opaque
+data:
+  JDBC_URL: amRiYzpteXNxbDovL215c3FsLXNlcnZlcjozMzA2L3N3LWFkbWlu
+  JDBC_USERNAME: c3ctYWRtaW4=
+  JDBC_PASSWORD: a2luZ3N0YXI=
+
+---
+apiVersion: v1
+kind: Secret
+metadata:
+  namespace: sw-admin-framework
+  name: redis-env-secret
+type: Opaque
+data:
+  SPRING_REDIS_HOST: cmVkaXMtc2VydmVy
+  SPRING_REDIS_PORT: NjM3OQ==
+  SPRING_REDIS_PASSWORD: OEt1d29zbE9pdXc3SA==
diff --git a/deploy-manifests/k8s/03-sw-backend-ingresses.yaml b/deploy-manifests/k8s/03-sw-backend-ingresses.yaml
new file mode 100644
index 0000000..682475d
--- /dev/null
+++ b/deploy-manifests/k8s/03-sw-backend-ingresses.yaml
@@ -0,0 +1,17 @@
+# sw-backend-ingresses.yaml
+
+---
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+  namespace: sw-admin-framework
+  name: sw-backend-ingress
+spec:
+  rules:
+  - host: sw-backend.supwisdom.com
+    http:
+      paths:
+      - path: /
+        backend:
+          serviceName: sw-backend-gateway-svc
+          servicePort: http
diff --git a/deploy-manifests/k8s/04-1-sw-backend-gateway.yaml b/deploy-manifests/k8s/04-1-sw-backend-gateway.yaml
new file mode 100644
index 0000000..e888025
--- /dev/null
+++ b/deploy-manifests/k8s/04-1-sw-backend-gateway.yaml
@@ -0,0 +1,124 @@
+# sw-backend-gateway.yaml
+
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  namespace: sw-admin-framework
+  name: sw-backend-gateway-env
+data:
+  SERVER_PORT: "8080"
+  SSL_ENABLED: "false"
+  #SSL_KEYSTORE_FILE: file:/certs/server/server.keystore
+  #SSL_TRUSTSTORE_FILE: file:/certs/server/server.truststore
+
+  SW_BACKEND_BFF_API_URI: http://sw-backend-admin-bff-svc.sw-admin-framework.svc.cluster.local:8080
+
+  SW_BACKEND_BASE_API_URI: http://sw-backend-admin-sa-svc.sw-admin-framework.svc.cluster.local:8080
+  SW_BACKEND_SYSTEM_API_URI: http://sw-backend-admin-sa-svc.sw-admin-framework.svc.cluster.local:8080
+  SW_BACKEND_BIZ_API_URI: http://sw-backend-biz-sa-svc.sw-admin-framework.svc.cluster.local:8080
+
+  INFRAS_SECURITY_BASIC_REACTIVE_ENABLED: "false"
+
+  INFRAS_SECURITY_JWT_REACTIVE_ENABLED: "true"
+
+  INFRAS_SECURITY_CAS_REACTIVE_ENABLED: "false"
+  APP_SERVER_HOST_URL: "https://sw-backend.supwisdom.com"
+  #APP_LOGIN_URL: "/cas/login"
+  #APP_LOGOUT_URL: "/cas/logout"
+  CAS_SERVER_HOST_URL: "https://cas.supwisdom.com/cas"
+
+  #SW_BACKEND_BASE_API_URI: http://sw-backend-admin-sa-svc.sw-admin-framework.svc.cluster.local:8080
+  SW_BACKEND_BASE_API_CLIENT_AUTH_ENABLED: "false"
+  #SW_BACKEND_BASE_API_CLIENT_AUTH_KEYSTORE_FILE: file:/certs/common/common.keystore
+  #SW_BACKEND_BASE_API_CLIENT_AUTH_TRUSTSTORE_FILE: file:/certs/common/common.truststore
+
+---
+apiVersion: v1
+kind: Secret
+metadata:
+  namespace: sw-admin-framework
+  name: sw-backend-gateway-env-secret
+type: Opaque
+data:
+  #SSL_KEYSTORE_PASSWORD: ""
+  #SSL_TRUSTSTORE_PASSWORD: ""
+
+  INFRAS_SECURITY_JWT_PUBLIC_KEY_PEM: LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlHZk1BMEdDU3FHU0liM0RRRUJBUVVBQTRHTkFEQ0JpUUtCZ1FEMUdmZEo0M0N5N01tandpMUw2VFNMWEx5R0syNVB2c0pXRWVKOXpNNlBXa0hCUytGSmJzL0pkK0lUeUlWdWd3ZWxxNXBGT3JGSmd5WGJoQ2FxaTFCWUlSZ0tKYnJpSzFoS0lhUStWVnVVbVBFaEIweFpydGhsa0NHY1VJVHEyY3J6ZnhwTFFDUzFTZXhzaW5Dd21td09aMlpUeGNTQ1VtcnJXeFlNUDQxUXJ3SURBUUFCCi0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQ==
+  INFRAS_SECURITY_JWT_PRIVATE_KEY_PEM_PKCS8: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUNlQUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQW1Jd2dnSmVBZ0VBQW9HQkFQVVo5MG5qY0xMc3lhUENMVXZwTkl0Y3ZJWXJiaysrd2xZUjRuM016bzlhUWNGTDRVbHV6OGwzNGhQSWhXNkRCNldybWtVNnNVbURKZHVFSnFxTFVGZ2hHQW9sdXVJcldFb2hwRDVWVzVTWThTRUhURm11MkdXUUlaeFFoT3JaeXZOL0drdEFKTFZKN0d5S2NMQ2FiQTVuWmxQRnhJSlNhdXRiRmd3L2pWQ3ZBZ01CQUFFQ2dZQXY1NEJPbjdaaU1pZUM2eXZCUDNZMm1zeDZDTGtKdXdYdW5wWSs4aTJaRlJIdS9xNnNsSXptR3BsRU5wZmxycFJyQyt3ZUJjZXF6NGd4ckZXR1ZhNUErV1BFTFJpeU4wNjVQaGRJdm85M2grWkFRODRBcWNQVXJ6WnBIMmROZ0QzSzB6ODlzWithNjBUM3A4aDIwM0k5enMxalpZMFdORC9IVnc3d2twZWVFUUpCQVB2V3R6dnhqVndNSkRqaVk0R012cWtPNEFJSGdtK1lqMW43TEJ6b2ZTQzFlbkRPRU1EM20renJwRXV0eXl6dmtCS1JzenVoRnB5NU0xL2FHbWtpSDVrQ1FRRDVKc0JSclF1QVVHOGZGcWlRMmM1Z0FtTmE5cHRBLzQyUHppSTRXL1N5ZTQ5TXB4RFpTVzJsZzZ2ek5raHJmSUlsMXZZa0FDRmxNTy84T2xWTUNEK0hBa0VBMTRsckJYaTAvV1MrMDVpZWhWQUtGZkxQTWExdnEwY3MyVndvNHd6dm1zRDNhL2hSU25ZaEUyS1NHTnREbXMvbHhKN0NnWFJiUUFNWnZ4MlJvUTA5Y1FKQkFNUkhCK2tRSnVoZDlUeUxrQjRVeUNVUW5JN3ppWmxwK1c2Wm1KSEh0M3pJSkRyaHZqOC9QbmJPeFM1anpDZUpQY3ByanhzTFUwT3hpczJzY3Jma0k0OENRUURYSWVVSVFna2lLenltT2Yxek5uN1ZpL0NQUlJTSzZTdWxrZGhpeURhZ3VtZGRWOS9QbFduakE5amhiczJ5L1AySkxOYUZzV2lNcXh6eGxmK2RLNlFWCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0=
+
+  #SW_BACKEND_BASE_API_CLIENT_AUTH_KEY_PASSWORD: ""
+  #SW_BACKEND_BASE_API_CLIENT_AUTH_KEYSTORE_PASSWORD: ""
+  #SW_BACKEND_BASE_API_CLIENT_AUTH_TRUSTSTORE_PASSWORD: ""
+
+---
+apiVersion: v1
+kind: Service
+metadata:
+  namespace: sw-admin-framework
+  name: sw-backend-gateway-svc
+  labels:
+    app: sw-backend-gateway-svc
+    needMonitor: 'true'
+spec:
+  ports:
+    - port: 8080
+      targetPort: http
+      protocol: TCP
+      name: http
+    - port: 6060
+      targetPort: http-metrics
+      protocol: TCP
+      name: http-metrics
+  selector:
+    app: sw-backend-gateway
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  namespace: sw-admin-framework
+  name: sw-backend-gateway
+spec:
+  selector:
+    matchLabels:
+      app: sw-backend-gateway
+  replicas: 1
+  template:
+    metadata:
+      labels:
+        app: sw-backend-gateway
+    spec:
+      containers:
+      - name: sw-backend-gateway
+        image: harbor.supwisdom.com/sw-admin-framework/sw-backend-gateway:0.0.1-SNAPSHOT
+        imagePullPolicy: Always
+        ports:
+        - containerPort: 8080
+          name: http
+        - containerPort: 6060
+          name: http-metrics
+        envFrom:
+        - configMapRef:
+            name: jvm-env
+        - secretRef:
+            name: redis-env-secret
+        - secretRef:
+            name: sw-backend-gateway-env-secret
+        - configMapRef:
+            name: sw-backend-gateway-env
+        resources:
+          requests:
+            memory: "400Mi"
+          limits:
+            memory: "400Mi"
+        readinessProbe:
+          httpGet:
+            path: /actuator/health
+            port: 8080
+          initialDelaySeconds: 20
+          periodSeconds: 5
+          timeoutSeconds: 5
+          successThreshold: 1
+          failureThreshold: 10
+      imagePullSecrets:
+        - name: harbor-supwisdom
diff --git a/deploy-manifests/k8s/04-2-sw-backend-admin-bff.yaml b/deploy-manifests/k8s/04-2-sw-backend-admin-bff.yaml
new file mode 100644
index 0000000..8bb4c60
--- /dev/null
+++ b/deploy-manifests/k8s/04-2-sw-backend-admin-bff.yaml
@@ -0,0 +1,121 @@
+# sw-backend-admin-bff.yaml
+
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  namespace: sw-admin-framework
+  name: sw-backend-admin-bff-env
+data:
+  SERVER_PORT: "8080"
+  SSL_ENABLED: "false"
+  #SSL_KEYSTORE_FILE: file:/certs/server/server.keystore
+  #SSL_TRUSTSTORE_FILE: file:/certs/server/server.truststore
+
+  SW_BACKEND_BASE_API_URI: http://sw-backend-admin-sa-svc.sw-admin-framework.svc.cluster.local:8080
+  SW_BACKEND_BASE_API_CLIENT_AUTH_ENABLED: "false"
+  #SW_BACKEND_BASE_API_CLIENT_AUTH_KEYSTORE_FILE: file:/certs/common/common.keystore
+  #SW_BACKEND_BASE_API_CLIENT_AUTH_TRUSTSTORE_FILE: file:/certs/common/common.truststore
+
+  SW_BACKEND_SYSTEM_API_URI: http://sw-backend-admin-sa-svc.sw-admin-framework.svc.cluster.local:8080
+  SW_BACKEND_SYSTEM_API_CLIENT_AUTH_ENABLED: "false"
+  #SW_BACKEND_SYSTEM_API_CLIENT_AUTH_KEYSTORE_FILE: file:/certs/common/common.keystore
+  #SW_BACKEND_SYSTEM_API_CLIENT_AUTH_TRUSTSTORE_FILE: file:/certs/common/common.truststore
+
+  SW_BACKEND_BIZ_API_URI: http://sw-backend-biz-sa-svc.sw-admin-framework.svc.cluster.local:8080
+  SW_BACKEND_BIZ_API_CLIENT_AUTH_ENABLED: "false"
+  #SW_BACKEND_BIZ_API_CLIENT_AUTH_KEYSTORE_FILE: file:/certs/common/common.keystore
+  #SW_BACKEND_BIZ_API_CLIENT_AUTH_TRUSTSTORE_FILE: file:/certs/common/common.truststore
+
+---
+apiVersion: v1
+kind: Secret
+metadata:
+  namespace: sw-admin-framework
+  name: sw-backend-admin-bff-env-secret
+type: Opaque
+data:
+  #SSL_KEYSTORE_PASSWORD: ""
+  #SSL_TRUSTSTORE_PASSWORD: ""
+
+  #SW_BACKEND_BASE_API_CLIENT_AUTH_KEY_PASSWORD: ""
+  #SW_BACKEND_BASE_API_CLIENT_AUTH_KEYSTORE_PASSWORD: ""
+  #SW_BACKEND_BASE_API_CLIENT_AUTH_TRUSTSTORE_PASSWORD: ""
+
+  #SW_BACKEND_SYSTEM_API_CLIENT_AUTH_KEY_PASSWORD: ""
+  #SW_BACKEND_SYSTEM_API_CLIENT_AUTH_KEYSTORE_PASSWORD: ""
+  #SW_BACKEND_SYSTEM_API_CLIENT_AUTH_TRUSTSTORE_PASSWORD: ""
+
+  #SW_BACKEND_BIZ_API_CLIENT_AUTH_KEY_PASSWORD: ""
+  #SW_BACKEND_BIZ_API_CLIENT_AUTH_KEYSTORE_PASSWORD: ""
+  #SW_BACKEND_BIZ_API_CLIENT_AUTH_TRUSTSTORE_PASSWORD: ""
+
+---
+apiVersion: v1
+kind: Service
+metadata:
+  namespace: sw-admin-framework
+  name: sw-backend-admin-bff-svc
+  labels:
+    app: sw-backend-admin-bff-svc
+    needMonitor: 'true'
+spec:
+  ports:
+    - port: 8080
+      targetPort: http
+      protocol: TCP
+      name: http
+    - port: 6060
+      targetPort: http-metrics
+      protocol: TCP
+      name: http-metrics
+  selector:
+    app: sw-backend-admin-bff
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  namespace: sw-admin-framework
+  name: sw-backend-admin-bff
+spec:
+  selector:
+    matchLabels:
+      app: sw-backend-admin-bff
+  replicas: 1
+  template:
+    metadata:
+      labels:
+        app: sw-backend-admin-bff
+    spec:
+      containers:
+      - name: sw-backend-admin-bff
+        image: harbor.supwisdom.com/sw-admin-framework/sw-backend-admin-bff:0.0.1-SNAPSHOT
+        imagePullPolicy: Always
+        ports:
+        - containerPort: 8080
+          name: http
+        - containerPort: 6060
+          name: http-metrics
+        envFrom:
+        - configMapRef:
+            name: jvm-env
+        - secretRef:
+            name: sw-backend-admin-bff-env-secret
+        - configMapRef:
+            name: sw-backend-admin-bff-env
+        resources:
+          requests:
+            memory: "400Mi"
+          limits:
+            memory: "400Mi"
+        readinessProbe:
+          httpGet:
+            path: /actuator/health
+            port: 8080
+          initialDelaySeconds: 20
+          periodSeconds: 5
+          timeoutSeconds: 5
+          successThreshold: 1
+          failureThreshold: 10
+      imagePullSecrets:
+        - name: harbor-supwisdom
diff --git a/deploy-manifests/k8s/04-3-sw-backend-admin-sa.yaml b/deploy-manifests/k8s/04-3-sw-backend-admin-sa.yaml
new file mode 100644
index 0000000..a03da7b
--- /dev/null
+++ b/deploy-manifests/k8s/04-3-sw-backend-admin-sa.yaml
@@ -0,0 +1,96 @@
+# sw-backend-admin-sa.yaml
+
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  namespace: sw-admin-framework
+  name: sw-backend-admin-sa-env
+data:
+  SERVER_PORT: "8080"
+  SSL_ENABLED: "false"
+  #SSL_KEYSTORE_FILE: file:/certs/server/server.keystore
+  #SSL_TRUSTSTORE_FILE: file:/certs/server/server.truststore
+
+---
+apiVersion: v1
+kind: Secret
+metadata:
+  namespace: sw-admin-framework
+  name: sw-backend-admin-sa-env-secret
+type: Opaque
+data:
+  #SSL_KEYSTORE_PASSWORD: ""
+  #SSL_TRUSTSTORE_PASSWORD: ""
+
+---
+apiVersion: v1
+kind: Service
+metadata:
+  namespace: sw-admin-framework
+  name: sw-backend-admin-sa-svc
+  labels:
+    app: sw-backend-admin-sa-svc
+    needMonitor: 'true'
+spec:
+  ports:
+    - port: 8080
+      targetPort: http
+      protocol: TCP
+      name: http
+    - port: 6060
+      targetPort: http-metrics
+      protocol: TCP
+      name: http-metrics
+  selector:
+    app: sw-backend-admin-sa
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  namespace: sw-admin-framework
+  name: sw-backend-admin-sa
+spec:
+  selector:
+    matchLabels:
+      app: sw-backend-admin-sa
+  replicas: 1
+  template:
+    metadata:
+      labels:
+        app: sw-backend-admin-sa
+    spec:
+      containers:
+      - name: sw-backend-admin-sa
+        image: harbor.supwisdom.com/sw-admin-framework/sw-backend-admin-sa:0.0.1-SNAPSHOT
+        imagePullPolicy: Always
+        ports:
+        - containerPort: 8080
+          name: http
+        - containerPort: 6060
+          name: http-metrics
+        envFrom:
+        - configMapRef:
+            name: jvm-env
+        - secretRef:
+            name: datasource-env-secret
+        - secretRef:
+            name: sw-backend-admin-sa-env-secret
+        - configMapRef:
+            name: sw-backend-admin-sa-env
+        resources:
+          requests:
+            memory: "400Mi"
+          limits:
+            memory: "400Mi"
+        readinessProbe:
+          httpGet:
+            path: /actuator/health
+            port: 8080
+          initialDelaySeconds: 20
+          periodSeconds: 5
+          timeoutSeconds: 5
+          successThreshold: 1
+          failureThreshold: 10
+      imagePullSecrets:
+        - name: harbor-supwisdom
diff --git a/doc/DEV_SPEC.md b/doc/DEV_SPEC.md
new file mode 100644
index 0000000..45a45c8
--- /dev/null
+++ b/doc/DEV_SPEC.md
@@ -0,0 +1,419 @@
+
+# 开发规范
+
+## 开发规范
+
+见 [开发规范](https://supwisdom.coding.net/p/develop-standard/git/blob/master/README.md)
+
+
+## 接口规范
+
+定义了接口的路径规则,接口的请求参数,接口的响应数据等规则
+
+
+响应状态码:
+```
+200    OK
+201    Created
+204    No Content
+```
+
+
+### 查询分页
+请求地址:GET /v1/examples?pageIndex=0&pageSize=20&mapBean[username]=xxx
+
+参数:(query)
+pageIndex
+pageSize
+<其他条件>
+
+响应:
+状态码:200 OK
+```
+{
+  "code": 0,
+  "message": null,
+  "data": {
+    "pageIndex": 0,
+    "pageSize": 20,
+    "currentCount": 8,
+    "totalCount": 96,
+
+    "items": [
+      {实体对象},
+      ……
+    ]
+  }
+}
+```
+
+
+### 查询数据(按主键)
+请求地址:GET /v1/examples/{id}
+
+参数:(path)
+id
+
+响应:
+状态码:200 OK
+```
+{
+  "code": 0,
+  "message": null,
+  "data": {
+    实体对象
+  }
+}
+```
+
+
+### 新增数据
+请求地址:POST /v1/examples
+
+requestBody:(application/json)
+```
+{
+  新增请求的对象VO
+}
+```
+
+响应:
+状态码:201 Created
+```
+{
+  "code": 0,
+  "message": null,
+  "data": {
+    新增成功的实体对象
+  }
+}
+```
+
+
+### 更新数据
+请求地址:PUT /v1/examples/{id}
+requestBody:(application/json)
+
+```
+{
+  更新请求的对象VO
+}
+```
+
+响应:
+状态码:200 OK
+```
+{
+  "code": 0,
+  "message": null,
+  "data": {
+    更新成功的实体对象
+  }
+}
+```
+
+
+### 删除数据
+请求地址:DELETE /v1/examples/{id}
+
+参数:(path)
+id
+
+响应:
+状态码:200 OK
+
+```
+{
+  "code": 0,
+  "message": null,
+  "data": {
+
+  }
+}
+```
+
+
+### 判断数据唯一
+请求地址:GET /v1/examples/exist-code
+
+参数:(query)
+code    必须
+id      可选
+
+响应:
+状态码:200 OK
+```
+{
+  "code": 0,
+  "message": null,
+  "data": {
+    "exist": true/false
+  }
+}
+```
+
+
+
+## 异常响应规范
+
+响应状态码:
+```
+400    Bad Request
+401    Unauthorized
+403    Forbidden
+404    Not Found
+
+500    Internal Server Error
+```
+
+### 400 Bad Request
+请求错误(校验类异常,数据格式错误等情况)
+
+响应:
+状态码:400 Bad Request
+```
+{
+  "code": -1,
+  "message": "Bad Request",
+  "error": 'Bad Request'
+}
+```
+
+### 401 Unauthorized
+spring security 的未认证的异常
+
+响应:
+状态码:401 Unauthorized
+```
+{
+  "code": -1,
+  "message": "Unauthorized",
+  "error": 'Unauthorized'
+}
+```
+
+```
+{"error":"unauthorized","error_description":"Full authentication is required to access this resource"}
+```
+
+### 403 Forbidden
+
+响应:
+状态码:403 Forbidden
+```
+{
+  "code": -1,
+  "message": "Forbidden",
+  "error": 'Forbidden'
+}
+```
+
+### 404 Not Found
+spring 框架异常,未找到
+
+响应:
+状态码:404 Not Found
+```
+{
+  "code": -1,
+  "message": "Not Found",
+  "error": 'Not Found'
+}
+```
+
+```
+{"timestamp":1531211597530,"status":404,"error":"Not Found","message":"Not Found","path":"/uaa/api/user2"}
+```
+
+### 500 Internal Server Error
+
+
+响应:
+状态码:500 Internal Server Error
+```
+{
+  "code": -1,
+  "message": "Code is null",
+  "error": 'Code is null'
+}
+```
+
+
+### 502 Bad Gateway
+网关熔断时,返回的错误码
+
+响应:
+状态码:502 Bad Gateway
+```
+{
+  "code": -1,
+  "message": "Bad Gateway",
+  "error": 'Bad Gateway'
+}
+```
+
+
+
+## 项目构建
+
+```bash
+mvn clean package
+```
+
+### 构建 spring-boot 可执行jar
+
+修改 pom.xml
+```xml
+  <properties>
+
+    <start-class>com.supwisdom.insitute.Application</start-class>
+  </properties>
+
+  <build>
+
+    <plugins>
+
+      <plugin>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-maven-plugin</artifactId>
+      </plugin>
+
+    </plugins>
+
+  </build>
+```
+- 在 properties 下,配置 start-class
+- 在 project.build.plugins 中,添加 spring-boot-maven-plugin 插件
+
+
+### 构建 docker 镜像
+
+在须构建成 docker 镜像的项目中,添加 Dockerfile,并修改 pom.xml
+
+参考示例:
+
+Dockerfile
+
+```
+FROM harbor.supwisdom.com/institute/openjdk:8-jre-alpine
+
+ARG NAME
+ARG VERSION
+ARG JAR_FILE
+
+LABEL name=$NAME \
+      version=$VERSION
+
+ENV ENABLE_JMX_SSL=false
+ENV JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -Dspring.profiles.active=docker
+ENV SPRING_PROFILES_ACTIVE=docker
+
+COPY --chown=java-app:java-app target/${JAR_FILE} /home/java-app/lib/app.jar
+
+EXPOSE 8080
+```
+
+pom.xml
+
+```xml
+  <build>
+    <finalName>${project.artifactId}</finalName>
+
+    <plugins>
+
+      <plugin>
+        <groupId>com.spotify</groupId>
+        <artifactId>dockerfile-maven-plugin</artifactId>
+        <configuration>
+          <skip>false</skip>
+        </configuration>
+      </plugin>
+
+    </plugins>
+
+  </build>
+```
+- 指定 project.build.finalName
+- 在 project.build.plugins 中,添加 dockerfile-maven-plugin 插件
+
+
+执行构建
+
+```bash
+mvn clean package dockerfile:build
+```
+
+若要推送到镜像服务器,须添加配置,并联系 harbor 管理员为您添加权限、创建项目等
+
+- 配置 maven settings.xml,在 servers 下添加配置
+
+```xml
+    <server>
+        <id>harbor.supwisdom.com</id>
+        <username>your harbor username</username>
+        <password>your harbor password</password>
+    </server>
+```
+
+- 执行推送命令
+
+```bash
+mvn dockerfile:push
+```
+
+
+
+## Git 说明
+
+代码提交时,规范commit 描述信息的规范
+
+Commit message 都包括三个部分:header,body 和 footer。
+
+```
+<type>(<scope>): #<issue-no-1>, #<issue-no-2>, <subject>
+<BLANK LINE>
+<body>
+<BLANK LINE>
+<footer>
+```
+
+Header 包括:
+* type 必须
+* scope 可选,用于说明 commit 影响的范围,比如数据层、控制层、视图层等等,视项目不同而不同
+* issue-no 可选,可多个
+* subject 必须
+
+type
+
+用于说明 commit 的类别,只允许使用下面7个标识。
+
+* feat:新功能(feature)
+* fix:修补bug
+* refactor:重构(即不是新增功能,也不是修改bug的代码变动)
+* style: 格式(不影响代码运行的变动)
+* test:增加测试
+* docs:文档(documentation)
+* chore:构建过程或辅助工具的变动
+
+
+Body 详细描述,可选
+
+Footer 对 不兼容变动,关闭 等提交的描述,可选(保留、暂时不用)
+
+
+提交示例:
+
+```
+feat: #14, 初始化项目结构
+```
+
+```
+doc: #1, #2, 完善sa-api的设计、新增sec-engine的设计
+```
+
+```
+chore: 整理开发说明
+
+由于开发过程中,使用了 redis、kafka 等中间件服务,故需要事先安装相关服务
+为了方便,考虑采用 docker-compose 来启动相关服务
+```
diff --git a/doc/USAGE.md b/doc/USAGE.md
new file mode 100644
index 0000000..2f1ce7a
--- /dev/null
+++ b/doc/USAGE.md
@@ -0,0 +1,688 @@
+# 应用开发框架 - 项目开发说明
+
+
+[TOC]
+
+
+## 项目介绍
+
+
+### 公共类库
+
+common,提供公用的工具类、框架代码
+
+
+
+### 业务类库
+
+以 业务领域 为单位,实现 业务领域、业务接口,如:
+
+* base,基础功能
+* system,系统功能
+* biz,业务示例
+
+每个 业务领域项目下,又分为 领域层 domain、接口层 api
+
+业务类库中,尽量以通用服务的方式实现业务
+
+
+
+### 微服务项目 sa
+
+将 业务类库 包装为 微服务,对外提供 RESTful API,以及相关的接口文档的访问、测试
+
+
+
+### 后端项目 bff
+
+提供 面向UI 的后端接口
+
+对服务接口的转发(建议,避免使用)
+
+对服务接口的聚合、裁剪、适配
+
+提供用户认证,保护接口的访问权限
+
+
+
+## 项目开发
+
+
+### 业务类库
+
+面对业务需求,设计时,进行领域划分,保证划分的粒度适中
+
+* 在 领域层 domain,创建 实体 entity、数据传输对象 dto、持久化 repo、业务逻辑 service
+
+* 在 接口层 api,创建 值对象 vo、接口 api
+
+#### 实体 entity
+
+```java
+package com.supwisdom.institute.backend.biz.domain.entity;
+
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.entity.ABaseEntity;
+
+@Entity
+@Table(name = "TB_BIZ")
+public class Biz extends ABaseEntity {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 5503233707196628811L;
+  
+  @Getter
+  @Setter
+  @Column(name = "NAME")
+  private String name;
+  
+  @Getter
+  @Setter
+  @Column(name = "BOOL")
+  private Boolean bool;
+  
+  @Getter
+  @Setter
+  @Column(name = "DATE")
+  private Date date;
+
+  @Getter
+  @Setter
+  @Column(name = "NUM")
+  private Integer num;
+
+}
+
+```
+
+继承 `ABaseEntity`
+
+注解 `@Entity`,指定该 class 为一个实体
+
+注解 `@Table`,指定该实体对应的数据库表的表名
+
+注解 `@Column`,指定该属性对应的数据库表的字段名
+
+表名、字段名 都采用大写,多个单词用 `下划线(_)` 隔开
+
+
+
+#### 数据传输对象 dto
+
+```java
+
+```
+
+
+#### 持久化 repo
+
+```java
+package com.supwisdom.institute.backend.biz.domain.repo;
+
+import java.util.Map;
+
+import org.springframework.data.domain.Page;
+import org.springframework.stereotype.Repository;
+
+import com.supwisdom.institute.backend.biz.domain.entity.Biz;
+import com.supwisdom.institute.backend.common.framework.repo.BaseJpaRepository;
+
+@Repository
+public interface BizRepository extends BaseJpaRepository<Biz> {
+  
+  @Override
+  public default Page<Biz> selectPageList(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean, Map<String, String> orderBy) {
+    
+    
+    return null;
+  }
+
+}
+
+```
+
+继承 `BaseJpaRepository<E>`,基类中,已经实现了基本的 CURD 逻辑,方法如下:
+
+* 分页查询
+```java
+  public default Page<E> selectPageList(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean, Map<String, String> orderBy)
+```
+
+* 根据ID获取
+```java
+  public default E selectById(String id)
+```
+
+* 新增
+```java
+  public default E insert(E entity)
+```
+
+* 更新
+```java
+  public default E update(E entity)
+```
+
+* 删除
+```java
+  public default void delete(String id)
+```
+
+
+注解 `@Repository`,指定该 class 为一个数据持久化类
+
+其中 `BaseJpaRepository` 的 `selectPageList` 方法,默认返回所有数据,不会处理查询条件、排序,故一般都需要由业务自行重写
+
+```java
+  default Specification<Biz> convertSpecification(Map<String, Object> mapBean) {
+
+    Specification<Biz> spec = new Specification<Biz>() {
+
+      /**
+       * 
+       */
+      private static final long serialVersionUID = -1820403213133310124L;
+
+      @Override
+      public Predicate toPredicate(Root<Biz> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
+        List<Predicate> predicates = new ArrayList<>();
+        
+        if (mapBean != null) {
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "name"))) {
+            predicates.add(criteriaBuilder.like(root.get("name"), MapBeanUtils.getString(mapBean, "name")));
+          }
+
+          if (!StringUtils.isEmpty(MapBeanUtils.getBoolean(mapBean, "bool"))) {
+            predicates.add(criteriaBuilder.equal(root.get("bool"), MapBeanUtils.getBoolean(mapBean, "bool")));
+          }
+          
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "dateBegin"))) {
+            String grantTimeBegin = MapBeanUtils.getString(mapBean, "dateBegin");
+            Date d = DateUtil.parseDate(grantTimeBegin+" 00:00:00", "yyyy-MM-dd HH:mm:ss");
+            
+            if (d != null) {
+              predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("date"), d));
+            }
+          }
+
+          if (!StringUtils.isEmpty(MapBeanUtils.getString(mapBean, "dateEnd"))) {
+            String grantTimeEnd = MapBeanUtils.getString(mapBean, "dateEnd");
+            Date d = DateUtil.parseDate(grantTimeEnd+" 23:59:59", "yyyy-MM-dd HH:mm:ss");
+            
+            if (d != null) {
+              predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("date"), d));
+            }
+          }
+        }
+        
+        return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
+      }
+      
+    };
+    
+    return spec;
+  }
+  
+  @Override
+  public default Page<Biz> selectPageList(boolean loadAll, int pageIndex, int pageSize, Map<String, Object> mapBean, Map<String, String> orderBy) {
+    
+    Specification<Biz> spec = this.convertSpecification(mapBean);
+
+    if (loadAll) {
+      pageIndex = 0;
+      pageSize = Integer.MAX_VALUE;
+    }
+    
+    Sort sort = new Sort(Sort.Direction.DESC, "date");  // Sort.unsorted
+
+    if (orderBy != null) {
+      List<Order> orders = new ArrayList<>();
+      
+      orderBy.forEach((k, v) -> {
+        if ("asc".equalsIgnoreCase(v)) {
+          Order order = Order.asc(k);
+          orders.add(order);
+        } else if ("desc".equalsIgnoreCase(v)) {
+          Order order = Order.desc(k);
+          orders.add(order);
+        } else {
+          Order order = Order.by(k);
+          orders.add(order);
+        }
+      });
+      
+      sort = Sort.by(orders);
+    }
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize, sort);
+    
+    return this.findAll(spec, pageRequest);
+  }
+
+```
+
+
+
+#### 业务逻辑 service
+
+```java
+package com.supwisdom.institute.backend.biz.domain.service;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.supwisdom.institute.backend.biz.domain.entity.Biz;
+import com.supwisdom.institute.backend.biz.domain.repo.BizRepository;
+import com.supwisdom.institute.backend.common.framework.service.ABaseService;
+
+@Service
+public class BizService extends ABaseService<Biz, BizRepository> {
+  
+  @Autowired
+  private BizRepository bizRepository;
+
+  @Override
+  public BizRepository getRepo() {
+    return bizRepository;
+  }
+
+}
+
+```
+
+继承 `ABaseService<E, REPO>`
+
+注解 `@Service`,指定该 class 为一个业务逻辑类
+
+注入 业务对应的 Repository,并实现 `public REPO getRepo()`
+
+
+#### 值对象 vo
+
+这里的vo,目前主要用于接口的请求、响应对象的封装
+
+
+
+#### 接口 api
+
+```java
+package com.supwisdom.institute.backend.biz.api.v1.admin;
+
+import io.swagger.annotations.Api;
+
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@Api(value = "BizAdminBiz", tags = { "BizAdminBiz" }, description = "Biz示例接口")
+@RestController
+@RequestMapping("/v1/admin/biz")
+public class AdminBizController {
+
+}
+
+```
+
+
+
+### 微服务项目
+
+* 关于 Application
+
+```java
+package com.supwisdom.institute.backend.admin.sa;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.domain.EntityScan;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+
+import com.supwisdom.infras.online.doc.configuration.EnableInfrasOnlineDoc;
+import com.supwisdom.institute.backend.common.core.transmit.annotation.EnableSimpleUserTransmit;
+import com.supwisdom.institute.backend.common.framework.exception.EnableCustomExceptionHandler;
+
+@SpringBootApplication
+
+@EnableSimpleUserTransmit
+@EnableCustomExceptionHandler
+
+@EnableInfrasOnlineDoc
+
+@EntityScan(basePackages = {"com.supwisdom.**.domain.entity"})  // 扫描子项目下的实体
+@EnableJpaRepositories(basePackages = {"com.supwisdom.**.domain.repo"})  // 扫描子项目下的持久类
+@ComponentScan(basePackages = {"com.supwisdom"})
+public class Application {
+
+  public static void main(String[] args) {
+    SpringApplication.run(Application.class, args);
+  }
+  
+  @Bean
+  public CorsFilter corsFilter() {
+    final CorsConfiguration config = new CorsConfiguration();
+    // config.setAllowCredentials(true);
+    config.addAllowedOrigin("*");
+    config.addAllowedHeader("*");
+    config.addAllowedMethod("*");
+
+    final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+    source.registerCorsConfiguration("/v2/api-docs", config);
+
+    return new CorsFilter(source);
+  }
+
+}
+
+```
+
+注解 `@SpringBootApplication`,
+
+注解 `@EnableSimpleUserTransmit`,从请求中接收 User 信息,通过 Feign 调用外部服务时,传递 User 信息
+
+注解 `@EnableCustomExceptionHandler`,将异常转换为符合开发规范的 json 数据
+
+注解 `@EntityScan`,扫描实体
+
+注解 `@EnableJpaRepositories`,扫描持久类
+
+注解 `@ComponentScan`,扫描组件,如 @Service、@Controller、@RestController 等
+
+Filter `corsFilter`,允许跨域请求,此处允许对路径 `/v2/api-docs` 的跨域请求
+
+
+* 在 pom 中,添加依赖配置
+
+```xml
+    <dependency>
+      <groupId>com.supwisdom.institute</groupId>
+      <artifactId>sw-backend-biz-api</artifactId>
+    </dependency>
+
+```
+
+* (可选)在 application.yml 中,添加业务相关的配置项
+
+
+
+### 后端项目
+
+* 关于 Application
+
+```java
+package com.supwisdom.institute.backend.admin.bff;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.context.annotation.Bean;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+
+import com.supwisdom.infras.security.configure.basic.EnableInfrasBasicApi;
+import com.supwisdom.infras.security.configure.cas.EnableInfrasCasSecurity;
+import com.supwisdom.infras.security.configure.jwt.EnableInfrasJWTApi;
+import com.supwisdom.institute.backend.common.core.transmit.annotation.EnableSimpleUserTransmit;
+import com.supwisdom.institute.backend.common.framework.exception.EnableCustomExceptionHandler;
+
+@SpringBootApplication
+@EnableFeignClients
+
+@EnableSimpleUserTransmit
+@EnableCustomExceptionHandler
+
+@EnableInfrasCasSecurity
+
+@EnableInfrasBasicApi
+@EnableInfrasJWTApi
+public class Application {
+
+  public static void main(String[] args) {
+    SpringApplication.run(Application.class, args);
+  }
+  
+  @Bean
+  public CorsFilter corsFilter() {
+    final CorsConfiguration config = new CorsConfiguration();
+    //config.setAllowCredentials(true);
+    config.addAllowedOrigin("*");
+    config.addAllowedHeader("*");
+    config.addAllowedMethod("*");
+
+    final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+    source.registerCorsConfiguration("/v2/api-docs", config);
+    source.registerCorsConfiguration("/api/**", config);  // 对 /api/** 下的请求,支持 cors 跨域请求,如不需要可以注释
+
+    return new CorsFilter(source);
+  }
+
+}
+
+```
+
+注解 `@SpringBootApplication`,
+
+注解 `@EnableFeignClients`,支持 FeignClient
+
+注解 `@EnableSimpleUserTransmit`,从请求中接收 User 信息,通过 Feign 调用外部服务时,传递 User 信息
+
+注解 `@EnableCustomExceptionHandler`,将异常转换为符合开发规范的 json 数据
+
+注解 `@EnableInfrasCasSecurity`,支持 cas,在 application.yml 可配置是否启用
+
+注解 `@EnableInfrasBasicApi`,支持 basic 认证(一般在开发环境启用),在 application.yml 可配置是否启用
+
+注解 `@EnableInfrasJWTApi`,支持 jwt 认证(一般在生产环境启用),在 application.yml 可配置是否启用
+
+
+
+* 关于接口请求
+
+后端项目中,使用服务接口的方式有 2 种,一是 请求转发,二是 远程调用
+
+
+#### 请求转发
+
+请求转发的方式,基于 spring-cloud-gateway 实现
+
+* 在 pom.xml 中,添加依赖配置
+
+```xml
+    <dependency>
+      <groupId>org.springframework.cloud</groupId>
+      <artifactId>spring-cloud-starter-gateway</artifactId>
+    </dependency>
+
+```
+
+* 在 application.yml 中,添加 spring.cloud.gateway.routes 配置
+
+```yaml
+spring:
+  cloud:
+    gateway:
+      routes:
+      - id: base-api
+        uri: http://localhost:8081
+        predicates:
+        - Path=/api/base/**
+        filters:
+        - RewritePath=/api/base/(?<suffix>.*), /$\{suffix}
+      - id: biz-api
+        uri: http://localhost:8081
+        predicates:
+        - Path=/api/biz/**
+        filters:
+        - RewritePath=/api/biz/(?<suffix>.*), /$\{suffix}
+
+```
+
+**疑问?为何不用服务注册与发现**
+
+考虑到,后期部署采用 k8s,而 k8s 会提供服务注册与发现的能力,所以此处不再考虑
+
+若存在脱离 k8s 部署的情况,可以考虑采用 nginx 实现服务端负载均衡即可,或其他方案
+
+
+#### 远程调用
+
+远程调用的方式,基于 spring-cloud-openfeign 实现
+
+* 在 pom.xml 中,添加依赖配置
+
+```xml
+    <dependency>
+      <groupId>org.springframework.cloud</groupId>
+      <artifactId>spring-cloud-starter-openfeign</artifactId>
+    </dependency>
+
+```
+
+* 在 Application 中,添加注解 `@EnableFeignClients`,`@EnableSimpleUserTransmit`
+
+注解 `@EnableFeignClients`,表示启用 FeignClients 的配置
+
+注解 `@EnableSimpleUserTransmit`,表示将 后端的登录用户信息,在请求 服务接口 的过程中,进行传递(加入到请求头 Header 中)
+
+* 在 application.yml 中,添加 服务接口地址 配置
+
+如:
+```yml
+sw-backend-biz-api.uri: http://localhost:8081
+```
+
+* 在 remote 包 下,创建 FeignClient 类、FallbackFactory 类
+
+```java
+package com.supwisdom.institute.backend.admin.bff.apis.remote.biz;
+
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import com.alibaba.fastjson.JSONObject;
+import com.supwisdom.institute.backend.admin.bff.apis.model.biz.Biz;
+
+@FeignClient(
+    name = "biz-biz-remote-feign-client",
+    url = "${sw-backend-biz-api.uri}/v1/admin/biz",
+    fallbackFactory = BizRemoteFallbackFactory.class
+)
+public interface BizRemoteFeignClient {
+  
+  @RequestMapping(method = RequestMethod.GET)
+  JSONObject query(
+      @RequestParam(name = "loadAll") boolean loadAll,
+      @RequestParam(name = "pageIndex") int pageIndex,
+      @RequestParam(name = "pageSize") int pageSize
+      
+  );
+  
+  @RequestMapping(method = RequestMethod.GET, path = "/{id}")
+  JSONObject load(
+      @PathVariable(name = "id") String id
+  );
+
+  @RequestMapping(method = RequestMethod.POST)
+  JSONObject create(
+      @RequestBody Biz biz
+  );
+
+  @RequestMapping(method = RequestMethod.PUT, path = "/{id}")
+  JSONObject update(
+      @PathVariable(name = "id") String id,
+      @RequestBody Biz biz
+  );
+  
+  @RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
+  JSONObject delete(
+      @PathVariable(name = "id") String id
+  );
+
+}
+
+```
+
+注解 `@FeignClient`,`url` 指定后端服务的地址,`fallbackFactory` 指定熔断回调处理的工厂类
+
+```java
+package com.supwisdom.institute.backend.admin.bff.apis.remote.biz;
+
+import org.springframework.stereotype.Component;
+
+import com.alibaba.fastjson.JSONObject;
+import com.supwisdom.institute.backend.admin.bff.apis.model.biz.Biz;
+import com.supwisdom.institute.backend.admin.bff.remote.FallbackError;
+
+import feign.hystrix.FallbackFactory;
+
+@Component
+public class BizRemoteFallbackFactory implements FallbackFactory<BizRemoteFeignClient> {
+
+  @Override
+  public BizRemoteFeignClient create(Throwable cause) {
+    return new BizRemoteFeignClient() {
+
+      @Override
+      public JSONObject query(boolean loadAll, int pageIndex, int pageSize) {
+        if (cause != null) {
+          cause.printStackTrace();
+        }
+        return FallbackError.defaultErrorJson(cause);
+      }
+
+      @Override
+      public JSONObject load(String id) {
+        if (cause != null) {
+          cause.printStackTrace();
+        }
+        return FallbackError.defaultErrorJson(cause);
+      }
+
+      @Override
+      public JSONObject create(Biz biz) {
+        if (cause != null) {
+          cause.printStackTrace();
+        }
+        return FallbackError.defaultErrorJson(cause);
+      }
+
+      @Override
+      public JSONObject update(String id, Biz biz) {
+        if (cause != null) {
+          cause.printStackTrace();
+        }
+        return FallbackError.defaultErrorJson(cause);
+      }
+
+      @Override
+      public JSONObject delete(String id) {
+        if (cause != null) {
+          cause.printStackTrace();
+        }
+        return FallbackError.defaultErrorJson(cause);
+      }
+      
+    };
+  }
+
+}
+
+```
\ No newline at end of file
diff --git a/doc/sw-backend.graffle b/doc/sw-backend.graffle
new file mode 100644
index 0000000..cd741bc
--- /dev/null
+++ b/doc/sw-backend.graffle
Binary files differ
diff --git a/doc/sw-backend.v1/1.arch.png b/doc/sw-backend.v1/1.arch.png
new file mode 100644
index 0000000..66589e3
--- /dev/null
+++ b/doc/sw-backend.v1/1.arch.png
Binary files differ
diff --git a/doc/sw-backend.v1/2.microservice_structure.png b/doc/sw-backend.v1/2.microservice_structure.png
new file mode 100644
index 0000000..bd4eb10
--- /dev/null
+++ b/doc/sw-backend.v1/2.microservice_structure.png
Binary files differ
diff --git a/doc/sw-backend.v1/3.bff_structure.png b/doc/sw-backend.v1/3.bff_structure.png
new file mode 100644
index 0000000..be58aa9
--- /dev/null
+++ b/doc/sw-backend.v1/3.bff_structure.png
Binary files differ
diff --git a/doc/sw-backend.v2/1.arch.png b/doc/sw-backend.v2/1.arch.png
new file mode 100644
index 0000000..0462f9d
--- /dev/null
+++ b/doc/sw-backend.v2/1.arch.png
Binary files differ
diff --git a/doc/sw-backend.v2/2.microservice_structure.png b/doc/sw-backend.v2/2.microservice_structure.png
new file mode 100644
index 0000000..bd4eb10
--- /dev/null
+++ b/doc/sw-backend.v2/2.microservice_structure.png
Binary files differ
diff --git a/doc/sw-backend.v2/3.bff_structure.png b/doc/sw-backend.v2/3.bff_structure.png
new file mode 100644
index 0000000..c607572
--- /dev/null
+++ b/doc/sw-backend.v2/3.bff_structure.png
Binary files differ
diff --git a/doc/sw-backend.v2/4.aggr_structure.png b/doc/sw-backend.v2/4.aggr_structure.png
new file mode 100644
index 0000000..854c203
--- /dev/null
+++ b/doc/sw-backend.v2/4.aggr_structure.png
Binary files differ
diff --git a/doc/sw-backend/1.arch.png b/doc/sw-backend/1.arch.png
new file mode 100644
index 0000000..e055771
--- /dev/null
+++ b/doc/sw-backend/1.arch.png
Binary files differ
diff --git a/doc/sw-backend/2.microservice_structure.png b/doc/sw-backend/2.microservice_structure.png
new file mode 100644
index 0000000..bd4eb10
--- /dev/null
+++ b/doc/sw-backend/2.microservice_structure.png
Binary files differ
diff --git a/doc/sw-backend/3.bff_structure.png b/doc/sw-backend/3.bff_structure.png
new file mode 100644
index 0000000..38b0f29
--- /dev/null
+++ b/doc/sw-backend/3.bff_structure.png
Binary files differ
diff --git a/doc/sw-backend/4.gateway_structure.png b/doc/sw-backend/4.gateway_structure.png
new file mode 100644
index 0000000..66f991e
--- /dev/null
+++ b/doc/sw-backend/4.gateway_structure.png
Binary files differ
diff --git a/doc/sw-backend/5.authn_domain.png b/doc/sw-backend/5.authn_domain.png
new file mode 100644
index 0000000..41304f7
--- /dev/null
+++ b/doc/sw-backend/5.authn_domain.png
Binary files differ
diff --git a/gateway/.gitignore b/gateway/.gitignore
new file mode 100644
index 0000000..b83d222
--- /dev/null
+++ b/gateway/.gitignore
@@ -0,0 +1 @@
+/target/
diff --git a/gateway/Dockerfile b/gateway/Dockerfile
new file mode 100644
index 0000000..372661c
--- /dev/null
+++ b/gateway/Dockerfile
@@ -0,0 +1,21 @@
+FROM harbor.supwisdom.com/institute/openjdk:8-jre
+
+ENV ENABLE_JMX_SSL=false
+ENV JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -Dspring.profiles.active=docker
+ENV SPRING_PROFILES_ACTIVE=docker
+
+ARG NAME
+ARG VERSION
+ARG JAR_FILE
+
+LABEL name=$NAME \
+      version=$VERSION
+
+EXPOSE 8080
+
+EXPOSE 8443
+
+COPY --chown=java-app:java-app target/${JAR_FILE} /home/java-app/lib/app.jar
+
+# COPY --chown=java-app:java-app target/doc /home/java-app/doc
+# COPY --chown=java-app:java-app target/api-docs /home/java-app/api-docs
diff --git a/gateway/pom.xml b/gateway/pom.xml
new file mode 100644
index 0000000..733629f
--- /dev/null
+++ b/gateway/pom.xml
@@ -0,0 +1,228 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.supwisdom.institute</groupId>
+    <artifactId>sw-backend-parent</artifactId>
+    <version>0.0.1</version>
+  </parent>
+
+  <groupId>com.supwisdom.institute</groupId>
+  <artifactId>sw-backend-gateway</artifactId>
+  <version>0.0.1</version>
+  <packaging>jar</packaging>
+
+  <name>Supwisdom Backend Framework Gateway</name>
+  <description>Supwisdom Backend Framework Gateway project</description>
+
+  <properties>
+    <start-class>com.supwisdom.institute.backend.gateway.Application</start-class>
+  </properties>
+
+  <dependencies>
+
+    <dependency>
+      <groupId>org.projectlombok</groupId>
+      <artifactId>lombok</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>javax.servlet-api</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter</artifactId>
+    </dependency>
+
+    <!-- 微服务 健康监控 -->
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-actuator</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-webflux</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.springframework.cloud</groupId>
+      <artifactId>spring-cloud-starter-gateway</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpclient</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpcore</artifactId>
+    </dependency>
+
+
+    <!-- <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-online-doc</artifactId>
+    </dependency> -->
+
+
+    <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-security</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-security</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>io.jsonwebtoken</groupId>
+      <artifactId>jjwt</artifactId>
+      <version>0.9.1</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework.security</groupId>
+      <artifactId>spring-security-cas</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>com.supwisdom.institute</groupId>
+      <artifactId>sw-backend-common-framework</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>commons-codec</groupId>
+      <artifactId>commons-codec</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>com.alibaba</groupId>
+      <artifactId>fastjson</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>io.springfox</groupId>
+      <artifactId>springfox-swagger2</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.springfox</groupId>
+      <artifactId>springfox-swagger-ui</artifactId>
+    </dependency>
+
+
+    <!-- 热部署,无需重启项目 -->
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-devtools</artifactId>
+      <scope>runtime</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+  </dependencies>
+
+  <build>
+    <finalName>${project.artifactId}</finalName>
+
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-failsafe-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-release-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+      </plugin>
+
+
+      <!-- <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-resources-plugin</artifactId>
+        <configuration>
+          <encoding>${project.build.sourceEncoding}</encoding>
+        </configuration>
+        <executions>
+          <execution>
+            <id>copy-doc-resources</id>
+            <phase>package</phase>
+            <goals>
+              <goal>copy-resources</goal>
+            </goals>
+            <configuration>
+              <encoding>utf-8</encoding>
+              <outputDirectory>${basedir}/target/doc</outputDirectory>
+              <overwrite>true</overwrite>
+              <resources>
+                <resource>
+                  <directory>${basedir}/../../doc</directory>
+                </resource>
+              </resources>
+            </configuration>
+          </execution>
+          <execution>
+            <id>copy-certs-jwt-resources</id>
+            <phase>package</phase>
+            <goals>
+              <goal>copy-resources</goal>
+            </goals>
+            <configuration>
+              <encoding>utf-8</encoding>
+              <outputDirectory>${basedir}/target/certs/jwt</outputDirectory>
+              <overwrite>true</overwrite>
+              <resources>
+                <resource>
+                  <directory>${basedir}/../../certs/jwt</directory>
+                </resource>
+              </resources>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin> -->
+
+
+      <plugin>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-maven-plugin</artifactId>
+      </plugin>
+
+      <plugin>
+        <groupId>com.spotify</groupId>
+        <artifactId>dockerfile-maven-plugin</artifactId>
+        <configuration>
+          <skip>false</skip>
+        </configuration>
+      </plugin>
+
+    </plugins>
+
+  </build>
+
+</project>
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/Application.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/Application.java
new file mode 100644
index 0000000..1ff16b3
--- /dev/null
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/Application.java
@@ -0,0 +1,81 @@
+package com.supwisdom.institute.backend.gateway;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.web.cors.reactive.CorsUtils;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.server.WebFilter;
+import org.springframework.web.server.WebFilterChain;
+
+import reactor.core.publisher.Mono;
+
+import com.supwisdom.infras.security.reactive.basic.EnableInfrasBasicWebFluxApi;
+import com.supwisdom.infras.security.reactive.jwt.EnableInfrasJWTWebFluxApi;
+import com.supwisdom.institute.backend.common.framework.exception.EnableCustomExceptionHandler;
+
+import static org.springframework.web.cors.CorsConfiguration.ALL;
+
+@SpringBootApplication
+
+@EnableScheduling
+
+@EnableCustomExceptionHandler
+
+@EnableInfrasJWTWebFluxApi
+@EnableInfrasBasicWebFluxApi
+public class Application {
+
+  public static void main(String[] args) {
+    SpringApplication.run(Application.class, args);
+  }
+  
+//  @Bean
+//  public CorsFilter corsFilter() {
+//    final CorsConfiguration config = new CorsConfiguration();
+//    //config.setAllowCredentials(true);
+//    config.addAllowedOrigin("*");
+//    config.addAllowedHeader("*");
+//    config.addAllowedMethod("*");
+//
+//    final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+//    source.registerCorsConfiguration("/v2/api-docs", config);
+//    source.registerCorsConfiguration("/api/**", config);  // 对 /api/** 下的请求,支持 cors 跨域请求,如不需要可以注释
+//
+//    return new CorsFilter(source);
+//  }
+
+  @Bean
+  public WebFilter corsFilter() {
+    return (ServerWebExchange ctx, WebFilterChain chain) -> {
+      ServerHttpRequest request = ctx.getRequest();
+      if (!CorsUtils.isCorsRequest(request)) {
+        return chain.filter(ctx);
+      }
+      HttpHeaders requestHeaders = request.getHeaders();
+      ServerHttpResponse response = ctx.getResponse();
+      HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod();
+      HttpHeaders headers = response.getHeaders();
+      headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin());
+      headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders.getAccessControlRequestHeaders());
+      if (requestMethod != null) {
+        headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name());
+      }
+      headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
+      headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, ALL);
+      // headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, MAX_AGE);
+      if (request.getMethod() == HttpMethod.OPTIONS) {
+        response.setStatusCode(HttpStatus.OK);
+        return Mono.empty();
+      }
+      return chain.filter(ctx);
+    };
+  }
+
+}
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/Account.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/Account.java
new file mode 100644
index 0000000..38f38d5
--- /dev/null
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/Account.java
@@ -0,0 +1,70 @@
+package com.supwisdom.institute.backend.gateway.authn.model;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.model.ABaseModel;
+
+@Getter
+@Setter
+public class Account extends ABaseModel {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -4889952442290543101L;
+
+  private String id;
+
+  /**
+   * 用户名
+   */
+  private String username;
+  
+  /**
+   * 密码
+   */
+  private String password;
+
+  /**
+   * 是否可用,1 可用,0 不可用,默认:1
+   */
+  private Boolean enabled;
+  /**
+   * 账号未过期,1 未过期,0 过期,默认:1
+   */
+  private Boolean accountNonExpired;
+  /**
+   * 账号未锁定,1 未锁定,0 锁定,默认:1
+   */
+  private Boolean accountNonLocked;
+  /**
+   * 密码未过期,1 未过期,0 过期,默认:1
+   */
+  private Boolean credentialsNonExpired;
+
+  /**
+   * 姓名
+   */
+  private String name;
+
+  /**
+   * 备注
+   */
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  private String status;
+
+  /**
+   * 登录手机
+   */
+  private String mobile;
+  /**
+   * 登录邮箱
+   */
+  private String email;
+
+}
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/Permission.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/Permission.java
new file mode 100644
index 0000000..53f3199
--- /dev/null
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/Permission.java
@@ -0,0 +1,69 @@
+package com.supwisdom.institute.backend.gateway.authn.model;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.model.ABaseModel;
+
+@Getter
+@Setter
+public class Permission extends ABaseModel {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -3042842657207449148L;
+
+  private String id;
+
+  /**
+   * 代码
+   */
+  private String code;
+
+  /**
+   * 名称
+   */
+  private String name;
+
+  /**
+   * 备注
+   */
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  private String status;
+
+  /**
+   * 类型(1 应用,2 菜单,3 操作)
+   */
+  private String type;
+
+  /**
+   * 菜单图标
+   */
+  private String icon;
+
+  /**
+   * URL地址
+   */
+  private String url;
+
+  /**
+   * 系统ID
+   */
+  private String applicationId;
+
+  /**
+   * 父级ID
+   */
+  private String parentId;
+
+  /**
+   * 排序
+   */
+  private Integer order;
+
+}
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/PermissionRoleSet.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/PermissionRoleSet.java
new file mode 100644
index 0000000..675e657
--- /dev/null
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/PermissionRoleSet.java
@@ -0,0 +1,5 @@
+package com.supwisdom.institute.backend.gateway.authn.model;
+
+public class PermissionRoleSet {
+
+}
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/ResourceRoleSet.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/ResourceRoleSet.java
new file mode 100644
index 0000000..2d7940c
--- /dev/null
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/ResourceRoleSet.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.gateway.authn.model;
+
+import java.util.Collection;
+
+import com.supwisdom.institute.backend.common.framework.model.ABaseModel;
+
+import lombok.Getter;
+import lombok.Setter;
+
+public class ResourceRoleSet extends ABaseModel {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -683204173918706673L;
+
+  public static final String ACCESS_ANONYMOUS = "anonymous"; // 匿名访问anonymous
+  public static final String ACCESS_AUTHENTICATE = "authenticate"; // 认证访问authenticate
+  public static final String ACCESS_AUTHORIZE = "authorize"; // 授权访问authorize
+  public static final String ACCESS_PERMIT_ALL = "permitAll"; // 允许所有permitAll
+  public static final String ACCESS_DENY_ALL = "denyAll"; // 拒绝所有denyAll
+
+  @Getter
+  @Setter
+  private String id;
+
+  /**
+   * 代码
+   */
+  @Getter
+  @Setter
+  private String code;
+
+  /**
+   * 名称
+   */
+  @Getter
+  @Setter
+  private String name;
+
+  /**
+   * 备注
+   */
+  @Getter
+  @Setter
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  @Getter
+  @Setter
+  private String status;
+
+  /**
+   * 请求方式(GET、POST、PUT、DELETE 等)
+   */
+  @Getter
+  @Setter
+  private String method;
+
+  /**
+   * 请求路径
+   */
+  @Getter
+  @Setter
+  private String path;
+
+  /**
+   * 访问规则(匿名访问anonymous、认证访问authenticate、授权访问authorize、允许所有permitAll、拒绝所有denyAll)
+   */
+  @Getter
+  @Setter
+  private String access;
+
+  @Getter
+  @Setter
+  Collection<Role> roles;
+  
+}
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/Role.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/Role.java
new file mode 100644
index 0000000..034ff84
--- /dev/null
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/Role.java
@@ -0,0 +1,39 @@
+package com.supwisdom.institute.backend.gateway.authn.model;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.model.ABaseModel;
+
+@Getter
+@Setter
+public class Role extends ABaseModel {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -8551951601186240995L;
+
+  private String id;
+
+  /**
+   * 代码
+   */
+  private String code;
+
+  /**
+   * 名称
+   */
+  private String name;
+
+  /**
+   * 备注
+   */
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  private String status;
+
+}
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/Route.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/Route.java
new file mode 100644
index 0000000..9ca66c6
--- /dev/null
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/Route.java
@@ -0,0 +1,68 @@
+package com.supwisdom.institute.backend.gateway.authn.model;
+
+import com.supwisdom.institute.backend.common.framework.model.ABaseModel;
+
+import lombok.Getter;
+import lombok.Setter;
+
+public class Route extends ABaseModel {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 1254295553813507087L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  /**
+   * 代码
+   */
+  @Getter
+  @Setter
+  private String code;
+
+  /**
+   * 名称
+   */
+  @Getter
+  @Setter
+  private String name;
+
+  /**
+   * 备注
+   */
+  @Getter
+  @Setter
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  @Getter
+  @Setter
+  private String status;
+
+  /**
+   * 路径前缀
+   */
+  @Getter
+  @Setter
+  private String pathPrefix;
+
+  /**
+   * 路由地址
+   */
+  @Getter
+  @Setter
+  private String url;
+
+  /**
+   * 是否排除前缀
+   */
+  @Getter
+  @Setter
+  private Boolean stripPrefix;
+
+}
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/remote/configuration/AuthnRestTemplateConfig.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/remote/configuration/AuthnRestTemplateConfig.java
new file mode 100644
index 0000000..789f930
--- /dev/null
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/remote/configuration/AuthnRestTemplateConfig.java
@@ -0,0 +1,100 @@
+package com.supwisdom.institute.backend.gateway.authn.remote.configuration;
+
+import javax.net.ssl.SSLContext;
+
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.apache.http.ssl.SSLContexts;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.ClientHttpRequestFactory;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.http.client.SimpleClientHttpRequestFactory;
+import org.springframework.util.ResourceUtils;
+import org.springframework.web.client.RestTemplate;
+
+@Configuration
+public class AuthnRestTemplateConfig {
+
+  @Bean
+  public ClientHttpRequestFactory simpleClientHttpRequestFactory(
+      @Value("${sw-backend-base-api.client-auth.enabled:false}") boolean enabled,
+      @Value("${sw-backend-base-api.client-auth.key-password:}") String keyPassword,
+      @Value("${sw-backend-base-api.client-auth.key-store:}") String keyStore,
+      @Value("${sw-backend-base-api.client-auth.key-store-password:}") String keyStorePassword,
+      @Value("${sw-backend-base-api.client-auth.trust-store:}") String trustStore,
+      @Value("${sw-backend-base-api.client-auth.trust-store-password:}") String trustStorePassword
+  ) {
+    if (!enabled) {
+      SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
+      factory.setReadTimeout(5000);// 单位为ms
+      factory.setConnectTimeout(5000);// 单位为ms
+      return factory;
+    }
+    
+    SSLContextBuilder sslContextBuilder = SSLContexts.custom();
+    
+    if (trustStore == null || trustStore.isEmpty()) {
+    } else {
+      try {
+        sslContextBuilder
+          .loadTrustMaterial(
+              ResourceUtils.getFile(trustStore),
+              trustStorePassword.toCharArray()
+          );
+      } catch (Exception e) {
+        e.printStackTrace();
+      }
+    }
+    
+    if (keyStore == null || keyStore.isEmpty()) {
+      SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
+      factory.setReadTimeout(5000);// 单位为ms
+      factory.setConnectTimeout(5000);// 单位为ms
+      return factory;
+    } else {
+      try {
+        sslContextBuilder
+          .loadKeyMaterial(
+              ResourceUtils.getFile(keyStore),
+              keyStorePassword.toCharArray(),
+              keyPassword.toCharArray());
+      } catch (Exception e) {
+        e.printStackTrace();
+      }
+    }
+    
+    try {
+      SSLContext sslContext = sslContextBuilder.build();
+  
+      SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
+          sslContext,
+          SSLConnectionSocketFactory.getDefaultHostnameVerifier());
+      
+      CloseableHttpClient httpClient = HttpClients.custom()
+          .setSSLSocketFactory(sslsf)
+          .build();
+      
+      HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
+      factory.setReadTimeout(5000);// 单位为ms
+      factory.setConnectTimeout(5000);// 单位为ms
+      return factory;
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+    
+    SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
+    factory.setReadTimeout(5000);// 单位为ms
+    factory.setConnectTimeout(5000);// 单位为ms
+    return factory;
+  }
+  
+  @Bean(name = "authnAccountRestTemplate")
+  public RestTemplate authnAccountRestTemplate(ClientHttpRequestFactory requestFactory) {
+    return new RestTemplate(requestFactory);
+  }
+  
+}
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/remote/web/client/AuthnRemoteRestTemplate.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/remote/web/client/AuthnRemoteRestTemplate.java
new file mode 100644
index 0000000..b0fe53d
--- /dev/null
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/remote/web/client/AuthnRemoteRestTemplate.java
@@ -0,0 +1,154 @@
+package com.supwisdom.institute.backend.gateway.authn.remote.web.client;
+
+import lombok.extern.slf4j.Slf4j;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.web.client.RestTemplate;
+
+import com.alibaba.fastjson.JSONObject;
+
+@Slf4j
+@Component
+public class AuthnRemoteRestTemplate {
+  
+  @Autowired
+  private RestTemplate authnRestTemplate;
+  
+  @Value(value = "${sw-backend-base-api.uri}/v1/authn")
+  private String url;
+  
+  private JSONObject defaultErrorJson(Throwable cause) {
+    JSONObject error = new JSONObject();
+    
+    error.put("code", -1);
+    error.put("message", cause.getMessage());
+    error.put("error", cause.getMessage());
+    
+    return error;
+  }
+
+  public JSONObject account(String username) {
+    
+    try {
+      final String path = "/{username}/account";
+      final String url = this.url + StringUtils.replaceEach(path, new String[] {"{username}"}, new String[] {username});
+      log.debug(url);
+      
+      return authnRestTemplate.getForObject(url, JSONObject.class);
+    } catch (Exception e) {
+      e.printStackTrace();
+      
+      return defaultErrorJson(e);
+    }
+  }
+
+  public JSONObject accountRoles(String username) {
+
+    try {
+      final String path = "/{username}/roles";
+      final String url = this.url + StringUtils.replaceEach(path, new String[] {"{username}"}, new String[] {username});
+      log.debug(url);
+      
+      return authnRestTemplate.getForObject(url, JSONObject.class);
+    } catch (Exception e) {
+      e.printStackTrace();
+      
+      return defaultErrorJson(e);
+    }
+  }
+
+  public JSONObject accountApplications(String username, String applicationId) {
+
+    try {
+      final String path = "/{username}/applications";
+      final String url = this.url + StringUtils.replaceEach(path, new String[] {"{username}"}, new String[] {username});
+      log.debug(url);
+      
+      return authnRestTemplate.getForObject(url, JSONObject.class);
+    } catch (Exception e) {
+      e.printStackTrace();
+      
+      return defaultErrorJson(e);
+    }
+  }
+
+  public JSONObject accountMenus(String username, String applicationId) {
+
+    try {
+      final String path = "/{username}/menus";
+      final String url = this.url + StringUtils.replaceEach(path, new String[] {"{username}"}, new String[] {username});
+      log.debug(url);
+      
+      return authnRestTemplate.getForObject(url, JSONObject.class);
+    } catch (Exception e) {
+      e.printStackTrace();
+      
+      return defaultErrorJson(e);
+    }
+  }
+
+  public JSONObject accountOperations(String username, String applicationId) {
+
+    try {
+      final String path = "/{username}/operations";
+      final String url = this.url + StringUtils.replaceEach(path, new String[] {"{username}"}, new String[] {username});
+      log.debug(url);
+      
+      return authnRestTemplate.getForObject(url, JSONObject.class);
+    } catch (Exception e) {
+      e.printStackTrace();
+      
+      return defaultErrorJson(e);
+    }
+  }
+
+  public JSONObject accountResources(String username, String applicationId) {
+
+    try {
+      final String path = "/{username}/resources";
+      final String url = this.url + StringUtils.replaceEach(path, new String[] {"{username}"}, new String[] {username});
+      log.debug(url);
+      
+      return authnRestTemplate.getForObject(url, JSONObject.class);
+    } catch (Exception e) {
+      e.printStackTrace();
+      
+      return defaultErrorJson(e);
+    }
+  }
+
+  public JSONObject resourceRoleSets() {
+
+    try {
+      final String path = "/resourceRoleSets";
+      final String url = this.url + path;
+      log.debug(url);
+      
+      return authnRestTemplate.getForObject(url, JSONObject.class);
+    } catch (Exception e) {
+      //e.printStackTrace();
+      
+      return defaultErrorJson(e);
+    }
+  }
+
+
+  public JSONObject routes() {
+
+    try {
+      final String path = "/routes";
+      final String url = this.url + path;
+      log.debug(url);
+      
+      return authnRestTemplate.getForObject(url, JSONObject.class);
+    } catch (Exception e) {
+      e.printStackTrace();
+      
+      return defaultErrorJson(e);
+    }
+  }
+
+}
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/service/AuthnService.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/service/AuthnService.java
new file mode 100644
index 0000000..13434ec
--- /dev/null
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/service/AuthnService.java
@@ -0,0 +1,92 @@
+package com.supwisdom.institute.backend.gateway.authn.service;
+
+import java.util.List;
+
+import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.alibaba.fastjson.JSONObject;
+import com.supwisdom.institute.backend.gateway.authn.model.Account;
+import com.supwisdom.institute.backend.gateway.authn.model.ResourceRoleSet;
+import com.supwisdom.institute.backend.gateway.authn.model.Role;
+import com.supwisdom.institute.backend.gateway.authn.model.Route;
+import com.supwisdom.institute.backend.gateway.authn.remote.web.client.AuthnRemoteRestTemplate;
+
+@Slf4j
+@Service
+public class AuthnService {
+  
+  @Autowired
+  private AuthnRemoteRestTemplate authnRemote;
+  
+  public Account account(String username) {
+    
+    JSONObject jsonObject = authnRemote.account(username);
+    if (jsonObject == null) {
+      return null;
+    }
+    log.debug("{}", jsonObject.toJSONString());
+    
+    if (jsonObject.getIntValue("code") == 0) {
+      JSONObject data = jsonObject.getJSONObject("data");
+      
+      return data.toJavaObject(Account.class);
+    }
+    
+    return null;
+  }
+
+  public List<Role> accountRoles(String username) {
+    
+    JSONObject jsonObject = authnRemote.accountRoles(username);
+    if (jsonObject == null) {
+      return null;
+    }
+    log.debug("{}", jsonObject.toJSONString());
+    
+    if (jsonObject.getIntValue("code") == 0) {
+      JSONObject data = jsonObject.getJSONObject("data");
+      
+      return data.getJSONArray("roles").toJavaList(Role.class);
+    }
+    
+    return null;
+  }
+
+  public List<ResourceRoleSet> resourceRoleSets() {
+    
+    JSONObject jsonObject = authnRemote.resourceRoleSets();
+    if (jsonObject == null) {
+      return null;
+    }
+    log.debug("{}", jsonObject.toJSONString());
+    
+    if (jsonObject.getIntValue("code") == 0) {
+      JSONObject data = jsonObject.getJSONObject("data");
+      
+      return data.getJSONArray("resourceRoleSets").toJavaList(ResourceRoleSet.class);
+    }
+    
+    return null;
+  }
+
+  public List<Route> routes() {
+    
+    JSONObject jsonObject = authnRemote.routes();
+    if (jsonObject == null) {
+      return null;
+    }
+    log.debug("{}", jsonObject.toJSONString());
+    
+    if (jsonObject.getIntValue("code") == 0) {
+      JSONObject data = jsonObject.getJSONObject("data");
+      
+      return data.getJSONArray("routes").toJavaList(Route.class);
+    }
+    
+    return null;
+  }
+
+}
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/configuration/GatewayGlobalFilterConfig.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/configuration/GatewayGlobalFilterConfig.java
new file mode 100644
index 0000000..f01b8fa
--- /dev/null
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/configuration/GatewayGlobalFilterConfig.java
@@ -0,0 +1,22 @@
+package com.supwisdom.institute.backend.gateway.configuration;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import com.supwisdom.institute.backend.gateway.filter.AccessControlGlobalFilter;
+import com.supwisdom.institute.backend.gateway.filter.SimpleUserTransmitGlobalFilter;
+
+@Configuration
+public class GatewayGlobalFilterConfig {
+  
+  @Bean
+  public AccessControlGlobalFilter accessControlGlobalFilter() {
+    return new AccessControlGlobalFilter();
+  }
+
+  @Bean
+  public SimpleUserTransmitGlobalFilter simpleUserTransmitGlobalFilter() {
+    return new SimpleUserTransmitGlobalFilter();
+  }
+
+}
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/configuration/PasswordEncoderConfig.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/configuration/PasswordEncoderConfig.java
new file mode 100644
index 0000000..f98770f
--- /dev/null
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/configuration/PasswordEncoderConfig.java
@@ -0,0 +1,29 @@
+package com.supwisdom.institute.backend.gateway.configuration;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.crypto.factory.PasswordEncoderFactories;
+import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
+import org.springframework.security.crypto.password.NoOpPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Configuration
+public class PasswordEncoderConfig {
+
+  @Bean
+  public PasswordEncoder passwordEncoder() {
+    
+    PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
+
+    if (passwordEncoder instanceof DelegatingPasswordEncoder) {
+      ((DelegatingPasswordEncoder)passwordEncoder).setDefaultPasswordEncoderForMatches(NoOpPasswordEncoder.getInstance());
+    }
+
+    log.debug("PasswordEncoderConfig passwordEncoder is {}", passwordEncoder);
+    return passwordEncoder;
+  }
+
+}
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/configuration/UserDetailsServiceConfig.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/configuration/UserDetailsServiceConfig.java
new file mode 100644
index 0000000..6f8be84
--- /dev/null
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/configuration/UserDetailsServiceConfig.java
@@ -0,0 +1,31 @@
+package com.supwisdom.institute.backend.gateway.configuration;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import com.supwisdom.institute.backend.gateway.security.core.userdetails.InMemeryUserDetailsService;
+import com.supwisdom.institute.backend.gateway.security.core.userdetails.MyUserDetailsService;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Configuration
+public class UserDetailsServiceConfig {
+
+  @Bean
+  public MyUserDetailsService userDetailsService() throws Exception {
+    MyUserDetailsService myUserDetailsService = new MyUserDetailsService();
+    log.debug("UserDetailsServiceConfig myUserDetailsService is {}", myUserDetailsService);
+
+    return myUserDetailsService;
+  }
+
+//  @Bean
+//  public InMemeryUserDetailsService userDetailsService() throws Exception {
+//    InMemeryUserDetailsService inMemeryUserDetailsService = new InMemeryUserDetailsService();
+//    log.debug("UserDetailsServiceConfig inMemeryUserDetailsService is {}", inMemeryUserDetailsService);
+//
+//    return inMemeryUserDetailsService;
+//  }
+
+}
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/filter/AccessControlGlobalFilter.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/filter/AccessControlGlobalFilter.java
new file mode 100644
index 0000000..7784615
--- /dev/null
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/filter/AccessControlGlobalFilter.java
@@ -0,0 +1,516 @@
+package com.supwisdom.institute.backend.gateway.filter;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.core.Ordered;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.server.RequestPath;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.security.access.ConfigAttribute;
+import org.springframework.security.access.SecurityConfig;
+import org.springframework.security.core.context.ReactiveSecurityContextHolder;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.server.ServerWebExchange;
+
+import com.supwisdom.infras.security.core.userdetails.InfrasUser;
+import com.supwisdom.institute.backend.gateway.authn.model.ResourceRoleSet;
+import com.supwisdom.institute.backend.gateway.authn.model.Role;
+import com.supwisdom.institute.backend.gateway.authn.service.AuthnService;
+
+import reactor.core.publisher.Mono;
+
+@Slf4j
+public class AccessControlGlobalFilter implements GlobalFilter, Ordered {
+  
+  @Override
+  public int getOrder() {
+    return Ordered.HIGHEST_PRECEDENCE;
+  }
+  
+  @Override
+  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+    log.debug("AccessControlGlobalFilter.filter");
+    
+    // 获取 请求路径 对应的 资源
+    Collection<ConfigAttribute> configAttributes = this.getAttributes(exchange);
+    log.debug("request's attributes is {}", configAttributes);
+    
+    // 判断 该资源 是否需要登录才能访问
+    if (configAttributes == null || configAttributes.size() <= 0) {
+      
+      return chain.filter(exchange);  // FIXME: 
+    }
+    
+    // 获取 当前登录用户(包括角色信息)
+    
+    // FIXME: 判断 登录用户 是否可以访问 该资源
+    
+    return ReactiveSecurityContextHolder.getContext()
+        .filter(sc -> {
+          return sc.getAuthentication() != null && sc.getAuthentication().isAuthenticated();
+        })
+        .flatMap(sc -> Mono.just(Optional.of(sc)))
+        .defaultIfEmpty(Optional.empty())
+        .flatMap(scOptional -> {
+          List<String> roles = null;
+          
+          if (scOptional.isPresent()) {
+            SecurityContext sc = scOptional.get();
+            if (sc.getAuthentication().getPrincipal() instanceof InfrasUser) {
+              InfrasUser infrasUser = (InfrasUser) sc.getAuthentication().getPrincipal();
+              log.debug("infrasUser's roles is {}", infrasUser.getRoles());
+              
+              roles = infrasUser.getRoles();
+            } else {
+              roles = new ArrayList<String>();
+            }
+          } else {
+            roles = new ArrayList<String>();
+          }
+          
+          boolean hasPermission = false;
+
+          ConfigAttribute ca;
+          String needRole;
+          for (Iterator<ConfigAttribute> iter = configAttributes.iterator(); iter.hasNext();) {
+            ca = iter.next();
+            needRole = ca.getAttribute();
+            
+            if (needRole == null || needRole.isEmpty()) {
+              continue;
+            }
+            
+            if (needRole.startsWith("ACCESS_")) {
+              String access = needRole.substring("ACCESS_".length()); log.debug("Access is {}", access);
+              if ("anonymous".equals(access)) {
+                hasPermission = false;
+              } else if ("authenticate".equals(access)) {
+                hasPermission = true;
+              } else if ("permitAll".equals(access)) {
+                hasPermission = true;
+              } else if ("denyAll".equals(access)) {
+                hasPermission = false;
+              } else {
+                hasPermission = false;
+              }
+              break;
+            }
+            
+            hasPermission = roles.contains(ca.getAttribute());
+            if (hasPermission) {
+              log.debug("match attribute is {}", ca.getAttribute());
+              break;
+            }
+          }
+          
+          if (!hasPermission) {
+            throw new RuntimeException("no right");
+          }
+          
+          return Mono.just(exchange);
+        })
+        .flatMap(ex -> chain.filter(ex));
+  }
+
+  @Autowired
+  private AuthnService authnService;
+
+  private Map<RequestMatcher, Collection<ConfigAttribute>> requestMap = new ConcurrentHashMap<RequestMatcher, Collection<ConfigAttribute>>();
+
+  @Scheduled(
+      initialDelayString = "${sw-backend-gateway.resource.refresh-delay:200}", 
+      fixedDelayString = "${sw-backend-gateway.resource.refresh-delay:10000}")
+  protected void refreshRequestMap() {
+    
+    log.debug("AccessControlGlobalFilter.refreshRequestMap");
+    
+    requestMap.clear();
+    loadRequestMap();
+  }
+
+  // 定时刷新 资源 与 可访问角色 的 Map
+  private void loadRequestMap() {
+
+    if (requestMap.isEmpty()) {
+      AntPathRequestMatcher requestMatcher0 = new AntPathRequestMatcher("/api/*/v*/open/**");
+      Collection<ConfigAttribute> attributes0 = new ArrayList<ConfigAttribute>();
+      attributes0.add(new SecurityConfig("ACCESS_"+ResourceRoleSet.ACCESS_PERMIT_ALL));
+      requestMap.put(requestMatcher0, attributes0);
+    
+      // 从 后端接口 加载
+      List<ResourceRoleSet> resourceRoleSets = authnService.resourceRoleSets();
+      if (resourceRoleSets != null) {
+        for (ResourceRoleSet resourceRoleSet : resourceRoleSets) {
+          String method = resourceRoleSet.getMethod();
+          String path = resourceRoleSet.getPath();
+          String access = resourceRoleSet.getAccess();
+          
+          final RequestMatcher requestMatcher = new AntPathRequestMatcher(path, method);
+          
+          Collection<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();
+          
+          if (access != null) {
+            if (access.equals(ResourceRoleSet.ACCESS_ANONYMOUS)) {
+              attributes.add(new SecurityConfig("ACCESS_"+ResourceRoleSet.ACCESS_ANONYMOUS));
+            } else if (access.equals(ResourceRoleSet.ACCESS_AUTHENTICATE)) {
+              attributes.add(new SecurityConfig("ACCESS_"+ResourceRoleSet.ACCESS_AUTHENTICATE));
+            } else if (access.equals(ResourceRoleSet.ACCESS_AUTHORIZE)) {
+              for (Role r : resourceRoleSet.getRoles()) {
+                ConfigAttribute ca = new SecurityConfig(r.getCode());
+                attributes.add(ca);
+              }
+            } else if (access.equals(ResourceRoleSet.ACCESS_PERMIT_ALL)) {
+              attributes.add(new SecurityConfig("ACCESS_"+ResourceRoleSet.ACCESS_PERMIT_ALL));
+            } else if (access.equals(ResourceRoleSet.ACCESS_DENY_ALL)) {
+              attributes.add(new SecurityConfig("ACCESS_"+ResourceRoleSet.ACCESS_DENY_ALL));
+            } else {
+              for (Role r : resourceRoleSet.getRoles()) {
+                ConfigAttribute ca = new SecurityConfig(r.getCode());
+                attributes.add(ca);
+              }
+            }
+          } else {
+            for (Role r : resourceRoleSet.getRoles()) {
+              ConfigAttribute ca = new SecurityConfig(r.getCode());
+              attributes.add(ca);
+            }
+          }
+          
+          requestMap.put(requestMatcher, attributes);
+        }
+      }
+      
+    }
+
+  }
+  
+  public Collection<ConfigAttribute> getAttributes(ServerWebExchange exchange) throws IllegalArgumentException {
+
+    if (requestMap.isEmpty()) {
+      loadRequestMap();
+    }
+    
+    ServerHttpRequest request = exchange.getRequest();
+
+
+    RequestMatcher requestMatcher;
+    for (Iterator<RequestMatcher> iter = requestMap.keySet().iterator(); iter.hasNext();) {
+      requestMatcher = iter.next();
+
+      if (requestMatcher.matches(request)) {
+        return requestMap.get(requestMatcher);
+      }
+    }
+
+    return null;
+  }
+  
+  
+  public static interface RequestMatcher {
+    
+    boolean matches(ServerHttpRequest request);
+  }
+
+  @Slf4j
+  public static class AntPathRequestMatcher implements RequestMatcher {
+    private static final String MATCH_ALL = "/**";
+
+    private final Matcher matcher;
+    
+    private final String pattern;
+    private final HttpMethod httpMethod;
+    private final boolean caseSensitive;
+
+    /**
+     * Creates a matcher with the specific pattern which will match all HTTP methods in a
+     * case insensitive manner.
+     *
+     * @param pattern the ant pattern to use for matching
+     */
+    public AntPathRequestMatcher(String pattern) {
+      this(pattern, null);
+    }
+
+    /**
+     * Creates a matcher with the supplied pattern and HTTP method in a case insensitive
+     * manner.
+     *
+     * @param pattern the ant pattern to use for matching
+     * @param httpMethod the HTTP method. The {@code matches} method will return false if
+     * the incoming request doesn't have the same method.
+     */
+    public AntPathRequestMatcher(String pattern, String httpMethod) {
+      this(pattern, httpMethod, true);
+    }
+
+    /**
+     * Creates a matcher with the supplied pattern which will match the specified Http
+     * method
+     *
+     * @param pattern the ant pattern to use for matching
+     * @param httpMethod the HTTP method. The {@code matches} method will return false if
+     * the incoming request doesn't doesn't have the same method.
+     * @param caseSensitive true if the matcher should consider case, else false
+     * @param urlPathHelper if non-null, will be used for extracting the path from the HttpServletRequest
+     */
+    public AntPathRequestMatcher(String pattern, String httpMethod,
+        boolean caseSensitive) {
+      Assert.hasText(pattern, "Pattern cannot be null or empty");
+      this.caseSensitive = caseSensitive;
+
+      if (pattern.equals(MATCH_ALL) || pattern.equals("**")) {
+        pattern = MATCH_ALL;
+        this.matcher = null;
+      }
+      else {
+        // If the pattern ends with {@code /**} and has no other wildcards or path
+        // variables, then optimize to a sub-path match
+        if (pattern.endsWith(MATCH_ALL)
+            && (pattern.indexOf('?') == -1 && pattern.indexOf('{') == -1
+                && pattern.indexOf('}') == -1)
+            && pattern.indexOf("*") == pattern.length() - 2) {
+          this.matcher = new SubpathMatcher(
+              pattern.substring(0, pattern.length() - 3), caseSensitive);
+        }
+        else {
+          this.matcher = new SpringAntMatcher(pattern, caseSensitive);
+        }
+      }
+
+      this.pattern = pattern;
+      this.httpMethod = StringUtils.hasText(httpMethod) ? HttpMethod.valueOf(httpMethod)
+          : null;
+    }
+
+//    @Override
+//    public boolean matches(ServerHttpRequest request) {
+//      
+//      RequestPath requestPath = request.getPath();
+//      log.info(requestPath.pathWithinApplication().value());
+//      String path = requestPath.pathWithinApplication().value();
+//      
+//      return false;
+//    }
+
+    /**
+     * Returns true if the configured pattern (and HTTP-Method) match those of the
+     * supplied request.
+     *
+     * @param request the request to match against. The ant pattern will be matched
+     * against the {@code servletPath} + {@code pathInfo} of the request.
+     */
+    @Override
+    public boolean matches(ServerHttpRequest request) {
+      if (this.httpMethod != null && StringUtils.hasText(request.getMethodValue())
+          && this.httpMethod != valueOf(request.getMethodValue())) {
+        if (log.isDebugEnabled()) {
+          log.debug("Request '" + request.getMethod() + " "
+              + getRequestPath(request) + "'" + " doesn't match '"
+              + this.httpMethod + " " + this.pattern);
+        }
+
+        return false;
+      }
+
+      if (this.pattern.equals(MATCH_ALL)) {
+        if (log.isDebugEnabled()) {
+          log.debug("Request '" + getRequestPath(request)
+              + "' matched by universal pattern '/**'");
+        }
+
+        return true;
+      }
+
+      String url = getRequestPath(request);
+
+      if (log.isDebugEnabled()) {
+        log.debug("Checking match of request : '" + url + "'; against '"
+            + this.pattern + "'");
+      }
+
+      return this.matcher.matches(url);
+    }
+
+
+    private String getRequestPath(ServerHttpRequest request) {
+      
+      RequestPath requestPath = request.getPath();
+      log.info(requestPath.pathWithinApplication().value());
+      String path = requestPath.pathWithinApplication().value();
+      
+      return path;
+      
+      //String url = request.getServletPath();
+
+      //String pathInfo = request.getPathInfo();
+      //if (pathInfo != null) {
+      //  url = StringUtils.hasLength(url) ? url + pathInfo : pathInfo;
+      //}
+
+      //return url;
+    }
+
+    public String getPattern() {
+      return this.pattern;
+    }
+
+
+    @Override
+    public boolean equals(Object obj) {
+      if (!(obj instanceof AntPathRequestMatcher)) {
+        return false;
+      }
+
+      AntPathRequestMatcher other = (AntPathRequestMatcher) obj;
+      return this.pattern.equals(other.pattern) && this.httpMethod == other.httpMethod
+          && this.caseSensitive == other.caseSensitive;
+    }
+
+    @Override
+    public int hashCode() {
+      int code = 31 ^ this.pattern.hashCode();
+      if (this.httpMethod != null) {
+        code ^= this.httpMethod.hashCode();
+      }
+      return code;
+    }
+
+    @Override
+    public String toString() {
+      StringBuilder sb = new StringBuilder();
+      sb.append("Ant [pattern='").append(this.pattern).append("'");
+
+      if (this.httpMethod != null) {
+        sb.append(", ").append(this.httpMethod);
+      }
+
+      sb.append("]");
+
+      return sb.toString();
+    }
+
+
+    /**
+     * Provides a save way of obtaining the HttpMethod from a String. If the method is
+     * invalid, returns null.
+     *
+     * @param method the HTTP method to use.
+     *
+     * @return the HttpMethod or null if method is invalid.
+     */
+    private static HttpMethod valueOf(String method) {
+      try {
+        return HttpMethod.valueOf(method);
+      }
+      catch (IllegalArgumentException e) {
+      }
+
+      return null;
+    }
+
+    private static interface Matcher {
+      boolean matches(String path);
+
+      Map<String, String> extractUriTemplateVariables(String path);
+    }
+
+    private static class SpringAntMatcher implements Matcher {
+      private final AntPathMatcher antMatcher;
+
+      private final String pattern;
+
+      private SpringAntMatcher(String pattern, boolean caseSensitive) {
+        this.pattern = pattern;
+        this.antMatcher = createMatcher(caseSensitive);
+      }
+
+      @Override
+      public boolean matches(String path) {
+        return this.antMatcher.match(this.pattern, path);
+      }
+
+      @Override
+      public Map<String, String> extractUriTemplateVariables(String path) {
+        return this.antMatcher.extractUriTemplateVariables(this.pattern, path);
+      }
+
+      private static AntPathMatcher createMatcher(boolean caseSensitive) {
+        AntPathMatcher matcher = new AntPathMatcher();
+        matcher.setTrimTokens(false);
+        matcher.setCaseSensitive(caseSensitive);
+        return matcher;
+      }
+    }
+
+    /**
+     * Optimized matcher for trailing wildcards
+     */
+    private static class SubpathMatcher implements Matcher {
+      private final String subpath;
+      private final int length;
+      private final boolean caseSensitive;
+
+      private SubpathMatcher(String subpath, boolean caseSensitive) {
+        assert!subpath.contains("*");
+        this.subpath = caseSensitive ? subpath : subpath.toLowerCase();
+        this.length = subpath.length();
+        this.caseSensitive = caseSensitive;
+      }
+
+      @Override
+      public boolean matches(String path) {
+        if (!this.caseSensitive) {
+          path = path.toLowerCase();
+        }
+        return path.startsWith(this.subpath)
+            && (path.length() == this.length || path.charAt(this.length) == '/');
+      }
+
+      @Override
+      public Map<String, String> extractUriTemplateVariables(String path) {
+        return Collections.emptyMap();
+      }
+    }
+    
+  }
+  
+  @AllArgsConstructor
+  @Getter
+  public static class RestRequestMatcher implements RequestMatcher {
+    
+    private final String path;
+    private final RequestMethod[] method;
+    private final String[] params;
+    private final String[] headers;
+    private final String[] consumes;
+    private final String[] produces;
+
+    @Override
+    public boolean matches(ServerHttpRequest request) {
+      
+      
+      return false;
+    }
+    
+  }
+
+}
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/filter/SimpleUserTransmitGlobalFilter.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/filter/SimpleUserTransmitGlobalFilter.java
new file mode 100644
index 0000000..0a8b606
--- /dev/null
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/filter/SimpleUserTransmitGlobalFilter.java
@@ -0,0 +1,79 @@
+package com.supwisdom.institute.backend.gateway.filter;
+
+import java.util.Optional;
+
+import lombok.extern.slf4j.Slf4j;
+
+import org.apache.commons.codec.binary.Base64;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.core.Ordered;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.ReactiveSecurityContextHolder;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.web.server.ServerWebExchange;
+
+import com.alibaba.fastjson.JSONObject;
+import com.supwisdom.infras.security.core.userdetails.InfrasUser;
+import com.supwisdom.institute.backend.common.core.transmit.user.User;
+import com.supwisdom.institute.backend.common.core.transmit.user.UserContext;
+
+import reactor.core.publisher.Mono;
+
+@Slf4j
+public class SimpleUserTransmitGlobalFilter implements GlobalFilter, Ordered {
+
+  @Override
+  public int getOrder() {
+    return 0;
+  }
+
+  @Override
+  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+    log.debug("SimpleUserTransmitGlobalFilter.filter");
+    
+    return ReactiveSecurityContextHolder.getContext()
+      .filter(c -> {
+        return c.getAuthentication() != null && c.getAuthentication().isAuthenticated() && c.getAuthentication().getPrincipal() instanceof InfrasUser;
+      })
+      .flatMap(sc -> Mono.just(Optional.of(sc)))
+      .defaultIfEmpty(Optional.empty())
+      .flatMap(scOptional -> {
+        if (scOptional.isPresent()) {
+          SecurityContext sc = scOptional.get();
+          
+          return Mono.just(sc)
+            .map(SecurityContext::getAuthentication)
+            .map(Authentication::getPrincipal)
+            .cast(InfrasUser.class)
+            .map(infrasUser -> {
+              try {
+                User user = new User(infrasUser.getUsername(), infrasUser.getRoles(), infrasUser.getAttributes());
+                
+                String jsonUser = JSONObject.toJSONString(user);
+                log.debug(jsonUser);
+                
+                //String headerValue = new String(URLDecoder.decode(jsonUser,"UTF-8"));
+                String headerValue = Base64.encodeBase64URLSafeString(jsonUser.getBytes("UTF-8"));
+                log.debug(headerValue);
+                
+                ServerHttpRequest request = exchange.getRequest().mutate()
+                    .header(UserContext.KEY_USER_IN_HTTP_HEADER, headerValue)
+                    .build();
+                log.debug("User set to gateway header: ok");
+                return exchange.mutate().request(request).build();
+              } catch (Exception e) {
+                log.warn("User set to gateway header: error", e);
+              }
+              return exchange;
+            });
+        }
+        
+        return Mono.just(exchange);
+      })
+      .flatMap(ex -> chain.filter(ex))
+    ;
+  }
+
+}
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/security/core/userdetails/InMemeryUserDetailsService.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/security/core/userdetails/InMemeryUserDetailsService.java
new file mode 100644
index 0000000..4e761a5
--- /dev/null
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/security/core/userdetails/InMemeryUserDetailsService.java
@@ -0,0 +1,72 @@
+package com.supwisdom.institute.backend.gateway.security.core.userdetails;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+import com.supwisdom.institute.backend.common.core.transmit.user.User;
+import com.supwisdom.institute.backend.common.core.transmit.user.UserContext;
+
+import reactor.core.publisher.Mono;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class InMemeryUserDetailsService implements UserDetailsService, ReactiveUserDetailsService {
+
+  @Autowired
+  PasswordEncoder passwordEncoder;
+  
+  @Override
+  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+    
+    log.debug("InMemeryUserDetailsService.loadUserByUsername({})", username);
+    
+    List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
+    authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
+    authorities.add(new SimpleGrantedAuthority("administrator"));
+    authorities.add(new SimpleGrantedAuthority("user"));
+
+    Map<String, Object> attributes = new HashMap<String, Object>();
+    
+    MyUser myUser = new MyUser(username, passwordEncoder.encode(username), authorities, attributes);
+    log.debug("myUser is {}", myUser);
+    
+    return myUser;
+  }
+
+  @Override
+  public Mono<UserDetails> findByUsername(String username) {
+
+    log.debug("InMemeryUserDetailsService.findByUsername({})", username);
+    
+    List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
+    authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
+    authorities.add(new SimpleGrantedAuthority("administrator"));
+    authorities.add(new SimpleGrantedAuthority("user"));
+
+    Map<String, Object> attributes = new HashMap<String, Object>();
+    
+    MyUser myUser = new MyUser(username, passwordEncoder.encode(username), authorities, attributes);
+    log.debug("myUser is {}", myUser);
+    
+    List<String> roles = new ArrayList<>();
+    roles.add("ROLE_ADMIN");
+    roles.add("administrator");
+    roles.add("user");
+    User user = new User(username, roles, attributes);
+    UserContext.setUser(user);
+    
+    return Mono.just(myUser);
+  }
+  
+}
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/security/core/userdetails/MyUser.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/security/core/userdetails/MyUser.java
new file mode 100644
index 0000000..9416893
--- /dev/null
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/security/core/userdetails/MyUser.java
@@ -0,0 +1,61 @@
+package com.supwisdom.institute.backend.gateway.security.core.userdetails;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.security.core.GrantedAuthority;
+
+import com.supwisdom.infras.security.core.userdetails.InfrasUser;
+
+public class MyUser extends InfrasUser {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 3195151947212484499L;
+
+  public MyUser(String username, String password, 
+      Collection<? extends GrantedAuthority> authorities, 
+      Map<String, Object> attributes) {
+    this(username, password, true, true, true, true, authorities, attributes);
+  }
+
+  public MyUser(String username, String password, 
+      boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, 
+      Collection<? extends GrantedAuthority> authorities, 
+      Map<String, Object> attributes) {
+    super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities, attributes);
+    
+    this.attributes = attributes;
+  }
+
+  private final Map<String, Object> attributes;
+  public Map<String, Object> getAttributes() {
+    return this.attributes;
+  }
+  
+  public List<String> getRoles() {
+    List<String> roles = new ArrayList<>();
+    for (GrantedAuthority grantedAuthority : this.getAuthorities()) {
+      roles.add(grantedAuthority.getAuthority());
+    }
+    
+    return roles;
+  }
+  
+
+  public Object getAttribute(String key) {
+    if (attributes == null) {
+      return null;
+    }
+    
+    if (!attributes.containsKey(key)) {
+      return null;
+    }
+    
+    return attributes.get(key);
+  }
+  
+}
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/security/core/userdetails/MyUserDetailsService.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/security/core/userdetails/MyUserDetailsService.java
new file mode 100644
index 0000000..8a30025
--- /dev/null
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/security/core/userdetails/MyUserDetailsService.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.gateway.security.core.userdetails;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+import com.supwisdom.institute.backend.gateway.authn.model.Account;
+import com.supwisdom.institute.backend.gateway.authn.model.Role;
+import com.supwisdom.institute.backend.gateway.authn.service.AuthnService;
+
+import reactor.core.publisher.Mono;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class MyUserDetailsService implements UserDetailsService, ReactiveUserDetailsService {
+  
+  @Autowired
+  PasswordEncoder passwordEncoder;
+
+  @Autowired
+  AuthnService authnAccountService;
+  
+  @Override
+  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // TODO: 
+
+    log.debug("MyUserDetailsService.loadUserByUsername({})", username);
+    
+    MyUser myUser = loadUser(username);
+
+    return myUser;
+  }
+
+  @Override
+  public Mono<UserDetails> findByUsername(String username) {
+    log.debug("MyUserDetailsService.findByUsername({})", username);
+    
+    MyUser myUser = loadUser(username);
+
+    return Mono.just(myUser);
+  }
+  
+  private MyUser loadUser(String username) {
+
+    Account account = authnAccountService.account(username);
+    if (account == null) {
+      throw new UsernameNotFoundException(String.format("%s not found", username));
+    }
+    
+    List<Role> roles = authnAccountService.accountRoles(username);
+    
+    List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
+    for (Role role : roles) {
+      authorities.add(new SimpleGrantedAuthority(role.getCode()));
+    }
+    
+    Map<String, Object> attributes = new HashMap<String, Object>();
+    attributes.put("accountId", account.getId());
+    attributes.put("accountName", account.getName());
+    //attributes.put("accountMobile", account.getMobile());
+    //attributes.put("accountEmail", account.getEmail());
+    
+    MyUser myUser = new MyUser(account.getUsername(), passwordEncoder.encode(account.getPassword()), 
+        account.getEnabled(), account.getAccountNonExpired(), account.getCredentialsNonExpired(), account.getAccountNonLocked(), 
+        authorities, attributes);
+    log.debug("myUser is {}", myUser);
+
+    return myUser;
+  }
+
+}
diff --git a/gateway/src/main/resources/application-docker.yml b/gateway/src/main/resources/application-docker.yml
new file mode 100644
index 0000000..661b919
--- /dev/null
+++ b/gateway/src/main/resources/application-docker.yml
@@ -0,0 +1,117 @@
+server:
+  port: ${SERVER_PORT:8443}
+  ssl:
+    enabled: ${SSL_ENABLED:true}
+    clientAuth: NEED
+    key-store: ${SSL_KEYSTORE_FILE:file:/certs/server/server.keystore}
+    key-store-password: ${SSL_KEYSTORE_PASSWORD:}
+    trust-store: ${SSL_TRUSTSTORE_FILE:file:/certs/server/server.truststore}
+    trust-store-password: ${SSL_TRUSTSTORE_PASSWORD:}
+  tomcat: 
+    accesslog: 
+      enabled: ${TOMCAT_ACCESSLOG_ENABLED:false}
+      buffered: ${TOMCAT_ACCESSLOG_BUFFERED:true}
+      directory: ${TOMCAT_ACCESSLOG_DIR:log}
+      prefix: ${TOMCAT_ACCESSLOG_PREFIX:sa-api-accesslog}
+      suffix: ${TOMCAT_ACCESSLOG_SUFFIX:.log}
+      file-date-format: ${TOMCAT_ACCESSLOG_FILE_DATE_FORMAT:.yyyy-MM-dd}
+      rotate: ${TOMCAT_ACCESSLOG_ROTATE:true}
+
+
+##
+# logging
+#
+logging:
+  level:
+    root: INFO
+    com.supwisdom: INFO
+
+
+spring:
+  jackson:
+    time-zone: ${JACKSON_TIME_ZONE:Asia/Shanghai}
+
+
+##
+# spring cloud gateway
+#
+  cloud:
+    gateway:
+      metrics:
+        enabled: true
+      routes:
+      - id: bff-api
+        uri: ${SW_BACKEND_BFF_API_URI:https://sw-backend-admin-bff}
+        predicates:
+        - Path=/api/bff/**
+        filters:
+        - RewritePath=/api/bff/(?<suffix>.*), /$\{suffix}
+      - id: base-api
+        uri: ${SW_BACKEND_BASE_API_URI:https://sw-backend-admin-sa}
+        predicates:
+        - Path=/api/base/**
+        filters:
+        - RewritePath=/api/base/(?<suffix>.*), /$\{suffix}
+      - id: system-api
+        uri: ${SW_BACKEND_SYSTEM_API_URI:https://sw-backend-admin-sa}
+        predicates:
+        - Path=/api/system/**
+        filters:
+        - RewritePath=/api/system/(?<suffix>.*), /$\{suffix}
+      - id: biz-api
+        uri: ${SW_BACKEND_BIZ_API_URI:https://sw-backend-biz-sa}
+        predicates:
+        - Path=/api/biz/**
+        filters:
+        - RewritePath=/api/biz/(?<suffix>.*), /$\{suffix}
+
+
+##
+# infras.online-doc
+#
+infras.online-doc.enabled: ${INFRAS_ONLINE_DOC_ENABLED:false}
+infras.online-doc.md-docs.staitc.path: ${INFRAS_ONLINE_DOC_MD_DOCS_STATIC_PATH:/doc/}
+infras.online-doc.api-docs.staitc.path: ${INFRAS_ONLINE_DOC_API_DOCS_STATIC_PATH:/api-docs/}
+
+
+##
+# infras.security basic
+#
+infras.security.basic.reactive.enabled: ${INFRAS_SECURITY_BASIC_REACTIVE_ENABLED:true}
+
+
+##
+# infras.security jwt
+#
+infras.security.jwt.enabled: ${INFRAS_SECURITY_JWT_REACTIVE_ENABLED:false}
+
+infras.security.jwt.public-key-pem: ${INFRAS_SECURITY_JWT_PUBLIC_KEY_PEM:}
+infras.security.jwt.private-key-pem-pkcs8: ${INFRAS_SECURITY_JWT_PRIVATE_KEY_PEM_PKCS8:}
+
+
+##
+# infras.security cas
+#
+infras.security.cas.enabled: ${INFRAS_SECURITY_CAS_REACTIVE_ENABLED:false}
+
+#应用访问地址
+app.server.host.url: ${APP_SERVER_HOST_URL:https://localhost:8443}
+#应用登录地址
+app.login.url: ${APP_LOGIN_URL:/cas/login}
+#应用登出地址
+app.logout.url: ${APP_LOGOUT_URL:/cas/logout}
+
+#CAS服务地址
+cas.server.host.url: ${CAS_SERVER_HOST_URL:https://cas-server/cas}
+
+
+sw-backend-base-api: 
+  uri: ${SW_BACKEND_BASE_API_URI:https://sw-backend-admin-sa}
+  client-auth:
+    enabled: ${SW_BACKEND_BASE_API_CLIENT_AUTH_ENABLED:true}
+    key-password: ${SW_BACKEND_BASE_API_CLIENT_AUTH_KEY_PASSWORD:}
+    key-store: ${SW_BACKEND_BASE_API_CLIENT_AUTH_KEYSTORE_FILE:file:/certs/common/common.keystore}
+    key-store-password: ${SW_BACKEND_BASE_API_CLIENT_AUTH_KEYSTORE_PASSWORD:}
+    trust-store: ${SW_BACKEND_BASE_API_CLIENT_AUTH_TRUSTSTORE_FILE:file:/certs/common/common.truststore}
+    trust-store-password: ${SW_BACKEND_BASE_API_CLIENT_AUTH_TRUSTSTORE_PASSWORD:}
+  
\ No newline at end of file
diff --git a/gateway/src/main/resources/application.yml b/gateway/src/main/resources/application.yml
new file mode 100644
index 0000000..1f3be47
--- /dev/null
+++ b/gateway/src/main/resources/application.yml
@@ -0,0 +1,99 @@
+server:
+  port: 8080
+
+
+##
+# logging
+#
+logging:
+  level:
+    root: INFO
+    com.supwisdom: DEBUG
+#    org.springframework.web: INFO
+#    org.springframework.cloud.openfeign: INFO
+
+
+spring:
+  jackson:
+    time-zone: Asia/Shanghai
+
+##
+# spring cloud gateway
+#
+  cloud:
+    gateway:
+      metrics:
+        enabled: true
+      routes:
+      - id: bff-api
+        uri: http://localhost:8081
+        predicates:
+        - Path=/api/bff/**
+        filters:
+        - RewritePath=/api/bff/(?<suffix>.*), /$\{suffix}
+      - id: base-api
+        uri: http://localhost:8082
+        predicates:
+        - Path=/api/base/**
+        filters:
+        - RewritePath=/api/base/(?<suffix>.*), /$\{suffix}
+      - id: system-api
+        uri: http://localhost:8082
+        predicates:
+        - Path=/api/system/**
+        filters:
+        - RewritePath=/api/system/(?<suffix>.*), /$\{suffix}
+      - id: biz-api
+        uri: http://localhost:8083
+        predicates:
+        - Path=/api/biz/**
+        filters:
+        - RewritePath=/api/biz/(?<suffix>.*), /$\{suffix}
+
+
+##
+# infras.online-doc
+#
+infras.online-doc.enabled: false
+infras.online-doc.md-docs.staitc.path: /Users/loie/c/work/git/institute/sw-backend/doc/
+infras.online-doc.api-docs.staitc.path: /Users/loie/c/work/git/institute/sw-backend/api-docs/
+
+
+##
+# infras.security basic
+#
+infras.security.basic.reactive.enabled: false
+
+
+##
+# infras.security jwt
+#
+infras.security.jwt.reactive.enabled: true
+
+infras.security.jwt.public-key-pem: |-
+  -----BEGIN PUBLIC KEY-----
+  MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCgzXhvHLKypr+G+gJgOJNt8Lu8ygFFCU0eO4qJ4j2vDzpGwTOWKmD/u7dwIWKyHR43hUSN+FN4SSy1AmHjEKxz0btm7Cki+0YFw0BE4/mB/0wPD251wOS3w0CLsRTfoov9OaGaXApjVSMM74aIX8D46CbwHioLHdAj0/jlVU6gZQIDAQAB
+  -----END PUBLIC KEY-----
+infras.security.jwt.private-key-pem-pkcs8: |-
+  -----BEGIN PRIVATE KEY-----
+  MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAKDNeG8csrKmv4b6AmA4k23wu7zKAUUJTR47ioniPa8POkbBM5YqYP+7t3AhYrIdHjeFRI34U3hJLLUCYeMQrHPRu2bsKSL7RgXDQETj+YH/TA8PbnXA5LfDQIuxFN+ii/05oZpcCmNVIwzvhohfwPjoJvAeKgsd0CPT+OVVTqBlAgMBAAECgYAtNxlyROOKkJCyZ4JbhA0QkOx5PWP2AZOJuLxP4SnvG50LYDAdPXVg82u1P+38n2truTF5qiXuYMUNcMoNixayWEZ074kVTI+FluLO405wwMYHvGPKOJVFIUTsKz+xkg4r48R963D5DZ6ZjoPIjLWvxL1zdrsgi9AOz/skAl0yAQJBANO0yadz1fYinSmYa2O27lgE1DpTvYBXGkY2qG7D/QJv2FwP6pdBy9ejym45UXce4wR1Yrlvh9wsErI4p790XOECQQDCcjti2nbIuZP3Dy5Ej97Y6sIbIEu5MpJW8kBjUzUssxgdE9urA/yWVzT8lmj34he+uWJv6s+e/HBDV5tc0tAFAkBK+q2s4+a0jN/SuovWPhS+Eb/EhKIKEU9Z7MPMrxctxMUBHhX8yi3SyszIKv7CTKskihqUCH86qFVaz5wBv8mhAkBgnQea13ebxnGZmSZhFKciWoq1lbdqPpFtuBJ8B5TtL9N0ZzCHaYSwYoZGVqmzONiZgF1DxIUCtuVE4JumZGzNAkB5B1sUdZfLo4q3jOiX5UQ/a4u17ptemvFPR4OynHkuVkgyAfTIo9SAB8/KIntHMlrgcP03G41ciJrYeP5zv8xm
+  -----END PRIVATE KEY-----
+
+##
+# infras.security cas
+#
+infras.security.reactive.cas.enabled: false
+
+#应用访问地址
+app.server.host.url: http://localhost:8080
+#应用登录地址
+app.login.url: /cas/login
+#应用登出地址
+app.logout.url: /cas/logout
+
+#CAS服务地址
+cas.server.host.url: https://cas.supwisdom.com/cas
+
+
+sw-backend-base-api:
+  uri: http://localhost:8082
diff --git a/gateway/src/main/resources/bootstrap.yml b/gateway/src/main/resources/bootstrap.yml
new file mode 100644
index 0000000..aa852b8
--- /dev/null
+++ b/gateway/src/main/resources/bootstrap.yml
@@ -0,0 +1,3 @@
+spring:
+  application:
+    name: sw-backend-gateway
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..18f1e7b
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,212 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.supwisdom.buildcommons</groupId>
+    <artifactId>spring-cloud-parent</artifactId>
+    <version>Finchley.RELEASE-1.1</version>
+  </parent>
+
+  <groupId>com.supwisdom.institute</groupId>
+  <artifactId>sw-backend-parent</artifactId>
+  <version>0.0.1</version>
+  <packaging>pom</packaging>
+
+  <name>Supwisdom Backend Framework Parent</name>
+  <description>Supwisdom Backend Framework Parent project</description>
+
+  <modules>
+    <module>common</module>
+    
+    <module>base</module>
+    <module>biz</module>
+
+    <module>sa</module>
+    <module>bff</module>
+
+    <module>gateway</module>
+    
+  </modules>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <java.version>1.8</java.version>
+
+    <argLine>-Dfile.encoding=UTF-8</argLine>
+
+    <downloadSources>true</downloadSources>
+    <downloadJavadocs>true</downloadJavadocs>
+
+    <maven.compiler.source>${java.version}</maven.compiler.source>
+    <maven.compiler.target>${java.version}</maven.compiler.target>
+
+    <dockerfile-maven-plugin.version>1.4.8</dockerfile-maven-plugin.version>
+    <dockerfile.image.server>harbor.supwisdom.com</dockerfile.image.server>
+    <dockerfile.image.prefix>sw-admin-framework</dockerfile.image.prefix>
+
+    <infras.version>0.1.1-SNAPSHOT</infras.version>
+
+    <io.springfox.version>2.9.2</io.springfox.version>
+
+    <mybatis.spring.boot.version>1.3.1</mybatis.spring.boot.version>
+
+  </properties>
+
+  <distributionManagement>
+    <repository>
+      <id>supwisdom-releases</id>
+      <name>internal release</name>
+      <url>https://app.supwisdom.com/nexus/content/repositories/releases</url>
+    </repository>
+    <snapshotRepository>
+      <id>supwisdom-snapshots</id>
+      <name>internal snapshots</name>
+      <url>https://app.supwisdom.com/nexus/content/repositories/snapshots</url>
+    </snapshotRepository>
+  </distributionManagement>
+
+  <repositories>
+    <repository>
+      <snapshots>
+        <enabled>true</enabled>
+      </snapshots>
+      <id>supwisdom</id>
+      <url>https://app.supwisdom.com/nexus/content/groups/public/</url>
+    </repository>
+    <repository>
+      <snapshots>
+        <enabled>false</enabled>
+      </snapshots>
+      <id>central</id>
+      <url>http://repo.maven.apache.org/maven2</url>
+    </repository>
+  </repositories>
+
+  <dependencyManagement>
+    <dependencies>
+
+      <dependency>
+        <groupId>com.supwisdom.infras</groupId>
+        <artifactId>infras-bom</artifactId>
+        <version>${infras.version}</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+
+
+      <dependency>
+        <groupId>com.supwisdom.institute</groupId>
+        <artifactId>sw-backend-common-core</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+
+      <dependency>
+        <groupId>com.supwisdom.institute</groupId>
+        <artifactId>sw-backend-common-utils</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+
+      <dependency>
+        <groupId>com.supwisdom.institute</groupId>
+        <artifactId>sw-backend-common-framework</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+
+      <dependency>
+        <groupId>com.supwisdom.institute</groupId>
+        <artifactId>sw-backend-base-domain</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>com.supwisdom.institute</groupId>
+        <artifactId>sw-backend-base-api</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+
+      <dependency>
+        <groupId>com.supwisdom.institute</groupId>
+        <artifactId>sw-backend-biz-domain</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>com.supwisdom.institute</groupId>
+        <artifactId>sw-backend-biz-api</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+
+      <!-- 使用Apache HttpClient替换Feign原生httpclient -->
+      <dependency>
+        <groupId>com.netflix.feign</groupId>
+        <artifactId>feign-httpclient</artifactId>
+        <version>8.17.0</version>
+      </dependency>
+
+      <dependency>
+        <groupId>mysql</groupId>
+        <artifactId>mysql-connector-java</artifactId>
+        <version>8.0.12</version>
+      </dependency>
+
+      <dependency>
+        <groupId>com.alibaba</groupId>
+        <artifactId>fastjson</artifactId>
+        <version>1.2.56</version>
+      </dependency>
+
+
+      <dependency>
+        <groupId>com.google.guava</groupId>
+        <artifactId>guava</artifactId>
+        <version>28.0-jre</version>
+      </dependency>
+
+      <dependency>
+        <groupId>io.springfox</groupId>
+        <artifactId>springfox-swagger2</artifactId>
+        <version>${io.springfox.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>io.springfox</groupId>
+        <artifactId>springfox-swagger-ui</artifactId>
+        <version>${io.springfox.version}</version>
+      </dependency>
+
+    </dependencies>
+  </dependencyManagement>
+
+  <build>
+    <pluginManagement>
+      <plugins>
+        <plugin>
+          <groupId>com.spotify</groupId>
+          <artifactId>dockerfile-maven-plugin</artifactId>
+          <version>${dockerfile-maven-plugin.version}</version>
+          <configuration>
+            <repository>${dockerfile.image.server}/${dockerfile.image.prefix}/${project.artifactId}</repository>
+            <tag>${project.version}</tag>
+            <useMavenSettingsForAuth>true</useMavenSettingsForAuth>
+            <buildArgs>
+              <JAR_FILE>${project.build.finalName}.${project.packaging}</JAR_FILE>
+              <VERSION>${project.version}</VERSION>
+              <NAME>${project.artifactId}</NAME>
+            </buildArgs>
+          </configuration>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+
+    <plugins>
+      <plugin>
+        <groupId>com.spotify</groupId>
+        <artifactId>dockerfile-maven-plugin</artifactId>
+        <configuration>
+          <skip>true</skip>
+        </configuration>
+      </plugin>
+    </plugins>
+
+  </build>
+
+</project>
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..7ea6e4a
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,183 @@
+# 应用开发框架 - 后端框架
+
+
+[TOC]
+
+
+## 目录结构
+
+
+```
+doc               文档
+
+gateway           网关,提供前端的认证、接口路由、访问控制,并将认证的用户信息传递到后端接口、服务接口。
+
+bff               后端项目,提供后端接口,将服务接口做封装后暴露给前端使用。
+                  面向不同的前端,可以创建多个 bff
+
+sa                微服务项目,提供服务接口,将业务类库包装成微服务。
+                  可以考虑将多个业务类库合并为一个 sa,也可以每个业务类库对应一个 sa
+                  业务类库之间存在依赖请求的情况:
+                    当考虑合并为一个 sa 时,则可以直接通过类库依赖的方式(即 领域层 相互依赖)
+                    当纯粹微服务化,即多个 sa 时,须使用 Feign Client 进行远程调用,不可以使用类库依赖
+
+common            公共类库
+
+base              业务类库,基础功能,领域层、接口层的实现
+
+system            业务类库,系统功能,领域层、接口层的实现
+
+biz               业务类库,业务示例,领域层、接口层的实现
+
+```
+
+
+## 技术架构
+
+![](doc/sw-backend/1.arch.png)
+
+
+
+### 网关架构
+
+![](doc/sw-backend/4.gateway_structure.png)
+
+
+
+### 后端项目结构
+
+![](doc/sw-backend/3.bff_structure.png)
+
+
+
+### 服务项目结构
+
+![](doc/sw-backend/2.microservice_structure.png)
+
+
+
+### 授权控制模型
+
+![](doc/sw-backend/5.authn_domain.png)
+
+
+
+## 项目说明
+
+
+### 网关
+
+网关层
+
+完成用户认证、访问控制、访问日志
+
+提供请求转发功能,基于spring cloud gateway,将前端请求 带上 当前登录的用户信息后,转发到 后端接口、或服务接口(建议,避免使用)
+
+
+### 后端项目
+
+BFF Backend for Frontend,用户体验适配层
+
+面向UI,可以为不同终端提供合适的接口,可以避免因UI变更导致频繁地修改 服务接口
+
+对服务接口可以进行聚合、裁剪、适配
+
+对接外部第三方服务、接口
+
+
+### 服务项目
+
+服务层
+
+微服务,业务领域实现,提供无状态的服务接口
+
+完成通用的业务,尽量可以支持业务重用
+
+通过 RESTful API 对外暴露接口
+
+
+## 开发技术
+
+以下 开发技术,自行了解
+
+### Git
+
+代码库管理
+
+
+### Maven
+
+项目构建
+
+
+### Jenkins
+
+自动化构建
+
+
+### Docker
+
+交付产物
+
+
+### k8s / Rancher
+
+部署环境
+
+
+### Swagger2
+
+服务接口文档,接口测试
+
+
+### Spring Boot
+
+项目搭建
+
+
+### Spring Data JPA
+
+处理数据持久化操作,ORM
+
+
+### Spring MVC
+
+服务接口(sa)、后端接口(bff),采用 Spring MVC 的 @RestController 构建 RESTful API
+
+
+### Spring WebFlux
+
+后端接口(bff),由于使用了 Spring Cloud Gateway,故 必须采用 Spring WebFlux
+
+
+### Spring Cloud Security
+
+对后端聚合接口进行安全认证、访问控制
+
+目前支持,Basic 认证、JWT 认证,以及 对接CAS 认证
+
+
+### Spring Cloud Gateway
+
+后端接口(bff),通过 Spring Cloud Gateway,将前端的请求透传到 后端服务接口(sa),透传过程中,会将认证登录的用户信息 通过 请求头 Header(X-FORWARD-USER) 传递到 后端服务接口,后端服务接口 根据实际需要 使用用户信息 完成业务实现
+
+
+### Spring Cloud OpenFeign
+
+后端接口(bff),若一个接口需要由后端服务的多个接口完成,可以通过 FeignClient 请求,随后在 service 层 进行逻辑处理
+
+
+
+## 开发规范
+
+开发规范,主要了解下接口请求、响应相关的规范
+
+详见:[开发规范](doc/DEV_SPEC.md)
+
+
+
+## 使用说明
+
+详见:[使用说明](doc/USAGE.md)
+
+
diff --git a/sa/admin/Dockerfile b/sa/admin/Dockerfile
new file mode 100644
index 0000000..f7bf86e
--- /dev/null
+++ b/sa/admin/Dockerfile
@@ -0,0 +1,18 @@
+FROM harbor.supwisdom.com/institute/openjdk:8-jre-alpine
+
+ENV ENABLE_JMX_SSL=false
+ENV JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -Dspring.profiles.active=docker
+ENV SPRING_PROFILES_ACTIVE=docker
+
+ARG NAME
+ARG VERSION
+ARG JAR_FILE
+
+LABEL name=$NAME \
+      version=$VERSION
+
+EXPOSE 8080
+
+EXPOSE 8443
+
+COPY --chown=java-app:java-app target/${JAR_FILE} /home/java-app/lib/app.jar
diff --git a/sa/admin/pom.xml b/sa/admin/pom.xml
new file mode 100644
index 0000000..5c84a4d
--- /dev/null
+++ b/sa/admin/pom.xml
@@ -0,0 +1,204 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.supwisdom.institute</groupId>
+    <artifactId>sw-backend-parent</artifactId>
+    <version>0.0.1</version>
+    <relativePath>../../</relativePath>
+  </parent>
+
+  <groupId>com.supwisdom.institute</groupId>
+  <artifactId>sw-backend-admin-sa</artifactId>
+  <version>0.0.1</version>
+  <packaging>jar</packaging>
+
+  <name>Supwisdom Backend Framework Admin Super Admin</name>
+  <description>Supwisdom Backend Framework Admin Super Admin project</description>
+
+  <properties>
+    <start-class>com.supwisdom.institute.backend.admin.sa.Application</start-class>
+  </properties>
+
+  <dependencies>
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter</artifactId>
+    </dependency>
+
+    <!-- 微服务 健康监控 -->
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-actuator</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>org.springframework.cloud</groupId>
+      <artifactId>spring-cloud-starter-openfeign</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-web</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-online-doc</artifactId>
+    </dependency>
+
+
+    <!-- <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-mvc</artifactId>
+    </dependency> -->
+
+    <!-- <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-object-mapper</artifactId>
+    </dependency> -->
+
+    <!-- <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-i18n</artifactId>
+    </dependency> -->
+
+    <!-- <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-lang</artifactId>
+    </dependency> -->
+
+
+    <dependency>
+      <groupId>com.supwisdom.institute</groupId>
+      <artifactId>sw-backend-base-api</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>io.springfox</groupId>
+      <artifactId>springfox-swagger2</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.springfox</groupId>
+      <artifactId>springfox-swagger-ui</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>mysql</groupId>
+      <artifactId>mysql-connector-java</artifactId>
+      <scope>runtime</scope>
+    </dependency>
+
+    <!-- 热部署,无需重启项目 -->
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-devtools</artifactId>
+      <scope>runtime</scope>
+    </dependency>
+
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+  </dependencies>
+
+  <build>
+    <finalName>${project.artifactId}</finalName>
+
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-failsafe-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-release-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+      </plugin>
+
+
+      <!-- <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-resources-plugin</artifactId>
+        <version>2.4.3</version>
+        <configuration>
+          <encoding>${project.build.sourceEncoding}</encoding>
+        </configuration>
+        <executions>
+          <execution>
+            <id>copy-doc-resources</id>
+            <phase>package</phase>
+            <goals>
+              <goal>copy-resources</goal>
+            </goals>
+            <configuration>
+              <encoding>utf-8</encoding>
+              <outputDirectory>${basedir}/target/doc</outputDirectory>
+              <overwrite>true</overwrite>
+              <resources>
+                <resource>
+                  <directory>${basedir}/../doc</directory>
+                </resource>
+              </resources>
+            </configuration>
+          </execution>
+          <execution>
+            <id>copy-api-docs-resources</id>
+            <phase>package</phase>
+            <goals>
+              <goal>copy-resources</goal>
+            </goals>
+            <configuration>
+              <encoding>utf-8</encoding>
+              <outputDirectory>${basedir}/target/api-docs</outputDirectory>
+              <overwrite>true</overwrite>
+              <resources>
+                <resource>
+                  <directory>${basedir}/../api-docs</directory>
+                </resource>
+              </resources>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin> -->
+
+
+      <plugin>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-maven-plugin</artifactId>
+      </plugin>
+
+      <plugin>
+        <groupId>com.spotify</groupId>
+        <artifactId>dockerfile-maven-plugin</artifactId>
+        <configuration>
+          <skip>false</skip>
+        </configuration>
+      </plugin>
+
+    </plugins>
+
+  </build>
+
+</project>
diff --git a/sa/admin/src/main/java/com/supwisdom/institute/backend/admin/sa/Application.java b/sa/admin/src/main/java/com/supwisdom/institute/backend/admin/sa/Application.java
new file mode 100644
index 0000000..8bf05fd
--- /dev/null
+++ b/sa/admin/src/main/java/com/supwisdom/institute/backend/admin/sa/Application.java
@@ -0,0 +1,47 @@
+package com.supwisdom.institute.backend.admin.sa;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.domain.EntityScan;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+
+import com.supwisdom.infras.online.doc.configuration.EnableInfrasOnlineDoc;
+import com.supwisdom.institute.backend.common.core.transmit.annotation.EnableSimpleUserTransmit;
+import com.supwisdom.institute.backend.common.framework.exception.EnableCustomExceptionHandler;
+
+@SpringBootApplication
+
+@EnableSimpleUserTransmit
+@EnableCustomExceptionHandler
+
+@EnableInfrasOnlineDoc
+
+@EntityScan(basePackages = {"com.supwisdom.**.domain.entity"})  // 扫描子项目下的实体
+@EnableJpaRepositories(basePackages = {"com.supwisdom.**.domain.repo"})  // 扫描子项目下的持久类
+@ComponentScan(basePackages = {"com.supwisdom"})
+public class Application {
+
+  public static void main(String[] args) {
+    SpringApplication.run(Application.class, args);
+  }
+  
+  @Bean
+  public CorsFilter corsFilter() {
+    final CorsConfiguration config = new CorsConfiguration();
+    // config.setAllowCredentials(true);
+    config.addAllowedOrigin("*");
+    config.addAllowedHeader("*");
+    config.addAllowedMethod("*");
+
+    final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+    source.registerCorsConfiguration("/v2/api-docs", config);
+
+    return new CorsFilter(source);
+  }
+
+}
diff --git a/sa/admin/src/main/java/com/supwisdom/institute/backend/admin/sa/configuration/Swagger2Config.java b/sa/admin/src/main/java/com/supwisdom/institute/backend/admin/sa/configuration/Swagger2Config.java
new file mode 100644
index 0000000..287cea5
--- /dev/null
+++ b/sa/admin/src/main/java/com/supwisdom/institute/backend/admin/sa/configuration/Swagger2Config.java
@@ -0,0 +1,110 @@
+package com.supwisdom.institute.backend.admin.sa.configuration;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.ApiKey;
+import springfox.documentation.service.AuthorizationScope;
+import springfox.documentation.service.Contact;
+import springfox.documentation.service.SecurityReference;
+import springfox.documentation.service.SecurityScheme;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.service.contexts.SecurityContext;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger.web.UiConfiguration;
+import springfox.documentation.swagger.web.UiConfigurationBuilder;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+@Configuration
+@EnableSwagger2
+public class Swagger2Config {
+
+  @Value("${swagger2.apis.basePackage:com.supwisdom.institute}")
+  private String basePackage;
+
+  @Bean
+  public Docket createRestApi() {
+    return new Docket(DocumentationType.SWAGGER_2)
+        .securitySchemes(securitySchemes())
+        .securityContexts(securityContexts())
+        .apiInfo(apiInfo())
+        .select()
+        .apis(RequestHandlerSelectors.basePackage(basePackage))
+        .paths(PathSelectors.any())
+        .build()
+    ;
+  }
+
+  private ApiInfo apiInfo() {
+    Contact contact = new Contact("Backend Admin Super Admin", "https://sw-backend-sa.supwisdom.com/swagger-ui.html", ""); // name, url, email
+    return new ApiInfoBuilder()
+        .title("Backend Admin Super Admin APIs")
+        .description("管理后台 - 服务接口<br /><br />"
+            + "X-FORWARD-USER(测试用):<br /><br />"
+            + "明文:{\"attributes\":{\"accountId\":\"1\"},\"roles\":[\"ROLE_ADMIN\",\"administrator\",\"user\"],\"username\":\"swadmin\"}<br /><br />"
+            + "Base64:eyJhdHRyaWJ1dGVzIjp7ImFjY291bnRJZCI6IjEifSwicm9sZXMiOlsiUk9MRV9BRE1JTiIsImFkbWluaXN0cmF0b3IiLCJ1c2VyIl0sInVzZXJuYW1lIjoic3dhZG1pbiJ9<br /><br />"
+            + "使用 Base64字符串 进行 Authorize,然后进行接口测试<br /><br />"
+            + "若需要其他帐号,请自行拼接明文,再进行 Base64 编码<br /><br />"
+            + ""
+        )
+        .termsOfServiceUrl("http://www.supwisdom.com/")
+        .contact(contact)
+        .version("1.0")
+        .build();
+  }
+
+  private List<SecurityScheme> securitySchemes() {
+    //return newArrayList(new BasicAuth("sample"));
+    return newArrayList(
+        //new BasicAuth("Basic"),
+        //new ApiKey("JWTToken", "Authorization", "header"), 
+        new ApiKey("SimpleUserTransmit", "X-FORWARD-USER", "header"));
+  }
+  
+  private List<SecurityContext> securityContexts() {
+
+    List<SecurityReference> globalSecurityReference = newArrayList(
+        new SecurityReference("SimpleUserTransmit", new AuthorizationScope[]{new AuthorizationScope("global", "accessEverything")}));
+    
+//    AuthorizationScope[] authScopes = new AuthorizationScope[1];
+//    authScopes[0] = new AuthorizationScopeBuilder()
+//            .scope("read")
+//            .description("read access")
+//            .build();
+//    SecurityReference securityReference = SecurityReference.builder()
+//            .reference("sample")
+//            .scopes(authScopes)
+//            .build();
+
+    return newArrayList(
+        SecurityContext.builder()
+          .securityReferences(newArrayList(globalSecurityReference))
+          .build());
+  }
+
+  @Bean
+  UiConfiguration uiConfig() {
+    
+    return UiConfigurationBuilder.builder().build();
+    
+//    return new UiConfiguration(null, // url
+//        "none", // docExpansion => none | list
+//        "alpha", // apiSorter => alpha
+//        "schema", // defaultModelRendering => schema
+//        UiConfiguration.Constants.DEFAULT_SUBMIT_METHODS,
+//        false, // enableJsonEditor => true || false
+//        true, // showRequestHeaders => true | false
+//        60000L); // requestTimeout => in milliseconds, defaults to null
+//    // (uses jquery xh timeout)
+  }
+
+}
diff --git a/sa/admin/src/main/resources/application-docker.yml b/sa/admin/src/main/resources/application-docker.yml
new file mode 100644
index 0000000..d72a475
--- /dev/null
+++ b/sa/admin/src/main/resources/application-docker.yml
@@ -0,0 +1,46 @@
+server:
+  port: ${SERVER_PORT:8443}
+  ssl:
+    enabled: ${SSL_ENABLED:true}
+    clientAuth: NEED
+    key-store: ${SSL_KEYSTORE_FILE:file:/certs/server/server.keystore}
+    key-store-password: ${SSL_KEYSTORE_PASSWORD:}
+    trust-store: ${SSL_TRUSTSTORE_FILE:file:/certs/server/server.truststore}
+    trust-store-password: ${SSL_TRUSTSTORE_PASSWORD:}
+  tomcat: 
+    accesslog: 
+      enabled: ${TOMCAT_ACCESSLOG_ENABLED:false}
+      buffered: ${TOMCAT_ACCESSLOG_BUFFERED:true}
+      directory: ${TOMCAT_ACCESSLOG_DIR:log}
+      prefix: ${TOMCAT_ACCESSLOG_PREFIX:sa-api-accesslog}
+      suffix: ${TOMCAT_ACCESSLOG_SUFFIX:.log}
+      file-date-format: ${TOMCAT_ACCESSLOG_FILE_DATE_FORMAT:.yyyy-MM-dd}
+      rotate: ${TOMCAT_ACCESSLOG_ROTATE:true}
+
+
+##
+# logging
+#
+logging:
+  level:
+    root: INFO
+    com.supwisdom: INFO
+
+
+spring:
+  jackson:
+    time-zone: ${JACKSON_TIME_ZONE:Asia/Shanghai}
+
+  datasource:
+    driver-class-name: ${JDBC_DRIVER_CLASS_NAME:com.mysql.cj.jdbc.Driver}
+    url: ${JDBC_URL:jdbc:mysql://mysql-server:3306/sw-admin}
+    username: ${JDBC_USERNAME:sw-admin}
+    password: ${JDBC_PASSWORD:}
+
+
+##
+# online-doc
+#
+infras.online-doc.enabled: ${INFRAS_ONLINE_DOC_ENABLED:false}
+infras.online-doc.md-docs.staitc.path: ${INFRAS_ONLINE_DOC_MD_DOCS_STATIC_PATH:/doc/}
+infras.online-doc.api-docs.staitc.path: ${INFRAS_ONLINE_DOC_API_DOCS_STATIC_PATH:/api-docs/}
diff --git a/sa/admin/src/main/resources/application.yml b/sa/admin/src/main/resources/application.yml
new file mode 100644
index 0000000..93558d2
--- /dev/null
+++ b/sa/admin/src/main/resources/application.yml
@@ -0,0 +1,48 @@
+server:
+  port: 8082
+  ssl:
+    enabled: false
+
+
+##
+# logging
+#
+logging:
+  level:
+    root: INFO
+    com.supwisdom: DEBUG
+#    org.springframework.web: INFO
+#    org.springframework.cloud.openfeign: INFO
+
+
+swagger2.apis.basePackage: com.supwisdom.institute
+
+
+spring:
+  jackson:
+    time-zone: Asia/Shanghai
+  datasource:
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    url: jdbc:mysql://localhost:3306/sw-admin
+    username: root
+    password: root
+    hikari:
+      data-source-properties:
+        useSSL: false
+        characterEncoding: utf8
+        characterSetResults: utf8
+  jpa: 
+    hibernate:
+      ddl-auto: none
+      naming:
+        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
+    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
+    show-sql: false
+
+
+##
+# infras.online-doc
+#
+infras.online-doc.enabled: true
+infras.online-doc.md-docs.staitc.path: /Users/loie/c/work/git/institute/sw-backend/doc/
+infras.online-doc.api-docs.staitc.path: /Users/loie/c/work/git/institute/sw-backend/api-docs/
diff --git a/sa/admin/src/main/resources/bootstrap.yml b/sa/admin/src/main/resources/bootstrap.yml
new file mode 100644
index 0000000..5e82cca
--- /dev/null
+++ b/sa/admin/src/main/resources/bootstrap.yml
@@ -0,0 +1,3 @@
+spring:
+  application:
+    name: sw-backend-admin-sa
diff --git a/sa/biz/.gitignore b/sa/biz/.gitignore
new file mode 100644
index 0000000..b83d222
--- /dev/null
+++ b/sa/biz/.gitignore
@@ -0,0 +1 @@
+/target/
diff --git a/sa/biz/Dockerfile b/sa/biz/Dockerfile
new file mode 100644
index 0000000..f7bf86e
--- /dev/null
+++ b/sa/biz/Dockerfile
@@ -0,0 +1,18 @@
+FROM harbor.supwisdom.com/institute/openjdk:8-jre-alpine
+
+ENV ENABLE_JMX_SSL=false
+ENV JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -Dspring.profiles.active=docker
+ENV SPRING_PROFILES_ACTIVE=docker
+
+ARG NAME
+ARG VERSION
+ARG JAR_FILE
+
+LABEL name=$NAME \
+      version=$VERSION
+
+EXPOSE 8080
+
+EXPOSE 8443
+
+COPY --chown=java-app:java-app target/${JAR_FILE} /home/java-app/lib/app.jar
diff --git a/sa/biz/pom.xml b/sa/biz/pom.xml
new file mode 100644
index 0000000..6d693af
--- /dev/null
+++ b/sa/biz/pom.xml
@@ -0,0 +1,204 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.supwisdom.institute</groupId>
+    <artifactId>sw-backend-parent</artifactId>
+    <version>0.0.1</version>
+    <relativePath>../../</relativePath>
+  </parent>
+
+  <groupId>com.supwisdom.institute</groupId>
+  <artifactId>sw-backend-biz-sa</artifactId>
+  <version>0.0.1</version>
+  <packaging>jar</packaging>
+
+  <name>Supwisdom Backend Framework Biz Super Admin</name>
+  <description>Supwisdom Backend Framework Biz Super Admin project</description>
+
+  <properties>
+    <start-class>com.supwisdom.institute.backend.biz.sa.Application</start-class>
+  </properties>
+
+  <dependencies>
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter</artifactId>
+    </dependency>
+
+    <!-- 微服务 健康监控 -->
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-actuator</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>org.springframework.cloud</groupId>
+      <artifactId>spring-cloud-starter-openfeign</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-web</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-online-doc</artifactId>
+    </dependency>
+
+
+    <!-- <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-mvc</artifactId>
+    </dependency> -->
+
+    <!-- <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-object-mapper</artifactId>
+    </dependency> -->
+
+    <!-- <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-i18n</artifactId>
+    </dependency> -->
+
+    <!-- <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-lang</artifactId>
+    </dependency> -->
+
+
+    <dependency>
+      <groupId>com.supwisdom.institute</groupId>
+      <artifactId>sw-backend-biz-api</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>io.springfox</groupId>
+      <artifactId>springfox-swagger2</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.springfox</groupId>
+      <artifactId>springfox-swagger-ui</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>mysql</groupId>
+      <artifactId>mysql-connector-java</artifactId>
+      <scope>runtime</scope>
+    </dependency>
+
+    <!-- 热部署,无需重启项目 -->
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-devtools</artifactId>
+      <scope>runtime</scope>
+    </dependency>
+
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+  </dependencies>
+
+  <build>
+    <finalName>${project.artifactId}</finalName>
+
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-failsafe-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-release-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+      </plugin>
+
+
+      <!-- <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-resources-plugin</artifactId>
+        <version>2.4.3</version>
+        <configuration>
+          <encoding>${project.build.sourceEncoding}</encoding>
+        </configuration>
+        <executions>
+          <execution>
+            <id>copy-doc-resources</id>
+            <phase>package</phase>
+            <goals>
+              <goal>copy-resources</goal>
+            </goals>
+            <configuration>
+              <encoding>utf-8</encoding>
+              <outputDirectory>${basedir}/target/doc</outputDirectory>
+              <overwrite>true</overwrite>
+              <resources>
+                <resource>
+                  <directory>${basedir}/../doc</directory>
+                </resource>
+              </resources>
+            </configuration>
+          </execution>
+          <execution>
+            <id>copy-api-docs-resources</id>
+            <phase>package</phase>
+            <goals>
+              <goal>copy-resources</goal>
+            </goals>
+            <configuration>
+              <encoding>utf-8</encoding>
+              <outputDirectory>${basedir}/target/api-docs</outputDirectory>
+              <overwrite>true</overwrite>
+              <resources>
+                <resource>
+                  <directory>${basedir}/../api-docs</directory>
+                </resource>
+              </resources>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin> -->
+
+
+      <plugin>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-maven-plugin</artifactId>
+      </plugin>
+
+      <plugin>
+        <groupId>com.spotify</groupId>
+        <artifactId>dockerfile-maven-plugin</artifactId>
+        <configuration>
+          <skip>false</skip>
+        </configuration>
+      </plugin>
+
+    </plugins>
+
+  </build>
+
+</project>
diff --git a/sa/biz/src/main/java/com/supwisdom/institute/backend/biz/sa/Application.java b/sa/biz/src/main/java/com/supwisdom/institute/backend/biz/sa/Application.java
new file mode 100644
index 0000000..b2b44be
--- /dev/null
+++ b/sa/biz/src/main/java/com/supwisdom/institute/backend/biz/sa/Application.java
@@ -0,0 +1,47 @@
+package com.supwisdom.institute.backend.biz.sa;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.domain.EntityScan;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+
+import com.supwisdom.infras.online.doc.configuration.EnableInfrasOnlineDoc;
+import com.supwisdom.institute.backend.common.core.transmit.annotation.EnableSimpleUserTransmit;
+import com.supwisdom.institute.backend.common.framework.exception.EnableCustomExceptionHandler;
+
+@SpringBootApplication
+
+@EnableSimpleUserTransmit
+@EnableCustomExceptionHandler
+
+@EnableInfrasOnlineDoc
+
+@EntityScan(basePackages = {"com.supwisdom.**.domain.entity"})  // 扫描子项目下的实体
+@EnableJpaRepositories(basePackages = {"com.supwisdom.**.domain.repo"})  // 扫描子项目下的持久类
+@ComponentScan(basePackages = {"com.supwisdom"})
+public class Application {
+
+  public static void main(String[] args) {
+    SpringApplication.run(Application.class, args);
+  }
+  
+  @Bean
+  public CorsFilter corsFilter() {
+    final CorsConfiguration config = new CorsConfiguration();
+    // config.setAllowCredentials(true);
+    config.addAllowedOrigin("*");
+    config.addAllowedHeader("*");
+    config.addAllowedMethod("*");
+
+    final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+    source.registerCorsConfiguration("/v2/api-docs", config);
+
+    return new CorsFilter(source);
+  }
+
+}
diff --git a/sa/biz/src/main/java/com/supwisdom/institute/backend/biz/sa/configuration/Swagger2Config.java b/sa/biz/src/main/java/com/supwisdom/institute/backend/biz/sa/configuration/Swagger2Config.java
new file mode 100644
index 0000000..f2ffe33
--- /dev/null
+++ b/sa/biz/src/main/java/com/supwisdom/institute/backend/biz/sa/configuration/Swagger2Config.java
@@ -0,0 +1,110 @@
+package com.supwisdom.institute.backend.biz.sa.configuration;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.ApiKey;
+import springfox.documentation.service.AuthorizationScope;
+import springfox.documentation.service.Contact;
+import springfox.documentation.service.SecurityReference;
+import springfox.documentation.service.SecurityScheme;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.service.contexts.SecurityContext;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger.web.UiConfiguration;
+import springfox.documentation.swagger.web.UiConfigurationBuilder;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+@Configuration
+@EnableSwagger2
+public class Swagger2Config {
+
+  @Value("${swagger2.apis.basePackage:com.supwisdom.institute}")
+  private String basePackage;
+
+  @Bean
+  public Docket createRestApi() {
+    return new Docket(DocumentationType.SWAGGER_2)
+        .securitySchemes(securitySchemes())
+        .securityContexts(securityContexts())
+        .apiInfo(apiInfo())
+        .select()
+        .apis(RequestHandlerSelectors.basePackage(basePackage))
+        .paths(PathSelectors.any())
+        .build()
+    ;
+  }
+
+  private ApiInfo apiInfo() {
+    Contact contact = new Contact("Backend Biz Super Admin", "https://sw-backend-biz-sa.supwisdom.com/swagger-ui.html", ""); // name, url, email
+    return new ApiInfoBuilder()
+        .title("Backend Biz Super Admin APIs")
+        .description("管理后台 - 服务接口<br /><br />"
+            + "X-FORWARD-USER(测试用):<br /><br />"
+            + "明文:{\"attributes\":{\"accountId\":\"1\"},\"roles\":[\"ROLE_ADMIN\",\"administrator\",\"user\"],\"username\":\"swadmin\"}<br /><br />"
+            + "Base64:eyJhdHRyaWJ1dGVzIjp7ImFjY291bnRJZCI6IjEifSwicm9sZXMiOlsiUk9MRV9BRE1JTiIsImFkbWluaXN0cmF0b3IiLCJ1c2VyIl0sInVzZXJuYW1lIjoic3dhZG1pbiJ9<br /><br />"
+            + "使用 Base64字符串 进行 Authorize,然后进行接口测试<br /><br />"
+            + "若需要其他帐号,请自行拼接明文,再进行 Base64 编码<br /><br />"
+            + ""
+        )
+        .termsOfServiceUrl("http://www.supwisdom.com/")
+        .contact(contact)
+        .version("1.0")
+        .build();
+  }
+
+  private List<SecurityScheme> securitySchemes() {
+    //return newArrayList(new BasicAuth("sample"));
+    return newArrayList(
+        //new BasicAuth("Basic"),
+        //new ApiKey("JWTToken", "Authorization", "header"), 
+        new ApiKey("SimpleUserTransmit", "X-FORWARD-USER", "header"));
+  }
+  
+  private List<SecurityContext> securityContexts() {
+
+    List<SecurityReference> globalSecurityReference = newArrayList(
+        new SecurityReference("SimpleUserTransmit", new AuthorizationScope[]{new AuthorizationScope("global", "accessEverything")}));
+    
+//    AuthorizationScope[] authScopes = new AuthorizationScope[1];
+//    authScopes[0] = new AuthorizationScopeBuilder()
+//            .scope("read")
+//            .description("read access")
+//            .build();
+//    SecurityReference securityReference = SecurityReference.builder()
+//            .reference("sample")
+//            .scopes(authScopes)
+//            .build();
+
+    return newArrayList(
+        SecurityContext.builder()
+          .securityReferences(newArrayList(globalSecurityReference))
+          .build());
+  }
+
+  @Bean
+  UiConfiguration uiConfig() {
+    
+    return UiConfigurationBuilder.builder().build();
+    
+//    return new UiConfiguration(null, // url
+//        "none", // docExpansion => none | list
+//        "alpha", // apiSorter => alpha
+//        "schema", // defaultModelRendering => schema
+//        UiConfiguration.Constants.DEFAULT_SUBMIT_METHODS,
+//        false, // enableJsonEditor => true || false
+//        true, // showRequestHeaders => true | false
+//        60000L); // requestTimeout => in milliseconds, defaults to null
+//    // (uses jquery xh timeout)
+  }
+
+}
diff --git a/sa/biz/src/main/resources/application-docker.yml b/sa/biz/src/main/resources/application-docker.yml
new file mode 100644
index 0000000..cbf3924
--- /dev/null
+++ b/sa/biz/src/main/resources/application-docker.yml
@@ -0,0 +1,46 @@
+server:
+  port: ${SERVER_PORT:8443}
+  ssl:
+    enabled: ${SSL_ENABLED:true}
+    clientAuth: NEED
+    key-store: ${SSL_KEYSTORE_FILE:file:/certs/server/server.keystore}
+    key-store-password: ${SSL_KEYSTORE_PASSWORD:}
+    trust-store: ${SSL_TRUSTSTORE_FILE:file:/certs/server/server.truststore}
+    trust-store-password: ${SSL_TRUSTSTORE_PASSWORD:}
+  tomcat: 
+    accesslog: 
+      enabled: ${TOMCAT_ACCESSLOG_ENABLED:false}
+      buffered: ${TOMCAT_ACCESSLOG_BUFFERED:true}
+      directory: ${TOMCAT_ACCESSLOG_DIR:log}
+      prefix: ${TOMCAT_ACCESSLOG_PREFIX:sa-api-accesslog}
+      suffix: ${TOMCAT_ACCESSLOG_SUFFIX:.log}
+      file-date-format: ${TOMCAT_ACCESSLOG_FILE_DATE_FORMAT:.yyyy-MM-dd}
+      rotate: ${TOMCAT_ACCESSLOG_ROTATE:true}
+
+
+##
+# logging
+#
+logging:
+  level:
+    root: INFO
+    com.supwisdom: INFO
+
+
+spring:
+  jackson:
+    time-zone: ${JACKSON_TIME_ZONE:Asia/Shanghai}
+
+  datasource:
+    driver-class-name: ${JDBC_DRIVER_CLASS_NAME:com.mysql.cj.jdbc.Driver}
+    url: ${JDBC_URL:jdbc:mysql://mysql-server:3306/sw-biz}
+    username: ${JDBC_USERNAME:sw-biz}
+    password: ${JDBC_PASSWORD:}
+
+
+##
+# online-doc
+#
+infras.online-doc.enabled: ${INFRAS_ONLINE_DOC_ENABLED:false}
+infras.online-doc.md-docs.staitc.path: ${INFRAS_ONLINE_DOC_MD_DOCS_STATIC_PATH:/doc/}
+infras.online-doc.api-docs.staitc.path: ${INFRAS_ONLINE_DOC_API_DOCS_STATIC_PATH:/api-docs/}
diff --git a/sa/biz/src/main/resources/application.yml b/sa/biz/src/main/resources/application.yml
new file mode 100644
index 0000000..531f8bd
--- /dev/null
+++ b/sa/biz/src/main/resources/application.yml
@@ -0,0 +1,48 @@
+server:
+  port: 8083
+  ssl:
+    enabled: false
+
+
+##
+# logging
+#
+logging:
+  level:
+    root: INFO
+    com.supwisdom: DEBUG
+#    org.springframework.web: INFO
+#    org.springframework.cloud.openfeign: INFO
+
+
+swagger2.apis.basePackage: com.supwisdom.institute
+
+
+spring:
+  jackson:
+    time-zone: Asia/Shanghai
+  datasource:
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    url: jdbc:mysql://localhost:3306/sw-biz
+    username: root
+    password: root
+    hikari:
+      data-source-properties:
+        useSSL: false
+        characterEncoding: utf8
+        characterSetResults: utf8
+  jpa: 
+    hibernate:
+      ddl-auto: none
+      naming:
+        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
+    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
+    show-sql: false
+
+
+##
+# infras.online-doc
+#
+infras.online-doc.enabled: true
+infras.online-doc.md-docs.staitc.path: /Users/loie/c/work/git/institute/sw-backend/doc/
+infras.online-doc.api-docs.staitc.path: /Users/loie/c/work/git/institute/sw-backend/api-docs/
diff --git a/sa/biz/src/main/resources/bootstrap.yml b/sa/biz/src/main/resources/bootstrap.yml
new file mode 100644
index 0000000..8c1026a
--- /dev/null
+++ b/sa/biz/src/main/resources/bootstrap.yml
@@ -0,0 +1,3 @@
+spring:
+  application:
+    name: sw-backend-biz-sa
diff --git a/sa/pom.xml b/sa/pom.xml
new file mode 100644
index 0000000..f10578e
--- /dev/null
+++ b/sa/pom.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  
+  <parent>
+    <groupId>com.supwisdom.institute</groupId>
+    <artifactId>sw-backend-parent</artifactId>
+    <version>0.0.1</version>
+  </parent>
+  
+  <groupId>com.supwisdom.institute</groupId>
+  <artifactId>sw-backend-sa-parent</artifactId>
+  <version>0.0.1</version>
+  <packaging>pom</packaging>
+
+  <name>Supwisdom Backend Framework Super Admin</name>
+  <description>Supwisdom Backend Framework Super Admin project</description>
+
+  <modules>
+    <module>admin</module>
+    <module>biz</module>
+  </modules>
+
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-deploy-plugin</artifactId>
+        <configuration>
+          <skip>true</skip>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/sql/base.sql b/sql/base.sql
new file mode 100644
index 0000000..2701905
--- /dev/null
+++ b/sql/base.sql
@@ -0,0 +1,649 @@
+
+
+CREATE TABLE `TB_BASE_APPLICATION` (
+  `ID` VARCHAR(100) NOT NULL COMMENT '',
+  `COMPANY_ID` VARCHAR(100) COMMENT 'CompanyID',
+  `DELETED` INT(1) COMMENT '是否删除',
+  `ADD_ACCOUNT` VARCHAR(100) COMMENT '创建人',
+  `ADD_TIME` DATETIME COMMENT '创建时间',
+  `EDIT_ACCOUNT` VARCHAR(100) COMMENT '修改人',
+  `EDIT_TIME` DATETIME COMMENT '修改时间',
+  `DELETE_ACCOUNT` VARCHAR(100) COMMENT '删除人',
+  `DELETE_TIME` DATETIME COMMENT '删除时间',
+
+  `CODE` VARCHAR(200) NOT NULL COMMENT '代码',
+  `NAME` VARCHAR(200) NOT NULL COMMENT '名称',
+  `MEMO` VARCHAR(500) COMMENT '备注',
+  `STATUS` VARCHAR(10) NOT NULL COMMENT '状态(1 启用,0 停用)',
+
+  `URL` VARCHAR(500) COMMENT '系统地址',
+
+  `SPI_URL_PREFIX` VARCHAR(500) COMMENT '同步接口服务(用于同步菜单)',
+
+  PRIMARY KEY (`ID`),
+  UNIQUE KEY `UQ_CODE` (`COMPANY_ID`,`CODE`)
+)
+COMMENT = '系统信息表';
+
+
+insert into TB_BASE_APPLICATION (ID, DELETED, CODE, NAME, STATUS) 
+values ('1', 0, 'admin', '树维管理后台', '1');
+
+commit;
+
+
+
+CREATE TABLE `TB_BASE_ROUTE` (
+  `ID` VARCHAR(100) NOT NULL COMMENT '',
+  `COMPANY_ID` VARCHAR(100) COMMENT 'CompanyID',
+  `DELETED` INT(1) COMMENT '是否删除',
+  `ADD_ACCOUNT` VARCHAR(100) COMMENT '创建人',
+  `ADD_TIME` DATETIME COMMENT '创建时间',
+  `EDIT_ACCOUNT` VARCHAR(100) COMMENT '修改人',
+  `EDIT_TIME` DATETIME COMMENT '修改时间',
+  `DELETE_ACCOUNT` VARCHAR(100) COMMENT '删除人',
+  `DELETE_TIME` DATETIME COMMENT '删除时间',
+
+  `CODE` VARCHAR(200) NOT NULL COMMENT '代码',
+  `NAME` VARCHAR(200) NOT NULL COMMENT '名称',
+  `MEMO` VARCHAR(500) COMMENT '备注',
+  `STATUS` VARCHAR(10) NOT NULL COMMENT '状态(1 启用,0 停用)',
+
+  `PATH_PREFIX` VARCHAR(100) NOT NULL COMMENT '路径前缀',
+  `URL` VARCHAR(500) NOT NULL COMMENT '路由地址',
+  `STRIP_PREFIX` INT(1) COMMENT '是否排除前缀',
+
+  PRIMARY KEY (`ID`),
+  UNIQUE KEY `UQ_CODE` (`COMPANY_ID`,`CODE`)
+)
+COMMENT = '路由信息表';
+
+
+
+insert into TB_BASE_ROUTE (ID, DELETED, CODE, NAME, STATUS, PATH_PREFIX, URL, STRIP_PREFIX) 
+values ('1', 0, 'bff-api', '管理服务 - 聚合接口', '1', '/api/bff', 'http://localhost:8081', 1);
+
+insert into TB_BASE_ROUTE (ID, DELETED, CODE, NAME, STATUS, PATH_PREFIX, URL, STRIP_PREFIX) 
+values ('10', 0, 'admin-base-api', '管理服务 - 基础接口', '1', '/api/base', 'http://localhost:8082', 1);
+insert into TB_BASE_ROUTE (ID, DELETED, CODE, NAME, STATUS, PATH_PREFIX, URL, STRIP_PREFIX) 
+values ('20', 0, 'adminsystem-api', '管理服务 - 系统接口', '1', '/api/system', 'http://localhost:8082', 1);
+insert into TB_BASE_ROUTE (ID, DELETED, CODE, NAME, STATUS, PATH_PREFIX, URL, STRIP_PREFIX) 
+values ('30', 0, 'biz-api', '管理服务 - 业务接口', '1', '/api/biz', 'http://localhost:8083', 1);
+
+commit;
+
+
+
+
+CREATE TABLE `TB_BASE_ACCOUNT` (
+  `ID` VARCHAR(100) NOT NULL COMMENT '',
+  `COMPANY_ID` VARCHAR(100) COMMENT 'CompanyID',
+  `DELETED` INT(1) COMMENT '是否删除',
+  `ADD_ACCOUNT` VARCHAR(100) COMMENT '创建人',
+  `ADD_TIME` DATETIME COMMENT '创建时间',
+  `EDIT_ACCOUNT` VARCHAR(100) COMMENT '修改人',
+  `EDIT_TIME` DATETIME COMMENT '修改时间',
+  `DELETE_ACCOUNT` VARCHAR(100) COMMENT '删除人',
+  `DELETE_TIME` DATETIME COMMENT '删除时间',
+
+  `USERNAME` VARCHAR(200) NOT NULL COMMENT '账号',
+  `PASSWORD` VARCHAR(200) NOT NULL COMMENT '密码',
+
+  `ENABLED` INT(11) NOT NULL COMMENT '是否可用,1 可用,0 不可用,默认:1',
+  `ACCOUNT_NON_EXPIRED` INT(11) NOT NULL COMMENT '账号未过期,1 未过期,0 过期,默认:1',
+  `ACCOUNT_NON_LOCKED` INT(11) NOT NULL COMMENT '账号未锁定,1 未锁定,0 锁定,默认:1',
+  `CREDENTIALS_NON_EXPIRED` INT(11) NOT NULL COMMENT '密码未过期,1 未过期,0 过期,默认:1',
+
+  `NAME` VARCHAR(200) NOT NULL COMMENT '姓名',
+  `MEMO` VARCHAR(500) COMMENT '备注',
+  `STATUS` VARCHAR(10) NOT NULL COMMENT '状态(1 启用,0 停用)',
+
+  `MOBILE` VARCHAR(100) COMMENT '登录手机',
+  `EMAIL` VARCHAR(100) COMMENT '登录邮箱',
+
+  PRIMARY KEY (`ID`),
+  UNIQUE KEY `UQ_USERNAME` (`USERNAME`)
+)
+COMMENT = '帐号表';
+
+
+CREATE TABLE `TB_BASE_GROUP` (
+  `ID` VARCHAR(100) NOT NULL COMMENT '',
+  `COMPANY_ID` VARCHAR(100) COMMENT 'CompanyID',
+  `DELETED` INT(1) COMMENT '是否删除',
+  `ADD_ACCOUNT` VARCHAR(100) COMMENT '创建人',
+  `ADD_TIME` DATETIME COMMENT '创建时间',
+  `EDIT_ACCOUNT` VARCHAR(100) COMMENT '修改人',
+  `EDIT_TIME` DATETIME COMMENT '修改时间',
+  `DELETE_ACCOUNT` VARCHAR(100) COMMENT '删除人',
+  `DELETE_TIME` DATETIME COMMENT '删除时间',
+
+  `CODE` VARCHAR(200) NOT NULL COMMENT '代码',
+  `NAME` VARCHAR(200) NOT NULL COMMENT '名称',
+  `MEMO` VARCHAR(500) COMMENT '备注',
+  `STATUS` VARCHAR(10) NOT NULL COMMENT '状态(1 启用,0 停用)',
+
+  PRIMARY KEY (`ID`),
+  UNIQUE KEY `UQ_CODE` (`COMPANY_ID`,`CODE`)
+)
+COMMENT = '用户组表';
+
+
+CREATE TABLE `TB_BASE_ROLE` (
+  `ID` VARCHAR(100) NOT NULL COMMENT '',
+  `COMPANY_ID` VARCHAR(100) COMMENT 'CompanyID',
+  `DELETED` INT(1) COMMENT '是否删除',
+  `ADD_ACCOUNT` VARCHAR(100) COMMENT '创建人',
+  `ADD_TIME` DATETIME COMMENT '创建时间',
+  `EDIT_ACCOUNT` VARCHAR(100) COMMENT '修改人',
+  `EDIT_TIME` DATETIME COMMENT '修改时间',
+  `DELETE_ACCOUNT` VARCHAR(100) COMMENT '删除人',
+  `DELETE_TIME` DATETIME COMMENT '删除时间',
+
+  `CODE` VARCHAR(200) NOT NULL COMMENT '代码',
+  `NAME` VARCHAR(200) NOT NULL COMMENT '名称',
+  `MEMO` VARCHAR(500) COMMENT '备注',
+  `STATUS` VARCHAR(10) NOT NULL COMMENT '状态(1 启用,0 停用)',
+
+  PRIMARY KEY (`ID`),
+  UNIQUE KEY `UQ_CODE` (`COMPANY_ID`,`CODE`)
+)
+COMMENT = '角色表';
+
+
+
+CREATE TABLE `TB_BASE_ACCOUNT_GROUP` (
+  `ID` VARCHAR(100) NOT NULL COMMENT '',
+  `COMPANY_ID` VARCHAR(100) COMMENT 'CompanyID',
+  `DELETED` INT(1) COMMENT '是否删除',
+  `ADD_ACCOUNT` VARCHAR(100) COMMENT '创建人',
+  `ADD_TIME` DATETIME COMMENT '创建时间',
+  `EDIT_ACCOUNT` VARCHAR(100) COMMENT '修改人',
+  `EDIT_TIME` DATETIME COMMENT '修改时间',
+  `DELETE_ACCOUNT` VARCHAR(100) COMMENT '删除人',
+  `DELETE_TIME` DATETIME COMMENT '删除时间',
+
+  `ACCOUNT_ID` VARCHAR(200) NOT NULL COMMENT '帐号ID',
+  `GROUP_ID` VARCHAR(200) NOT NULL COMMENT '用户组ID',
+
+  PRIMARY KEY (`ID`)
+)
+COMMENT = '用户组 - 帐号关联表';
+
+
+
+CREATE TABLE `TB_BASE_ACCOUNT_ROLE` (
+  `ID` VARCHAR(100) NOT NULL COMMENT '',
+  `COMPANY_ID` VARCHAR(100) COMMENT 'CompanyID',
+  `DELETED` INT(1) COMMENT '是否删除',
+  `ADD_ACCOUNT` VARCHAR(100) COMMENT '创建人',
+  `ADD_TIME` DATETIME COMMENT '创建时间',
+  `EDIT_ACCOUNT` VARCHAR(100) COMMENT '修改人',
+  `EDIT_TIME` DATETIME COMMENT '修改时间',
+  `DELETE_ACCOUNT` VARCHAR(100) COMMENT '删除人',
+  `DELETE_TIME` DATETIME COMMENT '删除时间',
+
+  `ACCOUNT_ID` VARCHAR(200) NOT NULL COMMENT '帐号ID',
+  `ROLE_ID` VARCHAR(200) NOT NULL COMMENT '角色ID',
+
+  PRIMARY KEY (`ID`)
+)
+COMMENT = '帐号 - 角色关联表';
+
+
+
+CREATE TABLE `TB_BASE_GROUP_ROLE` (
+  `ID` VARCHAR(100) NOT NULL COMMENT '',
+  `COMPANY_ID` VARCHAR(100) COMMENT 'CompanyID',
+  `DELETED` INT(1) COMMENT '是否删除',
+  `ADD_ACCOUNT` VARCHAR(100) COMMENT '创建人',
+  `ADD_TIME` DATETIME COMMENT '创建时间',
+  `EDIT_ACCOUNT` VARCHAR(100) COMMENT '修改人',
+  `EDIT_TIME` DATETIME COMMENT '修改时间',
+  `DELETE_ACCOUNT` VARCHAR(100) COMMENT '删除人',
+  `DELETE_TIME` DATETIME COMMENT '删除时间',
+
+  `GROUP_ID` VARCHAR(200) NOT NULL COMMENT '用户组ID',
+  `ROLE_ID` VARCHAR(200) NOT NULL COMMENT '角色ID',
+
+  PRIMARY KEY (`ID`)
+)
+COMMENT = '用户组 - 角色关联表';
+
+
+
+CREATE TABLE `TB_BASE_PERMISSION` (
+  `ID` VARCHAR(100) NOT NULL COMMENT '',
+  `COMPANY_ID` VARCHAR(100) COMMENT 'CompanyID',
+  `DELETED` INT(1) COMMENT '是否删除',
+  `ADD_ACCOUNT` VARCHAR(100) COMMENT '创建人',
+  `ADD_TIME` DATETIME COMMENT '创建时间',
+  `EDIT_ACCOUNT` VARCHAR(100) COMMENT '修改人',
+  `EDIT_TIME` DATETIME COMMENT '修改时间',
+  `DELETE_ACCOUNT` VARCHAR(100) COMMENT '删除人',
+  `DELETE_TIME` DATETIME COMMENT '删除时间',
+
+  `CODE` VARCHAR(200) NOT NULL COMMENT '代码',
+  `NAME` VARCHAR(200) NOT NULL COMMENT '名称',
+  `MEMO` VARCHAR(500) COMMENT '备注',
+  `STATUS` VARCHAR(10) NOT NULL COMMENT '状态(1 启用,0 停用)',
+
+  `TYPE_` VARCHAR(10) NOT NULL COMMENT '类型(1 应用,2 菜单,3 操作)',
+  `ICON` VARCHAR(500) COMMENT '菜单图标',
+  `URL` VARCHAR(500) COMMENT 'URL地址',
+
+  `APPLICATION_ID` VARCHAR(100) COMMENT '系统ID', 
+  `PARENT_ID` VARCHAR(100) NOT NULL COMMENT '父级ID', 
+  `ORDER_` INT(11) NOT NULL COMMENT '排序',
+  `LEVEL_` INT(11) NOT NULL COMMENT '层次',
+  `LFT` INT(11) NOT NULL COMMENT '左索引',
+  `RGT` INT(11) NOT NULL COMMENT '右索引',
+
+  PRIMARY KEY (`ID`),
+  UNIQUE KEY `UQ_CODE` (`COMPANY_ID`,`CODE`)
+)
+COMMENT = '权限表';
+
+
+
+CREATE TABLE `TB_BASE_ROLE_PERMISSION` (
+  `ID` VARCHAR(100) NOT NULL COMMENT '',
+  `COMPANY_ID` VARCHAR(100) COMMENT 'CompanyID',
+  `DELETED` INT(1) COMMENT '是否删除',
+  `ADD_ACCOUNT` VARCHAR(100) COMMENT '创建人',
+  `ADD_TIME` DATETIME COMMENT '创建时间',
+  `EDIT_ACCOUNT` VARCHAR(100) COMMENT '修改人',
+  `EDIT_TIME` DATETIME COMMENT '修改时间',
+  `DELETE_ACCOUNT` VARCHAR(100) COMMENT '删除人',
+  `DELETE_TIME` DATETIME COMMENT '删除时间',
+
+  `ROLE_ID` VARCHAR(200) NOT NULL COMMENT '角色ID',
+  `PERMISSION_ID` VARCHAR(200) NOT NULL COMMENT '权限ID',
+
+  PRIMARY KEY (`ID`)
+)
+COMMENT = '角色 - 权限关联表';
+
+
+
+CREATE TABLE `TB_BASE_RESOURCE` (
+  `ID` VARCHAR(100) NOT NULL COMMENT '',
+  `COMPANY_ID` VARCHAR(100) COMMENT 'CompanyID',
+  `DELETED` INT(1) COMMENT '是否删除',
+  `ADD_ACCOUNT` VARCHAR(100) COMMENT '创建人',
+  `ADD_TIME` DATETIME COMMENT '创建时间',
+  `EDIT_ACCOUNT` VARCHAR(100) COMMENT '修改人',
+  `EDIT_TIME` DATETIME COMMENT '修改时间',
+  `DELETE_ACCOUNT` VARCHAR(100) COMMENT '删除人',
+  `DELETE_TIME` DATETIME COMMENT '删除时间',
+
+  `CODE` VARCHAR(200) NOT NULL COMMENT '代码',
+  `NAME` VARCHAR(200) NOT NULL COMMENT '名称',
+  `MEMO` VARCHAR(500) COMMENT '备注',
+  `STATUS` VARCHAR(10) NOT NULL COMMENT '状态(1 启用,0 停用)',
+
+  `METHOD` VARCHAR(100) COMMENT '请求方式(GET、POST、PUT、DELETE 等)',
+  `PATH` VARCHAR(500) COMMENT '请求地址',
+  `ACCESS` VARCHAR(100) COMMENT '访问规则(匿名访问anonymous、认证访问authenticate、授权访问authorize、允许所有permitAll、拒绝所有denyAll)',
+
+  PRIMARY KEY (`ID`),
+  UNIQUE KEY `UQ_CODE` (`COMPANY_ID`,`CODE`)
+)
+COMMENT = '资源(API)表';
+
+
+CREATE TABLE `TB_BASE_PERMISSION_RESOURCE` (
+  `ID` VARCHAR(100) NOT NULL COMMENT '',
+  `COMPANY_ID` VARCHAR(100) COMMENT 'CompanyID',
+  `DELETED` INT(1) COMMENT '是否删除',
+  `ADD_ACCOUNT` VARCHAR(100) COMMENT '创建人',
+  `ADD_TIME` DATETIME COMMENT '创建时间',
+  `EDIT_ACCOUNT` VARCHAR(100) COMMENT '修改人',
+  `EDIT_TIME` DATETIME COMMENT '修改时间',
+  `DELETE_ACCOUNT` VARCHAR(100) COMMENT '删除人',
+  `DELETE_TIME` DATETIME COMMENT '删除时间',
+
+  `PERMISSION_ID` VARCHAR(200) NOT NULL COMMENT '权限ID',
+  `RESOURCE_ID` VARCHAR(200) NOT NULL COMMENT '资源ID',
+
+  PRIMARY KEY (`ID`)
+)
+COMMENT = '权限 - 资源关联表';
+
+
+
+insert into TB_BASE_ACCOUNT (ID, DELETED, USERNAME, PASSWORD, ENABLED, ACCOUNT_NON_EXPIRED, ACCOUNT_NON_LOCKED, CREDENTIALS_NON_EXPIRED, NAME, STATUS) 
+values ('1', 0, 'swadmin', 'swadmin', 1, 1, 1, 1, 'Supwisdom Admin', '1');
+
+commit;
+
+
+insert into TB_BASE_GROUP (ID, DELETED, CODE, NAME, STATUS) 
+values ('1', 0, 'users', '所有用户', '1');
+
+commit;
+
+
+insert into TB_BASE_ROLE (ID, DELETED, CODE, NAME, STATUS) 
+values ('1', 0, 'administrator', '超级管理员', '1');
+insert into TB_BASE_ROLE (ID, DELETED, CODE, NAME, STATUS) 
+values ('2', 0, 'user', '普通用户', '1');
+
+commit;
+
+
+insert into TB_BASE_ACCOUNT_GROUP (ID, DELETED, ACCOUNT_ID, GROUP_ID) 
+values ('1_1', 0, '1', '1');
+
+commit;
+
+
+insert into TB_BASE_ACCOUNT_ROLE (ID, DELETED, ACCOUNT_ID, ROLE_ID) 
+values ('1_1', 0, '1', '1');
+
+insert into TB_BASE_ACCOUNT_ROLE (ID, DELETED, ACCOUNT_ID, ROLE_ID) 
+values ('1_2', 0, '1', '2');
+
+commit;
+
+
+insert into TB_BASE_GROUP_ROLE (ID, DELETED, GROUP_ID, ROLE_ID) 
+values ('1', 0, '1', '2');
+
+commit;
+
+
+
+insert into TB_BASE_PERMISSION (ID, DELETED, CODE, NAME, STATUS, TYPE_, URL, APPLICATION_ID, PARENT_ID, ORDER_, LEVEL_, LFT, RGT) 
+values ('1', 0, 'admin', '树维管理后台', '1', '1', '', '1', '0', 1, 1, 1, 18);
+
+insert into TB_BASE_PERMISSION (ID, DELETED, CODE, NAME, STATUS, TYPE_, URL, APPLICATION_ID, PARENT_ID, ORDER_, LEVEL_, LFT, RGT) 
+values ('10000', 0, 'admin.base', '基础管理', '1', '2', '', '1', '1', 10000, 1, 2, 15);
+
+insert into TB_BASE_PERMISSION (ID, DELETED, CODE, NAME, STATUS, TYPE_, URL, APPLICATION_ID, PARENT_ID, ORDER_, LEVEL_, LFT, RGT) 
+values ('10100', 0, 'admin.base.account', '帐号管理', '1', '2', '', '1', '10000', 10100, 2, 3, 4);
+insert into TB_BASE_PERMISSION (ID, DELETED, CODE, NAME, STATUS, TYPE_, URL, APPLICATION_ID, PARENT_ID, ORDER_, LEVEL_, LFT, RGT) 
+values ('10200', 0, 'admin.base.role', '角色管理', '1', '2', '', '1', '10000', 10200, 2, 5, 6);
+
+
+insert into TB_BASE_PERMISSION (ID, DELETED, CODE, NAME, STATUS, TYPE_, URL, APPLICATION_ID, PARENT_ID, ORDER_, LEVEL_, LFT, RGT) 
+values ('10400', 0, 'admin.base.menu', '菜单管理', '1', '2', '', '1', '10000', 10400, 2, 7, 8);
+insert into TB_BASE_PERMISSION (ID, DELETED, CODE, NAME, STATUS, TYPE_, URL, APPLICATION_ID, PARENT_ID, ORDER_, LEVEL_, LFT, RGT) 
+values ('10500', 0, 'admin.base.operation', '操作管理', '1', '2', '', '1', '10000', 10500, 2, 9, 10);
+insert into TB_BASE_PERMISSION (ID, DELETED, CODE, NAME, STATUS, TYPE_, URL, APPLICATION_ID, PARENT_ID, ORDER_, LEVEL_, LFT, RGT) 
+values ('10600', 0, 'admin.base.resource', '资源管理', '1', '2', '', '1', '10000', 10600, 2, 11, 12);
+
+
+insert into TB_BASE_PERMISSION (ID, DELETED, CODE, NAME, STATUS, TYPE_, URL, APPLICATION_ID, PARENT_ID, ORDER_, LEVEL_, LFT, RGT) 
+values ('10700', 0, 'admin.base.route', '路由管理', '1', '2', '', '1', '10000', 10700, 2, 13, 14);
+
+
+insert into TB_BASE_PERMISSION (ID, DELETED, CODE, NAME, STATUS, TYPE_, URL, APPLICATION_ID, PARENT_ID, ORDER_, LEVEL_, LFT, RGT) 
+values ('20000', 0, 'admin.system', '系统管理', '1', '2', '', '1', '1', 20000, 1, 16, 17);
+
+/*
+insert into TB_BASE_PERMISSION (ID, DELETED, CODE, NAME, STATUS, TYPE_, URL, APPLICATION_ID, PARENT_ID, ORDER_, LEVEL_, LFT, RGT) 
+values ('10300', 0, 'admin.base.application', '应用管理', '1', '2', '', '1', '10000', 10300, 2, 13, 14);
+*/
+
+/*
+insert into TB_BASE_PERMISSION (ID, DELETED, CODE, NAME, STATUS, TYPE_, URL, APPLICATION_ID, PARENT_ID, ORDER_, LEVEL_, LFT, RGT) 
+values ('10200', 0, 'admin.base.group', '用户组管理', '1', '2', '', '1', '10000', 10200, 2, 4, 5);
+*/
+
+commit;
+
+
+/** 超级管理员角色的权限 */
+insert into TB_BASE_ROLE_PERMISSION (ID, DELETED, ROLE_ID, PERMISSION_ID) 
+select CONCAT('1_', ID), 0, '1', ID from TB_BASE_PERMISSION;
+
+/** 普通用户角色的权限 */
+insert into TB_BASE_ROLE_PERMISSION (ID, DELETED, ROLE_ID, PERMISSION_ID) 
+values ('2_1', 0, '2', '1');
+
+commit;
+
+
+
+
+
+
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('1', 0, 'User.user', '获取登录信息', '1', 'GET', '/api/user', 'authenticate');
+
+
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10101', 0, 'BaseAdminAccount.query', '查询帐号', '1', 'GET', '/api/base/v1/admin/accounts', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10102', 0, 'BaseAdminAccount.load', '获取帐号', '1', 'GET', '/api/base/v1/admin/accounts/{id}', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10103', 0, 'BaseAdminAccount.create', '创建帐号', '1', 'POST', '/api/base/v1/admin/accounts', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10104', 0, 'BaseAdminAccount.update', '修改帐号', '1', 'PUT', '/api/base/v1/admin/accounts/{id}', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10105', 0, 'BaseAdminAccount.delete', '删除帐号', '1', 'DELETE', '/api/base/v1/admin/accounts/{id}', 'authorize');
+
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10106', 0, 'BaseAdminAccount.accountRoles', '获取帐号的角色', '1', 'GET', '/api/base/v1/admin/accounts/{id}/roles', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10107', 0, 'BaseAdminAccount.relateRoles', '关联帐号的角色', '1', 'POST', '/api/base/v1/admin/accounts/{id}/roles', 'authorize');
+
+
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10201', 0, 'BaseAdminRole.query', '查询角色', '1', 'GET', '/api/base/v1/admin/roles', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10202', 0, 'BaseAdminRole.load', '获取角色', '1', 'GET', '/api/base/v1/admin/roles/{id}', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10203', 0, 'BaseAdminRole.create', '创建角色', '1', 'POST', '/api/base/v1/admin/roles', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10204', 0, 'BaseAdminRole.update', '修改角色', '1', 'PUT', '/api/base/v1/admin/roles/{id}', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10205', 0, 'BaseAdminRole.delete', '删除角色', '1', 'DELETE', '/api/base/v1/admin/roles/{id}', 'authorize');
+
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10206', 0, 'BaseAdminRole.roleAccounts', '获取角色的帐号', '1', 'GET', '/api/base/v1/admin/roles/{id}/accounts', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10207', 0, 'BaseAdminRole.relateAccounts', '关联角色的帐号', '1', 'POST', '/api/base/v1/admin/roles/{id}/accounts', 'authorize');
+
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10208', 0, 'BaseAdminRole.rolePermissions', '获取角色的权限', '1', 'GET', '/api/base/v1/admin/roles/{id}/permissions', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10209', 0, 'BaseAdminRole.relatePermissions', '关联角色的权限', '1', 'POST', '/api/base/v1/admin/roles/{id}/permissions', 'authorize');
+
+/*
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10301', 0, 'BaseAdminApplication.query', '查询应用', '1', 'GET', '/api/base/v1/admin/applications', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10302', 0, 'BaseAdminApplication.load', '获取应用', '1', 'GET', '/api/base/v1/admin/applications/{id}', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10303', 0, 'BaseAdminApplication.create', '创建应用', '1', 'POST', '/api/base/v1/admin/applications', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10304', 0, 'BaseAdminApplication.update', '修改应用', '1', 'PUT', '/api/base/v1/admin/applications/{id}', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10305', 0, 'BaseAdminApplication.delete', '删除应用', '1', 'DELETE', '/api/base/v1/admin/applications/{id}', 'authorize');
+*/
+
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10401', 0, 'BaseAdminMenu.query', '查询菜单', '1', 'GET', '/api/base/v1/admin/menus', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10402', 0, 'BaseAdminMenu.load', '获取菜单', '1', 'GET', '/api/base/v1/admin/menus/{id}', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10403', 0, 'BaseAdminMenu.create', '创建菜单', '1', 'POST', '/api/base/v1/admin/menus', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10404', 0, 'BaseAdminMenu.update', '修改菜单', '1', 'PUT', '/api/base/v1/admin/menus/{id}', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10405', 0, 'BaseAdminMenu.delete', '删除菜单', '1', 'DELETE', '/api/base/v1/admin/menus/{id}', 'authorize');
+
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10406', 0, 'BaseAdminMenu.tree', '树形菜单', '1', 'GET', '/api/base/v1/admin/menus/tree', 'authorize');
+
+
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10501', 0, 'BaseAdminOperation.query', '查询操作', '1', 'GET', '/api/base/v1/admin/operations', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10502', 0, 'BaseAdminOperation.load', '获取操作', '1', 'GET', '/api/base/v1/admin/operations/{id}', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10503', 0, 'BaseAdminOperation.create', '创建操作', '1', 'POST', '/api/base/v1/admin/operations', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10504', 0, 'BaseAdminOperation.update', '修改操作', '1', 'PUT', '/api/base/v1/admin/operations/{id}', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10505', 0, 'BaseAdminOperation.delete', '删除操作', '1', 'DELETE', '/api/base/v1/admin/operations/{id}', 'authorize');
+
+
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10601', 0, 'BaseAdminResource.query', '查询资源', '1', 'GET', '/api/base/v1/admin/resources', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10602', 0, 'BaseAdminResource.load', '获取资源', '1', 'GET', '/api/base/v1/admin/resources/{id}', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10603', 0, 'BaseAdminResource.create', '创建资源', '1', 'POST', '/api/base/v1/admin/resources', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10604', 0, 'BaseAdminResource.update', '修改资源', '1', 'PUT', '/api/base/v1/admin/resources/{id}', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10605', 0, 'BaseAdminResource.delete', '删除资源', '1', 'DELETE', '/api/system/v1/admin/resources/{id}', 'authorize');
+
+
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10701', 0, 'BaseAdminRoute.query', '查询路由', '1', 'GET', '/api/base/v1/admin/routes', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10702', 0, 'BaseAdminRoute.load', '获取路由', '1', 'GET', '/api/base/v1/admin/routes/{id}', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10703', 0, 'BaseAdminRoute.create', '创建路由', '1', 'POST', '/api/base/v1/admin/routes', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10704', 0, 'BaseAdminRoute.update', '修改路由', '1', 'PUT', '/api/base/v1/admin/routes/{id}', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10705', 0, 'BaseAdminRoute.delete', '删除路由', '1', 'DELETE', '/api/system/v1/admin/routes/{id}', 'authorize');
+
+
+/*
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10111', 0, 'BaseAdminAccount.accountGroups', '获取帐号的用户组', '1', 'GET', '/api/base/v1/admin/accounts/{id}/groups', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10112', 0, 'BaseAdminAccount.relateGroups', '关联帐号的用户组', '1', 'POST', '/api/base/v1/admin/accounts/{id}/groups', 'authorize');
+
+
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10211', 0, 'BaseAdminRole.roleGroups', '获取角色的用户组', '1', 'GET', '/api/base/v1/admin/roles/{id}/groups', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10212', 0, 'BaseAdminRole.relateGroups', '关联角色的用户组', '1', 'POST', '/api/base/v1/admin/roles/{id}/groups', 'authorize');
+
+
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10201', 0, 'BaseAdminGroup.query', '查询用户组', '1', 'GET', '/api/base/v1/admin/groups', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10202', 0, 'BaseAdminGroup.load', '获取用户组', '1', 'GET', '/api/base/v1/admin/groups/{id}', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10203', 0, 'BaseAdminGroup.create', '创建用户组', '1', 'POST', '/api/base/v1/admin/groups', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10204', 0, 'BaseAdminGroup.update', '修改用户组', '1', 'PUT', '/api/base/v1/admin/groups/{id}', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10205', 0, 'BaseAdminGroup.delete', '删除用户组', '1', 'DELETE', '/api/base/v1/admin/groups/{id}', 'authorize');
+
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10206', 0, 'BaseAdminGroup.groupAccounts', '获取用户组的帐号', '1', 'GET', '/api/base/v1/admin/groups/{id}/accounts', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10207', 0, 'BaseAdminGroup.relateAccounts', '关联用户组的帐号', '1', 'POST', '/api/base/v1/admin/groups/{id}/accounts', 'authorize');
+
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10208', 0, 'BaseAdminGroup.groupRoles', '获取用户组的角色', '1', 'GET', '/api/base/v1/admin/groups/{id}/roles', 'authorize');
+insert into TB_BASE_RESOURCE (ID, DELETED, CODE, NAME, STATUS, METHOD, PATH, ACCESS)
+values ('10209', 0, 'BaseAdminGroup.relateRoles', '关联用户组的角色', '1', 'POST', '/api/base/v1/admin/groups/{id}/roles', 'authorize');
+*/
+
+commit;
+
+
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10100_10101', 0, '10100', '10101');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10100_10102', 0, '10100', '10102');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10100_10103', 0, '10100', '10103');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10100_10104', 0, '10100', '10104');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10100_10105', 0, '10100', '10105');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10100_10106', 0, '10100', '10106');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10100_10107', 0, '10100', '10107');
+
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10200_10201', 0, '10200', '10201');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10200_10202', 0, '10200', '10202');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10200_10203', 0, '10200', '10203');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10200_10204', 0, '10200', '10204');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10200_10205', 0, '10200', '10205');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10200_10206', 0, '10200', '10206');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10200_10207', 0, '10200', '10207');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10200_10208', 0, '10200', '10208');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10200_10209', 0, '10200', '10209');
+
+/*
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10300_10301', 0, '10300', '10301');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10300_10302', 0, '10300', '10302');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10300_10303', 0, '10300', '10303');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10300_10304', 0, '10300', '10304');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10300_10305', 0, '10300', '10305');
+*/
+
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10400_10401', 0, '10400', '10401');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10400_10402', 0, '10400', '10402');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10400_10403', 0, '10400', '10403');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10400_10404', 0, '10400', '10404');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10400_10405', 0, '10400', '10405');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10400_10406', 0, '10400', '10406');
+
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10500_10501', 0, '10500', '10501');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10500_10502', 0, '10500', '10502');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10500_10503', 0, '10500', '10503');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10500_10504', 0, '10500', '10504');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10500_10505', 0, '10500', '10505');
+
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10600_10601', 0, '10600', '10601');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10600_10602', 0, '10600', '10602');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10600_10603', 0, '10600', '10603');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10600_10604', 0, '10600', '10604');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10600_10605', 0, '10600', '10605');
+
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10700_10701', 0, '10700', '10701');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10700_10702', 0, '10700', '10702');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10700_10703', 0, '10700', '10703');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10700_10704', 0, '10700', '10704');
+insert into TB_BASE_PERMISSION_RESOURCE (ID, DELETED, PERMISSION_ID, RESOURCE_ID)
+values ('10700_10705', 0, '10700', '10705');
+
+commit;
diff --git a/sql/biz.sql b/sql/biz.sql
new file mode 100644
index 0000000..7e8b585
--- /dev/null
+++ b/sql/biz.sql
@@ -0,0 +1,20 @@
+
+CREATE TABLE `TB_BIZ` (
+  `ID` VARCHAR(100) NOT NULL COMMENT '',
+  `COMPANY_ID` VARCHAR(100) COMMENT 'CompanyID',
+  `DELETED` INT(1) COMMENT '是否删除',
+  `ADD_ACCOUNT` VARCHAR(100) COMMENT '创建人',
+  `ADD_TIME` DATETIME COMMENT '创建时间',
+  `EDIT_ACCOUNT` VARCHAR(100) COMMENT '修改人',
+  `EDIT_TIME` DATETIME COMMENT '修改时间',
+  `DELETE_ACCOUNT` VARCHAR(100) COMMENT '删除人',
+  `DELETE_TIME` DATETIME COMMENT '删除时间',
+
+  `NAME` VARCHAR(200) NOT NULL COMMENT '名称',
+  `BOOL` INT(1) NOT NULL COMMENT '布尔',
+  `DATE` DATETIME COMMENT '时间',
+  `NUM` INT(11) NOT NULL COMMENT '数字',
+
+  PRIMARY KEY (`ID`)
+)
+COMMENT = '测试表';
diff --git a/sql/system.sql b/sql/system.sql
new file mode 100644
index 0000000..b12d034
--- /dev/null
+++ b/sql/system.sql
@@ -0,0 +1 @@
+/* system.sql */