refactor: 重命名项目目录,system -> base
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..1935e28
--- /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-SNAPSHOT</version>
+    <relativePath>../../</relativePath>
+  </parent>
+
+  <groupId>com.supwisdom.institute</groupId>
+  <artifactId>sw-backend-base-api</artifactId>
+  <version>0.0.1-SNAPSHOT</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/admin/AdminAccountController.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/admin/AdminAccountController.java
new file mode 100644
index 0000000..646445d
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/admin/AdminAccountController.java
@@ -0,0 +1,583 @@
+package com.supwisdom.institute.backend.base.api.v1.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.vo.request.AccountCreateRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.AccountDeleteBatchRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.AccountQueryRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.AccountRelateGroupsRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.AccountRelateRolesRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.AccountRelatedGroupsRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.AccountRelatedRolesRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.AccountUpdateRequest;
+import com.supwisdom.institute.backend.base.api.vo.response.AccountCreateResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.AccountDeleteBatchResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.AccountLoadResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.AccountQueryResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.AccountRelateGroupsResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.AccountRelateRolesResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.AccountRelatedGroupsResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.AccountRelatedRolesResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.AccountRemoveResponseData;
+import com.supwisdom.institute.backend.base.api.vo.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/admin/AdminConfigController.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/admin/AdminConfigController.java
new file mode 100644
index 0000000..15b3bda
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/admin/AdminConfigController.java
@@ -0,0 +1,198 @@
+package com.supwisdom.institute.backend.base.api.v1.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.vo.request.ConfigCreateRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.ConfigQueryRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.ConfigUpdateRequest;
+import com.supwisdom.institute.backend.base.api.vo.response.ConfigCreateResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.ConfigLoadResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.ConfigQueryResponseData;
+import com.supwisdom.institute.backend.base.api.vo.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/admin/AdminGroupController.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/admin/AdminGroupController.java
new file mode 100644
index 0000000..3dc572d
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/admin/AdminGroupController.java
@@ -0,0 +1,270 @@
+package com.supwisdom.institute.backend.base.api.v1.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.vo.request.GroupCreateRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.GroupQueryRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.GroupRelateAccountsRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.GroupRelateRolesRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.GroupRelatedAccountsRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.GroupRelatedRolesRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.GroupUpdateRequest;
+import com.supwisdom.institute.backend.base.api.vo.response.GroupCreateResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.GroupLoadResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.GroupQueryResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.GroupRelateAccountsResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.GroupRelateRolesResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.GroupRelatedAccountsResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.GroupRelatedRolesResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.GroupRemoveResponseData;
+import com.supwisdom.institute.backend.base.api.vo.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/admin/AdminMenuController.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/admin/AdminMenuController.java
new file mode 100644
index 0000000..9526d94
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/admin/AdminMenuController.java
@@ -0,0 +1,199 @@
+package com.supwisdom.institute.backend.base.api.v1.admin;
+
+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.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.supwisdom.institute.backend.base.api.vo.request.PermissionCreateRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.PermissionDeleteBatchRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.PermissionQueryRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.PermissionUpdateRequest;
+import com.supwisdom.institute.backend.base.api.vo.response.PermissionCreateResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.PermissionDeleteBatchResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.PermissionLoadResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.PermissionQueryResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.PermissionRemoveResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.PermissionTreeResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.PermissionUpdateResponseData;
+import com.supwisdom.institute.backend.base.domain.entity.Permission;
+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");
+    }
+    
+    PermissionLoadResponseData data = PermissionLoadResponseData.of(permission);
+
+    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() {
+    
+    Map<String, Object> mapBean = new HashMap<String, Object>();
+    mapBean.put("type", Permission.TYPE_MENU);
+
+    PermissionTreeNode tree = permissionService.selectPermissionTree(mapBean);
+
+    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/admin/AdminOperationController.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/admin/AdminOperationController.java
new file mode 100644
index 0000000..69c57d3
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/admin/AdminOperationController.java
@@ -0,0 +1,164 @@
+package com.supwisdom.institute.backend.base.api.v1.admin;
+
+import io.swagger.annotations.Api;
+
+import java.util.HashMap;
+
+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.vo.request.PermissionCreateRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.PermissionQueryRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.PermissionUpdateRequest;
+import com.supwisdom.institute.backend.base.api.vo.response.PermissionCreateResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.PermissionLoadResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.PermissionQueryResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.PermissionRemoveResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.PermissionUpdateResponseData;
+import com.supwisdom.institute.backend.base.domain.entity.Permission;
+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");
+    }
+    
+    PermissionLoadResponseData data = PermissionLoadResponseData.of(permission);
+
+    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/admin/AdminPermissionController.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/admin/AdminPermissionController.java
new file mode 100644
index 0000000..7859731
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/admin/AdminPermissionController.java
@@ -0,0 +1,5 @@
+package com.supwisdom.institute.backend.base.api.v1.admin;
+
+public class AdminPermissionController {
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/admin/AdminResourceController.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/admin/AdminResourceController.java
new file mode 100644
index 0000000..67686d6
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/admin/AdminResourceController.java
@@ -0,0 +1,146 @@
+package com.supwisdom.institute.backend.base.api.v1.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.vo.request.ResourceCreateRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.ResourceQueryRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.ResourceUpdateRequest;
+import com.supwisdom.institute.backend.base.api.vo.response.ResourceCreateResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.ResourceLoadResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.ResourceQueryResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.ResourceRemoveResponseData;
+import com.supwisdom.institute.backend.base.api.vo.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/admin/AdminRoleController.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/admin/AdminRoleController.java
new file mode 100644
index 0000000..448384b
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/v1/admin/AdminRoleController.java
@@ -0,0 +1,346 @@
+package com.supwisdom.institute.backend.base.api.v1.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.vo.request.RoleCreateRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.RoleDeleteBatchRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.RoleQueryRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.RoleRelateAccountsRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.RoleRelateGroupsRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.RoleRelatePermissionsRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.RoleRelatedAccountsRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.RoleRelatedGroupsRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.RoleRelatedPermissionsRequest;
+import com.supwisdom.institute.backend.base.api.vo.request.RoleUpdateRequest;
+import com.supwisdom.institute.backend.base.api.vo.response.RoleCreateResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.RoleDeleteBatchResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.RoleLoadResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.RoleQueryResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.RoleRelateAccountsResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.RoleRelateGroupsResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.RoleRelatePermissionsResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.RoleRelatedAccountsResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.RoleRelatedGroupsResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.RoleRelatedPermissionsResponseData;
+import com.supwisdom.institute.backend.base.api.vo.response.RoleRemoveResponseData;
+import com.supwisdom.institute.backend.base.api.vo.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/vo/.gitkeep b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/.gitkeep
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/AccountCreateRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/AccountCreateRequest.java
new file mode 100644
index 0000000..ce6bf94
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/AccountCreateRequest.java
@@ -0,0 +1,21 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/request/AccountDeleteBatchRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/AccountDeleteBatchRequest.java
new file mode 100644
index 0000000..27ff27a
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/AccountDeleteBatchRequest.java
@@ -0,0 +1,21 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/request/AccountQueryRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/AccountQueryRequest.java
new file mode 100644
index 0000000..fdaf3a3
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/AccountQueryRequest.java
@@ -0,0 +1,40 @@
+package com.supwisdom.institute.backend.base.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 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/vo/request/AccountRelateGroupsRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/AccountRelateGroupsRequest.java
new file mode 100644
index 0000000..51f542a
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/AccountRelateGroupsRequest.java
@@ -0,0 +1,22 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/request/AccountRelateRolesRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/AccountRelateRolesRequest.java
new file mode 100644
index 0000000..235cf22
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/AccountRelateRolesRequest.java
@@ -0,0 +1,22 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/request/AccountRelatedGroupsRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/AccountRelatedGroupsRequest.java
new file mode 100644
index 0000000..362c5b6
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/AccountRelatedGroupsRequest.java
@@ -0,0 +1,40 @@
+package com.supwisdom.institute.backend.base.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 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/vo/request/AccountRelatedRolesRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/AccountRelatedRolesRequest.java
new file mode 100644
index 0000000..0fc9e07
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/AccountRelatedRolesRequest.java
@@ -0,0 +1,40 @@
+package com.supwisdom.institute.backend.base.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 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/vo/request/AccountUpdateRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/AccountUpdateRequest.java
new file mode 100644
index 0000000..bb7a089
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/AccountUpdateRequest.java
@@ -0,0 +1,28 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/request/ConfigCreateRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/ConfigCreateRequest.java
new file mode 100644
index 0000000..f175704
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/ConfigCreateRequest.java
@@ -0,0 +1,21 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/request/ConfigQueryRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/ConfigQueryRequest.java
new file mode 100644
index 0000000..0449b6d
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/ConfigQueryRequest.java
@@ -0,0 +1,40 @@
+package com.supwisdom.institute.backend.base.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 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/vo/request/ConfigUpdateRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/ConfigUpdateRequest.java
new file mode 100644
index 0000000..a69ebb4
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/ConfigUpdateRequest.java
@@ -0,0 +1,29 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/request/GroupCreateRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/GroupCreateRequest.java
new file mode 100644
index 0000000..dd06d2e
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/GroupCreateRequest.java
@@ -0,0 +1,21 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/request/GroupQueryRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/GroupQueryRequest.java
new file mode 100644
index 0000000..6cf4017
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/GroupQueryRequest.java
@@ -0,0 +1,40 @@
+package com.supwisdom.institute.backend.base.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 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/vo/request/GroupRelateAccountsRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/GroupRelateAccountsRequest.java
new file mode 100644
index 0000000..3251273
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/GroupRelateAccountsRequest.java
@@ -0,0 +1,22 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/request/GroupRelateRolesRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/GroupRelateRolesRequest.java
new file mode 100644
index 0000000..5323c0c
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/GroupRelateRolesRequest.java
@@ -0,0 +1,22 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/request/GroupRelatedAccountsRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/GroupRelatedAccountsRequest.java
new file mode 100644
index 0000000..fd4b708
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/GroupRelatedAccountsRequest.java
@@ -0,0 +1,40 @@
+package com.supwisdom.institute.backend.base.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 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/vo/request/GroupRelatedRolesRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/GroupRelatedRolesRequest.java
new file mode 100644
index 0000000..2ec6d00
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/GroupRelatedRolesRequest.java
@@ -0,0 +1,40 @@
+package com.supwisdom.institute.backend.base.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 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/vo/request/GroupUpdateRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/GroupUpdateRequest.java
new file mode 100644
index 0000000..da9322a
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/GroupUpdateRequest.java
@@ -0,0 +1,28 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/request/PermissionCreateRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/PermissionCreateRequest.java
new file mode 100644
index 0000000..0fc549d
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/PermissionCreateRequest.java
@@ -0,0 +1,21 @@
+package com.supwisdom.institute.backend.base.api.vo.request;
+
+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;
+
+  public Permission getEntity() {
+    return EntityUtils.copy(this, new Permission());
+  }
+  
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/PermissionDeleteBatchRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/PermissionDeleteBatchRequest.java
new file mode 100644
index 0000000..99663ba
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/PermissionDeleteBatchRequest.java
@@ -0,0 +1,21 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/request/PermissionQueryRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/PermissionQueryRequest.java
new file mode 100644
index 0000000..ed66913
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/PermissionQueryRequest.java
@@ -0,0 +1,40 @@
+package com.supwisdom.institute.backend.base.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 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/vo/request/PermissionUpdateRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/PermissionUpdateRequest.java
new file mode 100644
index 0000000..896b901
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/PermissionUpdateRequest.java
@@ -0,0 +1,28 @@
+package com.supwisdom.institute.backend.base.api.vo.request;
+
+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;
+
+  public Permission getEntity() {
+    return EntityUtils.copy(this, new Permission());
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/ResourceCreateRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/ResourceCreateRequest.java
new file mode 100644
index 0000000..74ac0c5
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/ResourceCreateRequest.java
@@ -0,0 +1,21 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/request/ResourceQueryRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/ResourceQueryRequest.java
new file mode 100644
index 0000000..cc73efb
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/ResourceQueryRequest.java
@@ -0,0 +1,40 @@
+package com.supwisdom.institute.backend.base.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 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/vo/request/ResourceUpdateRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/ResourceUpdateRequest.java
new file mode 100644
index 0000000..13f7bf7
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/ResourceUpdateRequest.java
@@ -0,0 +1,28 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/request/RoleCreateRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/RoleCreateRequest.java
new file mode 100644
index 0000000..467dcb1
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/RoleCreateRequest.java
@@ -0,0 +1,21 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/request/RoleDeleteBatchRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/RoleDeleteBatchRequest.java
new file mode 100644
index 0000000..ae6edc8
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/RoleDeleteBatchRequest.java
@@ -0,0 +1,21 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/request/RoleQueryRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/RoleQueryRequest.java
new file mode 100644
index 0000000..d247f24
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/RoleQueryRequest.java
@@ -0,0 +1,40 @@
+package com.supwisdom.institute.backend.base.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 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/vo/request/RoleRelateAccountsRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/RoleRelateAccountsRequest.java
new file mode 100644
index 0000000..214caea
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/RoleRelateAccountsRequest.java
@@ -0,0 +1,22 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/request/RoleRelateGroupsRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/RoleRelateGroupsRequest.java
new file mode 100644
index 0000000..0bdff1a
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/RoleRelateGroupsRequest.java
@@ -0,0 +1,22 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/request/RoleRelatePermissionsRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/RoleRelatePermissionsRequest.java
new file mode 100644
index 0000000..f00d520
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/RoleRelatePermissionsRequest.java
@@ -0,0 +1,22 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/request/RoleRelatedAccountsRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/RoleRelatedAccountsRequest.java
new file mode 100644
index 0000000..cc1c505
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/RoleRelatedAccountsRequest.java
@@ -0,0 +1,40 @@
+package com.supwisdom.institute.backend.base.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 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/vo/request/RoleRelatedGroupsRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/RoleRelatedGroupsRequest.java
new file mode 100644
index 0000000..88c0ac8
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/RoleRelatedGroupsRequest.java
@@ -0,0 +1,40 @@
+package com.supwisdom.institute.backend.base.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 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/vo/request/RoleRelatedPermissionsRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/RoleRelatedPermissionsRequest.java
new file mode 100644
index 0000000..12eaf23
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/RoleRelatedPermissionsRequest.java
@@ -0,0 +1,40 @@
+package com.supwisdom.institute.backend.base.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 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/vo/request/RoleUpdateRequest.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/RoleUpdateRequest.java
new file mode 100644
index 0000000..c3cbe19
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/request/RoleUpdateRequest.java
@@ -0,0 +1,28 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/AccountCreateResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/AccountCreateResponseData.java
new file mode 100644
index 0000000..c5cabb4
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/AccountCreateResponseData.java
@@ -0,0 +1,33 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/AccountDeleteBatchResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/AccountDeleteBatchResponseData.java
new file mode 100644
index 0000000..6726752
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/AccountDeleteBatchResponseData.java
@@ -0,0 +1,35 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/AccountLoadResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/AccountLoadResponseData.java
new file mode 100644
index 0000000..184d935
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/AccountLoadResponseData.java
@@ -0,0 +1,33 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/AccountQueryResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/AccountQueryResponseData.java
new file mode 100644
index 0000000..cf4c59a
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/AccountQueryResponseData.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.base.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.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/vo/response/AccountRelateGroupsResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/AccountRelateGroupsResponseData.java
new file mode 100644
index 0000000..2baafb9
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/AccountRelateGroupsResponseData.java
@@ -0,0 +1,29 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/AccountRelateRolesResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/AccountRelateRolesResponseData.java
new file mode 100644
index 0000000..5c7a252
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/AccountRelateRolesResponseData.java
@@ -0,0 +1,29 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/AccountRelatedGroupsResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/AccountRelatedGroupsResponseData.java
new file mode 100644
index 0000000..54db3e9
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/AccountRelatedGroupsResponseData.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.base.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.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/vo/response/AccountRelatedRolesResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/AccountRelatedRolesResponseData.java
new file mode 100644
index 0000000..96fff82
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/AccountRelatedRolesResponseData.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.base.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.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/vo/response/AccountRemoveResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/AccountRemoveResponseData.java
new file mode 100644
index 0000000..718df5d
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/AccountRemoveResponseData.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/AccountUpdateResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/AccountUpdateResponseData.java
new file mode 100644
index 0000000..43d9098
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/AccountUpdateResponseData.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/ConfigCreateResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/ConfigCreateResponseData.java
new file mode 100644
index 0000000..c29d112
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/ConfigCreateResponseData.java
@@ -0,0 +1,32 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/ConfigLoadResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/ConfigLoadResponseData.java
new file mode 100644
index 0000000..4753b1d
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/ConfigLoadResponseData.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/ConfigQueryResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/ConfigQueryResponseData.java
new file mode 100644
index 0000000..f27c1cd
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/ConfigQueryResponseData.java
@@ -0,0 +1,83 @@
+package com.supwisdom.institute.backend.base.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.base.api.vo.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/vo/response/ConfigRemoveResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/ConfigRemoveResponseData.java
new file mode 100644
index 0000000..fc483de
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/ConfigRemoveResponseData.java
@@ -0,0 +1,33 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/ConfigUpdateResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/ConfigUpdateResponseData.java
new file mode 100644
index 0000000..ae27d68
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/ConfigUpdateResponseData.java
@@ -0,0 +1,33 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/GroupCreateResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/GroupCreateResponseData.java
new file mode 100644
index 0000000..96c8149
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/GroupCreateResponseData.java
@@ -0,0 +1,33 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/GroupLoadResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/GroupLoadResponseData.java
new file mode 100644
index 0000000..b1e6f47
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/GroupLoadResponseData.java
@@ -0,0 +1,33 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/GroupQueryResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/GroupQueryResponseData.java
new file mode 100644
index 0000000..fa702a3
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/GroupQueryResponseData.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.base.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.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/vo/response/GroupRelateAccountsResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/GroupRelateAccountsResponseData.java
new file mode 100644
index 0000000..61d95e8
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/GroupRelateAccountsResponseData.java
@@ -0,0 +1,29 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/GroupRelateRolesResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/GroupRelateRolesResponseData.java
new file mode 100644
index 0000000..17c65f7
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/GroupRelateRolesResponseData.java
@@ -0,0 +1,29 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/GroupRelatedAccountsResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/GroupRelatedAccountsResponseData.java
new file mode 100644
index 0000000..0d37091
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/GroupRelatedAccountsResponseData.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.base.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.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/vo/response/GroupRelatedRolesResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/GroupRelatedRolesResponseData.java
new file mode 100644
index 0000000..c5ee184
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/GroupRelatedRolesResponseData.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.base.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.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/vo/response/GroupRemoveResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/GroupRemoveResponseData.java
new file mode 100644
index 0000000..0fb7409
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/GroupRemoveResponseData.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/GroupUpdateResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/GroupUpdateResponseData.java
new file mode 100644
index 0000000..bb9df8b
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/GroupUpdateResponseData.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/PermissionCreateResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/PermissionCreateResponseData.java
new file mode 100644
index 0000000..94cf9db
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/PermissionCreateResponseData.java
@@ -0,0 +1,33 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/PermissionDeleteBatchResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/PermissionDeleteBatchResponseData.java
new file mode 100644
index 0000000..141afb4
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/PermissionDeleteBatchResponseData.java
@@ -0,0 +1,35 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/PermissionLoadResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/PermissionLoadResponseData.java
new file mode 100644
index 0000000..422b88e
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/PermissionLoadResponseData.java
@@ -0,0 +1,33 @@
+package com.supwisdom.institute.backend.base.api.vo.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.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;
+
+  private PermissionLoadResponseData() {
+
+  }
+
+  public static PermissionLoadResponseData of(Permission entity) {
+    PermissionLoadResponseData data = new PermissionLoadResponseData();
+    return EntityUtils.copy(entity, data);
+  }
+
+}
diff --git a/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/PermissionQueryResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/PermissionQueryResponseData.java
new file mode 100644
index 0000000..bb2c6b3
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/PermissionQueryResponseData.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.base.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.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/vo/response/PermissionRemoveResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/PermissionRemoveResponseData.java
new file mode 100644
index 0000000..b2179dd
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/PermissionRemoveResponseData.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/PermissionTreeResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/PermissionTreeResponseData.java
new file mode 100644
index 0000000..5eeb263
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/PermissionTreeResponseData.java
@@ -0,0 +1,31 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/PermissionUpdateResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/PermissionUpdateResponseData.java
new file mode 100644
index 0000000..28c3a70
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/PermissionUpdateResponseData.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/ResourceCreateResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/ResourceCreateResponseData.java
new file mode 100644
index 0000000..ba61a45
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/ResourceCreateResponseData.java
@@ -0,0 +1,33 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/ResourceLoadResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/ResourceLoadResponseData.java
new file mode 100644
index 0000000..9db0971
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/ResourceLoadResponseData.java
@@ -0,0 +1,33 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/ResourceQueryResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/ResourceQueryResponseData.java
new file mode 100644
index 0000000..a87f048
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/ResourceQueryResponseData.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.base.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.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/vo/response/ResourceRemoveResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/ResourceRemoveResponseData.java
new file mode 100644
index 0000000..9ff9637
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/ResourceRemoveResponseData.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/ResourceUpdateResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/ResourceUpdateResponseData.java
new file mode 100644
index 0000000..95592b1
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/ResourceUpdateResponseData.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/RoleCreateResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/RoleCreateResponseData.java
new file mode 100644
index 0000000..9bde1cf
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/RoleCreateResponseData.java
@@ -0,0 +1,33 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/RoleDeleteBatchResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/RoleDeleteBatchResponseData.java
new file mode 100644
index 0000000..ec520a4
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/RoleDeleteBatchResponseData.java
@@ -0,0 +1,35 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/RoleLoadResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/RoleLoadResponseData.java
new file mode 100644
index 0000000..c311285
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/RoleLoadResponseData.java
@@ -0,0 +1,33 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/RoleQueryResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/RoleQueryResponseData.java
new file mode 100644
index 0000000..18e9c22
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/RoleQueryResponseData.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.base.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.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/vo/response/RoleRelateAccountsResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/RoleRelateAccountsResponseData.java
new file mode 100644
index 0000000..a91bad9
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/RoleRelateAccountsResponseData.java
@@ -0,0 +1,29 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/RoleRelateGroupsResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/RoleRelateGroupsResponseData.java
new file mode 100644
index 0000000..995b48f
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/RoleRelateGroupsResponseData.java
@@ -0,0 +1,29 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/RoleRelatePermissionsResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/RoleRelatePermissionsResponseData.java
new file mode 100644
index 0000000..c9a34b0
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/RoleRelatePermissionsResponseData.java
@@ -0,0 +1,29 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/RoleRelatedAccountsResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/RoleRelatedAccountsResponseData.java
new file mode 100644
index 0000000..385ff3e
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/RoleRelatedAccountsResponseData.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.base.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.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/vo/response/RoleRelatedGroupsResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/RoleRelatedGroupsResponseData.java
new file mode 100644
index 0000000..f32a2f6
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/RoleRelatedGroupsResponseData.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.base.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.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/vo/response/RoleRelatedPermissionsResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/RoleRelatedPermissionsResponseData.java
new file mode 100644
index 0000000..35bcb2b
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/RoleRelatedPermissionsResponseData.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.base.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.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/vo/response/RoleRemoveResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/RoleRemoveResponseData.java
new file mode 100644
index 0000000..5ef2057
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/RoleRemoveResponseData.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.base.api.vo.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/vo/response/RoleUpdateResponseData.java b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/RoleUpdateResponseData.java
new file mode 100644
index 0000000..0c96f46
--- /dev/null
+++ b/base/api/src/main/java/com/supwisdom/institute/backend/base/api/vo/response/RoleUpdateResponseData.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.base.api.vo.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/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..26580ba
--- /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-SNAPSHOT</version>
+    <relativePath>../../</relativePath>
+  </parent>
+
+  <groupId>com.supwisdom.institute</groupId>
+  <artifactId>sw-backend-base-domain</artifactId>
+  <version>0.0.1-SNAPSHOT</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..9e05174
--- /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_U_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..318d373
--- /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_U_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..4a834b0
--- /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_U_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/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..87eaf2e
--- /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_U_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..be96211
--- /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_U_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..0d7dbd0
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/Permission.java
@@ -0,0 +1,177 @@
+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_U_PERMISSION")
+public class Permission extends ABaseEntity {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -8834200833972243635L;
+  
+  public static final String ROOT_PARENT_ID = "0";
+
+  public static final String TYPE_MENU = "2";
+  public static final String TYPE_OPERATION = "3";
+
+  /**
+   * 代码
+   */
+  @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;
+
+  /**
+   * 类型(1 应用,2 菜单,3 操作)
+   */
+  @Column(name = "TYPE_")
+  private String type;
+
+  /**
+   * URL地址
+   */
+  @Column(name = "URL", nullable = true)
+  private String url;
+
+  /**
+   * 父级ID
+   */
+  @Column(name = "PARENT_ID")
+  private String parentId;
+
+  /**
+   * 排序
+   */
+  @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 getUrl() {
+    return url;
+  }
+
+  public void setUrl(String url) {
+    this.url = url;
+  }
+
+  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/Resource.java b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/Resource.java
new file mode 100644
index 0000000..7b3a08c
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/entity/Resource.java
@@ -0,0 +1,61 @@
+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_U_RESOURCE")
+public class Resource extends ABaseEntity {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 4288268877209267453L;
+
+  /**
+   * 代码
+   */
+  @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;
+
+  /**
+   * URL地址
+   */
+  @Getter
+  @Setter
+  @Column(name = "URL", nullable = true)
+  private String url;
+
+}
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..0714133
--- /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_U_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..df8a031
--- /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_U_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/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/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/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/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..9db0e26
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/GroupRepository.java
@@ -0,0 +1,49 @@
+package com.supwisdom.institute.backend.base.domain.repo;
+
+import java.util.Map;
+
+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.common.framework.repo.BaseJpaRepository;
+import com.supwisdom.institute.backend.common.util.MapBeanUtils;
+
+@Repository
+public interface GroupRepository extends BaseJpaRepository<Group> {
+
+  @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..e7e7940
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/PermissionRepository.java
@@ -0,0 +1,409 @@
+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 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 = 9071470982419099273L;
+
+      @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, "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("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<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_U_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_U_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("0");
+    }
+
+    if (entity.getParentId() == null || entity.getParentId().isEmpty() || "0".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("0");
+    }
+    
+    if (entity.getParentId() == null) {
+      entity.setParentId("0");
+    }
+    
+    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() || "0".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 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 = this.findOne(example);
+    
+    if (o.isPresent()) {
+      return o.get();
+    }
+    
+    return null;
+  }
+  
+ 
+
+  @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);
+
+  public default List<Permission> selectByUsername(String username, String applicationCode, String type) {
+    List<Permission> permissions = new ArrayList<Permission>();
+    
+    Permission applicationPermission = selectApplicationPermissionByCode(applicationCode);
+    if (applicationPermission == null) {
+      return permissions;
+    }
+    
+    int lft = applicationPermission.getLft();
+    int rgt = applicationPermission.getRgt();
+    
+    List<Permission> accountRolePermissions = selectAccountRolePermissionByUsername(username, lft, rgt, type);
+    permissions.addAll(accountRolePermissions);
+    
+    List<Permission> accountGroupRolePermissions = selectAccountGroupRolePermissionByUsername(username, lft, rgt, type);
+    permissions.addAll(accountGroupRolePermissions);
+    
+    return permissions;
+  }
+
+
+
+
+  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);
+    }
+  }
+
+
+}
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..ae9055d
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/ResourceRepository.java
@@ -0,0 +1,68 @@
+package com.supwisdom.institute.backend.base.domain.repo;
+
+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.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 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..b416a7e
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/repo/RoleRepository.java
@@ -0,0 +1,101 @@
+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 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.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+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 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);
+
+  public default List<Role> selectByUsername(String username) {
+    List<Role> roles = new ArrayList<Role>();
+    
+    List<Role> userRoles = selectAccountRoleByUsername(username);
+    roles.addAll(userRoles);
+    
+    List<Role> userGroupRoles = selectAccountGroupRoleByUsername(username);
+    roles.addAll(userGroupRoles);
+    
+    return roles;
+  }
+  
+}
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..ece1cda
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/AccountService.java
@@ -0,0 +1,199 @@
+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;
+  }
+
+}
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..f8b4f11
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/GroupService.java
@@ -0,0 +1,175 @@
+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 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..f2cfe4b
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/PermissionService.java
@@ -0,0 +1,71 @@
+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.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.supwisdom.institute.backend.base.domain.entity.Permission;
+import com.supwisdom.institute.backend.base.domain.model.PermissionTreeNode;
+import com.supwisdom.institute.backend.base.domain.repo.PermissionRepository;
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.service.ABaseService;
+
+@Slf4j
+@Service
+public class PermissionService extends ABaseService<Permission, PermissionRepository> {
+  
+  @Override
+  public PermissionRepository getRepo() {
+    return permissionRepository;
+  }
+
+  @Autowired
+  private PermissionRepository permissionRepository;
+
+  public void deleteBatch(List<String> ids) {
+    
+    ids.stream().forEach(id -> {
+      this.deleteById(id);
+    });
+  }
+
+  public PermissionTreeNode selectPermissionTree(Map<String, Object> mapBean) {
+    
+    List<Permission> permissions = permissionRepository.selectList(true, -1, -1, mapBean, null);
+    
+    Map<String, PermissionTreeNode> parentTreeNode = new LinkedHashMap<String, PermissionTreeNode>();
+    
+    PermissionTreeNode rootTreeNode = new PermissionTreeNode();
+    rootTreeNode.setId(Permission.ROOT_PARENT_ID);
+    rootTreeNode.setCode("ROOT");
+    rootTreeNode.setName("顶级菜单");
+    rootTreeNode.setChildren(new ArrayList<PermissionTreeNode>());
+    
+    parentTreeNode.put(rootTreeNode.getId(), rootTreeNode);
+    
+    for (Permission permission : permissions) {
+      PermissionTreeNode treeNode = EntityUtils.copy(permission, new PermissionTreeNode());
+      treeNode.setChildren(new ArrayList<PermissionTreeNode>());
+      
+      parentTreeNode.put(treeNode.getId(), treeNode);
+    }
+    
+    for (PermissionTreeNode treeNode : parentTreeNode.values()) {
+      if (!parentTreeNode.containsKey(treeNode.getParentId())) {
+        continue;
+      }
+      
+      parentTreeNode.get(treeNode.getParentId()).getChildren().add(treeNode);
+    }
+    
+    return rootTreeNode;
+  }
+
+}
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..075fa74
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/ResourceService.java
@@ -0,0 +1,21 @@
+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.Resource;
+import com.supwisdom.institute.backend.base.domain.repo.ResourceRepository;
+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;
+
+}
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..1239d2f
--- /dev/null
+++ b/base/domain/src/main/java/com/supwisdom/institute/backend/base/domain/service/RoleService.java
@@ -0,0 +1,249 @@
+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.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.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;
+  }
+
+}
diff --git a/base/pom.xml b/base/pom.xml
new file mode 100644
index 0000000..2d60e6f
--- /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-SNAPSHOT</version>
+  </parent>
+  
+  <groupId>com.supwisdom.institute</groupId>
+  <artifactId>sw-backend-base-aggregator</artifactId>
+  <version>0.0.1-SNAPSHOT</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>