deason 5 年之前
當前提交
8c6e1cee00
共有 100 個文件被更改,包括 12221 次插入0 次删除
  1. 12 0
      .gitignore
  2. 3 0
      README.md
  3. 26 0
      examcloud-core-basic-api-provider/pom.xml
  4. 33 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/AppController.java
  5. 127 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/Auth2Controller.java
  6. 222 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/AuthController.java
  7. 532 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/CourseController.java
  8. 138 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/CourseSpeciatlyRelationController.java
  9. 90 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/FaceController.java
  10. 95 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/LogController.java
  11. 1406 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/OrgController.java
  12. 194 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/ResourceController.java
  13. 804 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/RolePrivilegeController.java
  14. 386 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/SpecialtyController.java
  15. 654 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/StudentController.java
  16. 144 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/SystemPropertyController.java
  17. 60 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/TestController.java
  18. 711 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/UserController.java
  19. 87 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/CourseDomain.java
  20. 30 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/CustomPrivilegeDomain.java
  21. 115 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/ExamSiteDomain.java
  22. 149 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/OrgDomain.java
  23. 73 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/OrgPrivilegeTreeDomain.java
  24. 273 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/PrivilegeDomain.java
  25. 153 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/PrivilegeGroupDomain.java
  26. 113 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/ResourceDomain.java
  27. 68 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/RoleDomain.java
  28. 49 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/SaveOrgPropertiesDomain.java
  29. 59 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/SpecialtyDomain.java
  30. 199 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/StudentDomain.java
  31. 86 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/StudentFaceDomain.java
  32. 66 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/SystemPropertyDomain.java
  33. 58 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/UpdateRolePrivilegeRelationsDomain.java
  34. 41 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/UpdateRootOrgPrivilegeRelationsDomain.java
  35. 172 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/UserDomain.java
  36. 128 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/UserFormDomain.java
  37. 41 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/provider/AuthCloudServiceProvider.java
  38. 161 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/provider/CourseCloudServiceProvider.java
  39. 156 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/provider/FaceCloudServiceProvider.java
  40. 220 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/provider/OrgCloudServiceProvider.java
  41. 135 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/provider/RolePrivilegeCloudServiceProvider.java
  42. 267 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/provider/StudentCloudServiceProvider.java
  43. 78 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/provider/SystemPropertyCloudServiceProvider.java
  44. 333 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/provider/UserCloudServiceProvider.java
  45. 34 0
      examcloud-core-basic-base/pom.xml
  46. 32 0
      examcloud-core-basic-base/src/main/java/cn/com/qmth/examcloud/core/basic/base/constants/BasicConsts.java
  47. 17 0
      examcloud-core-basic-base/src/main/java/cn/com/qmth/examcloud/core/basic/base/constants/PropKeys.java
  48. 13 0
      examcloud-core-basic-base/src/main/java/cn/com/qmth/examcloud/core/basic/base/constants/SystemProps.java
  49. 62 0
      examcloud-core-basic-base/src/main/java/cn/com/qmth/examcloud/core/basic/base/enums/AccountType.java
  50. 28 0
      examcloud-core-basic-base/src/main/java/cn/com/qmth/examcloud/core/basic/base/processor/HttpMethodProcessorImpl.java
  51. 5 0
      examcloud-core-basic-base/src/main/java/cn/com/qmth/examcloud/core/basic/base/util/BaseUtil.java
  52. 19 0
      examcloud-core-basic-dao/pom.xml
  53. 23 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/AppRepo.java
  54. 25 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/CourseRepo.java
  55. 23 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/CourseSpeciatlyRelationRepo.java
  56. 17 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/FacesetRepo.java
  57. 28 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/OrgPropertyRepo.java
  58. 36 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/OrgRepo.java
  59. 26 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/PrivilegeGroupRepo.java
  60. 36 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/PrivilegeRepo.java
  61. 19 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/ResourceRepo.java
  62. 35 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/RolePrivilegeRelationRepo.java
  63. 29 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/RoleRepo.java
  64. 26 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/RootOrgPrivilegeRelationRepo.java
  65. 13 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/SmsAssemblyRepo.java
  66. 22 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/SpecialtyRepo.java
  67. 24 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/StudentCodeRepo.java
  68. 13 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/StudentFaceRepo.java
  69. 30 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/StudentRepo.java
  70. 22 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/SystemPropertyRepo.java
  71. 12 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/ThirdPartyAccessRepo.java
  72. 29 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/UniqueRuleHolder.java
  73. 43 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/UserRepo.java
  74. 33 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/UserRoleRelationRepo.java
  75. 114 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/AppEntity.java
  76. 98 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/CourseEntity.java
  77. 56 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/CourseSpeciatlyRelationEntity.java
  78. 36 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/CourseSpeciatlyRelationPK.java
  79. 91 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/FacesetEntity.java
  80. 138 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/OrgEntity.java
  81. 73 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/OrgPropertyEntity.java
  82. 249 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/PrivilegeEntity.java
  83. 172 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/PrivilegeGroupEntity.java
  84. 110 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/ResourceEntity.java
  85. 85 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/RoleEntity.java
  86. 57 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/RolePrivilegeRelationEntity.java
  87. 57 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/RolePrivilegeRelationPK.java
  88. 61 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/RootOrgPrivilegeRelationEntity.java
  89. 46 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/RootOrgPrivilegeRelationPK.java
  90. 221 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/SmsAssemblyEntity.java
  91. 82 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/SpecialtyEntity.java
  92. 86 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/StudentCodeEntity.java
  93. 158 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/StudentEntity.java
  94. 82 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/StudentFaceEntity.java
  95. 72 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/SystemPropertyEntity.java
  96. 71 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/ThirdPartyAccessEntity.java
  97. 45 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/ThirdPartyAccessPK.java
  98. 137 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/UserEntity.java
  99. 57 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/UserPrivilegeRelationEntity.java
  100. 46 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/UserPrivilegeRelationPK.java

+ 12 - 0
.gitignore

@@ -0,0 +1,12 @@
+.project
+.classpath
+.settings
+target/
+.idea/
+*.iml
+*test/
+# Package Files #
+*.jar
+logs/
+
+

+ 3 - 0
README.md

@@ -0,0 +1,3 @@
+#考试云平台核心服务
+
+包含机构、考试、考生、课程及成绩服务

+ 26 - 0
examcloud-core-basic-api-provider/pom.xml

@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>cn.com.qmth.examcloud.core.basic</groupId>
+		<artifactId>examcloud-core-basic</artifactId>
+		<version>2019-SNAPSHOT</version>
+	</parent>
+	<artifactId>examcloud-core-basic-api-provider</artifactId>
+	<packaging>jar</packaging>
+
+	<dependencies>
+		<dependency>
+			<groupId>cn.com.qmth.examcloud.rpc</groupId>
+			<artifactId>examcloud-core-basic-api</artifactId>
+			<version>${examcloud.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>cn.com.qmth.examcloud.core.basic</groupId>
+			<artifactId>examcloud-core-basic-service</artifactId>
+			<version>${examcloud.version}</version>
+		</dependency>
+
+	</dependencies>
+
+</project>

+ 33 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/AppController.java

@@ -0,0 +1,33 @@
+package cn.com.qmth.examcloud.core.basic.api.controller;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Sort;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.core.basic.dao.AppRepo;
+import cn.com.qmth.examcloud.core.basic.dao.entity.AppEntity;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * 应用服务API Created by ting.yin on 17/3/9.
+ */
+@RestController
+@RequestMapping("${$rmp.ctr.basic}/app")
+public class AppController extends ControllerSupport {
+
+	@Autowired
+	AppRepo appRepo;
+
+	@ApiOperation(value = "查询所有应用")
+	@GetMapping("getAllApp")
+	public List<AppEntity> getAllApp() {
+		Sort sort = new Sort(Sort.Direction.ASC, "id");
+		return appRepo.findAll(sort);
+	}
+
+}

+ 127 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/Auth2Controller.java

@@ -0,0 +1,127 @@
+package cn.com.qmth.examcloud.core.basic.api.controller;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.google.common.collect.Maps;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.basic.base.enums.AccountType;
+import cn.com.qmth.examcloud.core.basic.dao.UserRepo;
+import cn.com.qmth.examcloud.core.basic.dao.entity.UserEntity;
+import cn.com.qmth.examcloud.core.basic.service.AuthService;
+import cn.com.qmth.examcloud.core.basic.service.bean.LoginInfo;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import cn.com.qmth.examcloud.web.support.Naked;
+import cn.com.qmth.examcloud.web.support.WithoutStackTrace;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * 废弃登陆. 保留原因:第三方接口鉴权
+ *
+ * @author WANGWEI
+ * @date 2018年11月19日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@RestController
+@RequestMapping("/api/ecs_core/user")
+public class Auth2Controller extends ControllerSupport {
+
+	@Autowired
+	UserRepo userRepo;
+
+	@Autowired
+	AuthService authService;
+
+	/**
+	 * 系统遗留,谨慎修改
+	 *
+	 * @author WANGWEI
+	 * @param loginName
+	 * @param password
+	 * @return
+	 */
+	@Naked
+	@WithoutStackTrace(true)
+	@ApiOperation(value = "登录1", notes = "登录")
+	@PostMapping("/login")
+	public Map<String, Object> login1(@RequestParam String loginName,
+			@RequestParam(required = false) Long rootOrgId) {
+		String password = getRequiredStringParam("password");
+		return login(loginName, password, rootOrgId);
+	}
+
+	/**
+	 * 系统遗留,谨慎修改
+	 *
+	 * @author WANGWEI
+	 * @param rootOrgId
+	 * @param loginName
+	 * @param password
+	 * @return
+	 */
+	@Naked
+	@WithoutStackTrace(true)
+	@ApiOperation(value = "登录2", notes = "登录")
+	@PostMapping("/login/{rootOrgId}")
+	public Map<String, Object> login2(@PathVariable long rootOrgId,
+			@RequestParam String loginName) {
+		String password = getRequiredStringParam("password");
+		return login(loginName, password, rootOrgId);
+	}
+
+	/**
+	 * 废弃的登陆
+	 *
+	 * @author WANGWEI
+	 * @param loginName
+	 * @param password
+	 * @param rootOrgId
+	 * @return
+	 */
+	private Map<String, Object> login(String loginName, String password, Long rootOrgId) {
+
+		UserEntity user = null;
+		if (null != rootOrgId) {
+			user = userRepo.findByRootOrgIdAndLoginName(rootOrgId, loginName);
+		} else {
+			List<UserEntity> userList = userRepo.findByLoginName(loginName);
+			if (CollectionUtils.isNotEmpty(userList)) {
+				user = null;
+			}
+			if (1 == userList.size()) {
+				user = userList.get(0);
+			} else if (1 < userList.size()) {
+				throw new StatusException("001003", "不同顶级机构下存在相同登录名");
+			}
+		}
+
+		if (user == null) {
+			throw new StatusException("001001", "该用户不存在");
+		} else if (!user.getEnable()) {
+			throw new StatusException("001002", "该用户被禁用");
+		}
+
+		LoginInfo loginInfo = new LoginInfo();
+		loginInfo.setAccountType(AccountType.COMMON_LOGIN_NAME.name());
+		loginInfo.setAccountValue(user.getLoginName());
+		loginInfo.setPassword(password);
+		loginInfo.setRootOrgId(user.getRootOrgId());
+		User loginUser = authService.login(loginInfo);
+
+		Map<String, Object> ret = Maps.newHashMap();
+		ret.put("userId", loginUser.getUserId());
+		ret.put("token", loginUser.getKey() + ":" + loginUser.getToken());
+		return ret;
+	}
+
+}

+ 222 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/AuthController.java

@@ -0,0 +1,222 @@
+package cn.com.qmth.examcloud.core.basic.api.controller;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.api.commons.security.bean.UserType;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.basic.dao.StudentRepo;
+import cn.com.qmth.examcloud.core.basic.dao.entity.StudentEntity;
+import cn.com.qmth.examcloud.core.basic.service.AuthService;
+import cn.com.qmth.examcloud.core.basic.service.SmsCodeService;
+import cn.com.qmth.examcloud.core.basic.service.StudentService;
+import cn.com.qmth.examcloud.core.basic.service.bean.LoginInfo;
+import cn.com.qmth.examcloud.core.basic.service.cache.StudentCache;
+import cn.com.qmth.examcloud.reports.commons.bean.OnlineStudentReport;
+import cn.com.qmth.examcloud.reports.commons.bean.OnlineUserReport;
+import cn.com.qmth.examcloud.reports.commons.util.ReportsUtil;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+import cn.com.qmth.examcloud.web.redis.RedisClient;
+import cn.com.qmth.examcloud.web.support.ApiId;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import cn.com.qmth.examcloud.web.support.Naked;
+import cn.com.qmth.examcloud.web.support.StatusResponseX;
+import cn.com.qmth.examcloud.web.support.WithoutStackTrace;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * {@link StatusException} 状态码范围:002XXX<br>
+ * {@link ApiId}范围: 200-299<br>
+ * 
+ * 鉴权
+ *
+ * @author WANGWEI
+ * @date 2018年5月25日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@RestController
+@RequestMapping("${$rmp.ctr.basic}/" + "auth")
+public class AuthController extends ControllerSupport {
+
+	@Autowired
+	AuthService authService;
+
+	@Autowired
+	RedisClient redisClient;
+
+	@Autowired
+	SmsCodeService smsCodeService;
+
+	@Autowired
+	StudentRepo studentRepo;
+
+	@Autowired
+	StudentService studentService;
+
+	@Autowired
+	StudentCache studentCache;
+
+	@Naked
+	@WithoutStackTrace(true)
+	@ApiOperation(value = "登入", notes = "")
+	@PostMapping("login")
+	public Object login(@RequestBody LoginInfo loginInfo, HttpServletRequest request) {
+
+		Boolean alwaysOK = loginInfo.getAlwaysOK();
+		if (null != alwaysOK && alwaysOK) {
+			setAlwaysOKResponse();
+		}
+
+		trim(loginInfo, false);
+
+		String realIp = request.getHeader("x-forwarded-for");
+		if (StringUtils.isBlank(realIp)) {
+			realIp = request.getHeader("x-real-ip");
+		}
+		if (StringUtils.isNotBlank(realIp)) {
+			loginInfo.setClientIp(realIp);
+		} else {
+			loginInfo.setClientIp(null);
+		}
+
+		User user = authService.login(loginInfo);
+		// 在线数据打点 start
+		if (UserType.STUDENT.equals(user.getUserType())) {
+			// 在线学生登录打点
+			ReportsUtil.report(new OnlineStudentReport(user.getRootOrgId(), user.getUserId()));
+		} else if (UserType.COMMON.equals(user.getUserType())) {
+			// 在线用户登录打点
+			ReportsUtil.report(new OnlineUserReport(user.getRootOrgId(), user.getUserId()));
+		}
+		// 在线数据打点 end
+		if (null != alwaysOK && alwaysOK) {
+			return new StatusResponseX<User>(user);
+		} else {
+			return user;
+		}
+	}
+
+	@WithoutStackTrace(true)
+	@ApiOperation(value = "登出", notes = "")
+	@PostMapping("logout")
+	public void logout() {
+		User user = getAccessUser();
+		authService.logout(user);
+	}
+
+	@Naked
+	@WithoutStackTrace(true)
+	@ApiOperation(value = "获取登录用户", notes = "")
+	@PostMapping("getLoginUser")
+	public User getLoginUser(@RequestParam String key, @RequestParam String token) {
+
+		if (StringUtils.isBlank(key)) {
+			throw new StatusException("001009", "key is blank");
+		}
+		if (StringUtils.isBlank(token)) {
+			throw new StatusException("001010", "token is blank");
+		}
+
+		return authService.getLoginUser(key, token);
+	}
+
+	@Naked
+	@WithoutStackTrace(true)
+	@ApiOperation(value = "第三方普通用户接入", notes = "")
+	@PostMapping("/thirdPartyAccess")
+	public User thirdPartyCommonUserAccess(HttpServletRequest request, @RequestParam Long orgId,
+			@RequestParam String loginName, @RequestParam String appId,
+			@RequestParam String timestamp, @RequestParam String token) {
+		String realIp = request.getHeader("x-forwarded-for");
+		if (StringUtils.isBlank(realIp)) {
+			realIp = request.getHeader("x-real-ip");
+		}
+		return authService.thirdPartyCommonUserAccess(orgId, loginName, appId, timestamp, token,
+				realIp);
+	}
+
+	@Naked
+	@WithoutStackTrace(true)
+	@ApiOperation(value = "第三方学生接入", notes = "")
+	@PostMapping("/thirdPartyStudentAccess")
+	public User thirdPartyStudentAccess(HttpServletRequest request, @RequestParam Long rootOrgId,
+			@RequestParam String accountType, @RequestParam String accountValue,
+			@RequestParam String appId, @RequestParam String timestamp,
+			@RequestParam String token) {
+		String realIp = request.getHeader("x-forwarded-for");
+		if (StringUtils.isBlank(realIp)) {
+			realIp = request.getHeader("x-real-ip");
+		}
+		return authService.thirdPartyStudentAccess(rootOrgId, accountType, accountValue, appId,
+				timestamp, token, realIp);
+	}
+
+	@Naked
+	@WithoutStackTrace(true)
+	@ApiOperation(value = "发送验证码", notes = "")
+	@PostMapping("sendVerificationCode4Student")
+	public void sendVerificationCode4Student(@RequestParam(required = true) String phone,
+			@RequestParam(required = true) Boolean bound) {
+
+		Boolean hasBeBound = studentService.hasBeBound(phone);
+		if (bound && !hasBeBound) {
+			throw new StatusException("002050", "手机号未被关联");
+		} else if ((!bound) && hasBeBound) {
+			throw new StatusException("002050", "手机号已被关联");
+		}
+
+		smsCodeService.sendSmsCode(phone);
+	}
+
+	@Naked
+	@WithoutStackTrace(true)
+	@ApiOperation(value = "发送验证码", notes = "")
+	@PostMapping("/sendVerificationCode")
+	public void sendVerificationCode(@RequestParam String phone) {
+		smsCodeService.sendSmsCode(phone);
+	}
+
+	@WithoutStackTrace(true)
+	@ApiOperation(value = "校验验证码", notes = "")
+	@PostMapping("/checkVerificationCode")
+	public void checkVerificationCode(@RequestParam String phone,
+			@RequestParam String verificationCode) {
+		smsCodeService.checkSmsCode(phone, verificationCode);
+	}
+
+	@WithoutStackTrace(true)
+	@ApiOperation(value = "学生绑定手机号", notes = "")
+	@PostMapping("/bindSecurityPhone")
+	public void bindStudentSecurityPhone(@RequestParam String phone,
+			@RequestParam String verificationCode) {
+
+		smsCodeService.checkSmsCode(phone, verificationCode);
+
+		User accessUser = getAccessUser();
+		Long studentId = accessUser.getUserId();
+
+		StudentEntity sb = studentRepo.findBySecurityPhone(phone);
+		if (null != sb) {
+			if (!sb.getId().equals(studentId)) {
+				throw new StatusException("001052", "手机号已被其他用户绑定,请更换手机号");
+			} else {
+				throw new StatusException("001053", "手机号已绑定成功,无需重复绑定");
+			}
+		}
+
+		StudentEntity s = GlobalHelper.getEntity(studentRepo, studentId, StudentEntity.class);
+		s.setSecurityPhone(phone);
+		StudentEntity saved = studentRepo.saveAndFlush(s);
+
+		studentCache.remove(saved.getId());
+	}
+
+}

+ 532 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/CourseController.java

@@ -0,0 +1,532 @@
+package cn.com.qmth.examcloud.core.basic.api.controller;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+import javax.persistence.criteria.Subquery;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.fileupload.disk.DiskFileItem;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.domain.Sort.Direction;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.commons.CommonsMultipartFile;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+import cn.com.qmth.examcloud.api.commons.enums.CourseLevel;
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.commons.helpers.poi.ExcelWriter;
+import cn.com.qmth.examcloud.commons.util.PathUtil;
+import cn.com.qmth.examcloud.core.basic.api.CourseCloudService;
+import cn.com.qmth.examcloud.core.basic.api.controller.bean.CourseDomain;
+import cn.com.qmth.examcloud.core.basic.dao.CourseRepo;
+import cn.com.qmth.examcloud.core.basic.dao.entity.CourseEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.CourseSpeciatlyRelationEntity;
+import cn.com.qmth.examcloud.core.basic.service.CourseService;
+import cn.com.qmth.examcloud.core.basic.service.bean.CourseInfo;
+import cn.com.qmth.examcloud.task.api.DataSyncCloudService;
+import cn.com.qmth.examcloud.task.api.request.SyncCourseReq;
+import cn.com.qmth.examcloud.web.config.SystemProperties;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * 课程服务API Created by songyue on 17/1/14.
+ */
+@RestController
+@RequestMapping("${$rmp.ctr.basic}/course")
+public class CourseController extends ControllerSupport {
+
+	@Autowired
+	CourseRepo courseRepo;
+
+	@Autowired
+	SystemProperties systemConfig;
+
+	@Autowired
+	CourseService courseService;
+
+	@Autowired
+	CourseCloudService courseCloudService;
+
+	@Autowired
+	DataSyncCloudService dataSyncCloudService;
+
+	private static final String[] EXCEL_HEADER = new String[]{"课程名称", "课程代码", "层次(ZSB,GQZ,ALL)"};
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param curPage
+	 * @param pageSize
+	 * @param name
+	 * @param code
+	 * @param level
+	 * @param enable
+	 * @param specialtyId
+	 * @return
+	 */
+	@ApiOperation(value = "分页查询课程")
+	@GetMapping("coursePage/{curPage}/{pageSize}")
+	public Page<CourseEntity> getCoursePage(@PathVariable Integer curPage,
+			@PathVariable Integer pageSize, @RequestParam(required = false) String name,
+			@RequestParam(required = false) String code,
+			@RequestParam(required = false) String level,
+			@RequestParam(required = false) Boolean enable,
+			@RequestParam(required = false) Long specialtyId) {
+
+		User accessUser = getAccessUser();
+
+		Specification<CourseEntity> specification = (root, query, cb) -> {
+			List<Predicate> predicates = new ArrayList<>();
+
+			predicates.add(cb.equal(root.get("rootOrgId"), accessUser.getRootOrgId()));
+
+			if (StringUtils.isNotBlank(name)) {
+				predicates.add(cb.like(root.get("name"), toSqlSearchPattern(name)));
+			}
+			if (StringUtils.isNotBlank(code)) {
+				predicates.add(cb.like(root.get("code"), toSqlSearchPattern(code)));
+			}
+			if (StringUtils.isNotBlank(level)) {
+				predicates.add(cb.equal(root.get("level"), CourseLevel.valueOf(level)));
+			}
+			if (null != enable) {
+				predicates.add(cb.equal(root.get("enable"), enable));
+			}
+
+			if (null != specialtyId) {
+				Subquery<CourseSpeciatlyRelationEntity> subquery = query
+						.subquery(CourseSpeciatlyRelationEntity.class);
+				Root<CourseSpeciatlyRelationEntity> subRoot = subquery
+						.from(CourseSpeciatlyRelationEntity.class);
+				subquery.select(subRoot.get("courseId"));
+				Predicate p1 = cb.equal(subRoot.get("specialtyId"), specialtyId);
+				Predicate p2 = cb.equal(subRoot.get("courseId"), root.get("id"));
+				subquery.where(cb.and(p1, p2));
+				predicates.add(cb.exists(subquery));
+			}
+
+			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+		};
+
+		PageRequest pageRequest = PageRequest.of(curPage, pageSize,
+				new Sort(Direction.DESC, "updateTime", "id"));
+
+		Page<CourseEntity> page = courseRepo.findAll(specification, pageRequest);
+		return page;
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param name
+	 * @param level
+	 * @param enable
+	 * @param specialtyId
+	 * @return
+	 */
+	@ApiOperation(value = "查询课程")
+	@GetMapping("query")
+	public List<CourseEntity> query(@RequestParam(required = false) Long rootOrgId,
+			@RequestParam(required = false) String name,
+			@RequestParam(required = false) String level,
+			@RequestParam(required = false) Boolean enable,
+			@RequestParam(required = false) Long specialtyId) {
+
+		User accessUser = getAccessUser();
+		if (null == rootOrgId) {
+			rootOrgId = accessUser.getRootOrgId();
+		}
+
+		final Long finalRootOrgId = rootOrgId;
+
+		Specification<CourseEntity> specification = (root, query, cb) -> {
+			List<Predicate> predicates = new ArrayList<>();
+
+			predicates.add(cb.equal(root.get("rootOrgId"), finalRootOrgId));
+
+			Predicate pr1 = cb.like(root.get("name"), toSqlSearchPattern(name));
+			Predicate pr2 = cb.like(root.get("code"), toSqlSearchPattern(name));
+
+			predicates.add(cb.or(pr1, pr2));
+
+			if (StringUtils.isNotBlank(level)) {
+				predicates.add(cb.equal(root.get("level"), toSqlSearchPattern(level)));
+			}
+			if (null != enable) {
+				predicates.add(cb.equal(root.get("enable"), enable));
+			}
+
+			if (null != specialtyId) {
+				Subquery<CourseSpeciatlyRelationEntity> subquery = query
+						.subquery(CourseSpeciatlyRelationEntity.class);
+				Root<CourseSpeciatlyRelationEntity> subRoot = subquery
+						.from(CourseSpeciatlyRelationEntity.class);
+				subquery.select(subRoot.get("courseId"));
+				Predicate p1 = cb.equal(subRoot.get("specialtyId"), specialtyId);
+				Predicate p2 = cb.equal(subRoot.get("courseId"), root.get("id"));
+				subquery.where(cb.and(p1, p2));
+				predicates.add(cb.exists(subquery));
+			}
+
+			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+		};
+
+		PageRequest pageRequest = PageRequest.of(0, 50, new Sort(Direction.DESC, "updateTime"));
+
+		Page<CourseEntity> page = courseRepo.findAll(specification, pageRequest);
+
+		Iterator<CourseEntity> iterator = page.iterator();
+		List<CourseEntity> list = Lists.newArrayList();
+
+		while (iterator.hasNext()) {
+			CourseEntity next = iterator.next();
+
+			CourseEntity bean = new CourseEntity();
+			bean.setCode(next.getCode());
+			bean.setEnable(next.getEnable());
+			bean.setId(next.getId());
+			bean.setLevel(next.getLevel());
+			bean.setName(next.getName());
+			bean.setRootOrgId(next.getRootOrgId());
+
+			list.add(bean);
+		}
+
+		return list;
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param id
+	 * @return
+	 */
+	@ApiOperation(value = "按ID查询课程", notes = "ID查询")
+	@GetMapping("{id}")
+	public CourseEntity getCourseById(@PathVariable Long id) {
+		CourseEntity course = GlobalHelper.getEntity(courseRepo, id, CourseEntity.class);
+		if (null == course) {
+			throw new StatusException("540001", "课程不存在");
+		}
+
+		Long rootOrgId = course.getRootOrgId();
+		validateRootOrgIsolation(rootOrgId);
+		return course;
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param rootOrgId
+	 * @param code
+	 * @return
+	 */
+	@ApiOperation(value = "按code查询课程", notes = "code查询")
+	@GetMapping("byCode")
+	public CourseEntity getCourseByCode(@RequestParam Long rootOrgId, @RequestParam String code) {
+		validateRootOrgIsolation(rootOrgId);
+
+		CourseEntity course = courseRepo.findByRootOrgIdAndCode(rootOrgId, code);
+		if (null == course) {
+			throw new StatusException("540001", "课程不存在");
+		}
+
+		return course;
+	}
+
+	/**
+	 * 修正
+	 *
+	 * @author WANGWEI
+	 * @param domain
+	 * @return
+	 */
+	@ApiOperation(value = "新增课程", notes = "新增")
+	@PostMapping
+	@Transactional
+	public Long addCourse(@RequestBody CourseDomain domain) {
+		trim(domain, true);
+
+		User accessUser = getAccessUser();
+		Long rootOrgId = accessUser.getRootOrgId();
+
+		String code = domain.getCode();
+		String name = domain.getName();
+		if (StringUtils.isBlank(code)) {
+			throw new StatusException("620001", "课程编码不能为空");
+		}
+		if (StringUtils.isBlank(name)) {
+			throw new StatusException("620002", "课程名称不能为空");
+		}
+		if (name.length() > 50) {
+			throw new StatusException("620003", "课程名称不能超过50个字符");
+		}
+		CourseEntity course = courseRepo.findByRootOrgIdAndCode(rootOrgId, code);
+		if (null != course) {
+			throw new StatusException("620002", "课程编码已被占用");
+		}
+
+		CourseInfo info = new CourseInfo();
+		info.setRootOrgId(rootOrgId);
+		info.setCode(domain.getCode());
+		info.setEnable(domain.getEnable());
+		info.setId(domain.getId());
+		info.setLevel(domain.getLevel());
+		info.setName(domain.getName());
+
+		CourseEntity saved = courseService.saveCourse(info);
+		return saved.getId();
+	}
+
+	/**
+	 * 修正
+	 *
+	 * @author WANGWEI
+	 * @param domain
+	 * @return
+	 */
+	@ApiOperation(value = "更新课程", notes = "更新")
+	@PutMapping
+	@Transactional
+	public Long updateCourse(@RequestBody CourseDomain domain) {
+		trim(domain, true);
+
+		User accessUser = getAccessUser();
+		Long rootOrgId = accessUser.getRootOrgId();
+
+		String code = domain.getCode();
+		String name = domain.getName();
+		if (StringUtils.isBlank(code)) {
+			throw new StatusException("620001", "课程编码不能为空");
+		}
+		if (StringUtils.isBlank(name)) {
+			throw new StatusException("620002", "课程名称不能为空");
+		}
+		if (name.length() > 50) {
+			throw new StatusException("620003", "课程名称不能超过50个字符");
+		}
+
+		CourseInfo info = new CourseInfo();
+		info.setRootOrgId(rootOrgId);
+		info.setCode(domain.getCode());
+		info.setEnable(domain.getEnable());
+		info.setId(domain.getId());
+		info.setLevel(domain.getLevel());
+		info.setName(domain.getName());
+
+		CourseEntity saved = courseService.saveCourse(info);
+		return saved.getId();
+	}
+
+	@ApiOperation(value = "删除课程", notes = "删除")
+	@DeleteMapping("{ids}")
+	@Transactional
+	public void deleteCourse(@PathVariable String ids) {
+		List<Long> courseIds = Stream.of(ids.split(",")).map(s -> Long.parseLong(s.trim()))
+				.collect(Collectors.toList());
+		for (Long courseId : courseIds) {
+			CourseEntity one = GlobalHelper.getEntity(courseRepo, courseId, CourseEntity.class);
+			if (null == one) {
+				continue;
+			}
+			validateRootOrgIsolation(one.getRootOrgId());
+			courseRepo.delete(one);
+		}
+	}
+
+	@ApiOperation(value = "下载导入模板", notes = "下载导入模板")
+	@GetMapping("importTemplate")
+	public void getImportTemplate(HttpServletResponse response) {
+		String resoucePath = PathUtil.getResoucePath("templates/courseImportTemplate.xlsx");
+		exportFile("课程导入模板.xlsx", new File(resoucePath));
+	}
+
+	@ApiOperation(value = "导入课程", notes = "导入")
+	@PostMapping("import")
+	@Transactional
+	public Map<String, Object> importCourse(@RequestParam CommonsMultipartFile file) {
+		DiskFileItem item = (DiskFileItem) file.getFileItem();
+		File storeLocation = item.getStoreLocation();
+		List<Map<String, Object>> failRecords = courseService.importCourse(getRootOrgId(),
+				storeLocation);
+		Map<String, Object> map = Maps.newHashMap();
+		map.put("hasError", CollectionUtils.isNotEmpty(failRecords));
+		map.put("failRecords", failRecords);
+		return map;
+	}
+
+	@ApiOperation(value = "导出课程", notes = "导出")
+	@GetMapping("export")
+	public void export(@RequestParam(required = false) String name,
+			@RequestParam(required = false) String code,
+			@RequestParam(required = false) String level,
+			@RequestParam(required = false) Boolean enable,
+			@RequestParam(required = false) Long specialtyId) {
+
+		User accessUser = getAccessUser();
+
+		Specification<CourseEntity> specification = (root, query, cb) -> {
+			List<Predicate> predicates = new ArrayList<>();
+
+			predicates.add(cb.equal(root.get("rootOrgId"), accessUser.getRootOrgId()));
+
+			if (StringUtils.isNotBlank(name)) {
+				predicates.add(cb.like(root.get("name"), toSqlSearchPattern(name)));
+			}
+			if (StringUtils.isNotBlank(code)) {
+				predicates.add(cb.like(root.get("code"), toSqlSearchPattern(code)));
+			}
+			if (StringUtils.isNotBlank(level)) {
+				predicates.add(cb.equal(root.get("level"), CourseLevel.valueOf(level)));
+			}
+			if (null != enable) {
+				predicates.add(cb.equal(root.get("enable"), enable));
+			}
+
+			if (null != specialtyId) {
+				Subquery<CourseSpeciatlyRelationEntity> subquery = query
+						.subquery(CourseSpeciatlyRelationEntity.class);
+				Root<CourseSpeciatlyRelationEntity> subRoot = subquery
+						.from(CourseSpeciatlyRelationEntity.class);
+				subquery.select(subRoot.get("courseId"));
+				Predicate p1 = cb.equal(subRoot.get("specialtyId"), specialtyId);
+				Predicate p2 = cb.equal(subRoot.get("courseId"), root.get("id"));
+				subquery.where(cb.and(p1, p2));
+				predicates.add(cb.exists(subquery));
+			}
+
+			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+		};
+
+		long count = courseRepo.count(specification);
+		if (100000 < count) {
+			throw new StatusException("620200", "数据量过大,无法导出");
+		}
+
+		List<CourseEntity> list = courseRepo.findAll(specification);
+
+		List<Object[]> datas = Lists.newArrayList();
+
+		for (CourseEntity cur : list) {
+			datas.add(new Object[]{cur.getName(), cur.getCode(), cur.getLevel().getName()});
+		}
+
+		String filePath = systemConfig.getTempDataDir() + File.separator
+				+ System.currentTimeMillis() + ".xlsx";
+		File file = new File(filePath);
+
+		ExcelWriter.write(EXCEL_HEADER, new Class[]{String.class, String.class, String.class},
+				datas, new File(filePath));
+
+		exportFile("课程列表-" + getRootOrgId() + ".xlsx", file);
+
+		FileUtils.deleteQuietly(file);
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param ids
+	 * @return
+	 */
+	@ApiOperation(value = "禁用课程", notes = "禁用")
+	@PutMapping("disable/{ids}")
+	@Transactional
+	public List<Long> disableCourse(@PathVariable String ids) {
+		List<Long> courseIds = Stream.of(ids.split(",")).map(s -> Long.parseLong(s.trim()))
+				.collect(Collectors.toList());
+
+		List<CourseEntity> savedList = Lists.newArrayList();
+		for (Long courseId : courseIds) {
+			CourseEntity course = GlobalHelper.getEntity(courseRepo, courseId, CourseEntity.class);
+			course.setEnable(false);
+			CourseEntity saved = courseRepo.saveAndFlush(course);
+			savedList.add(saved);
+		}
+
+		for (CourseEntity ce : savedList) {
+			SyncCourseReq req = new SyncCourseReq();
+			req.setCode(ce.getCode());
+			req.setEnable(ce.getEnable());
+			req.setId(ce.getId());
+			req.setLevel(ce.getLevel().name());
+			req.setName(ce.getName());
+			req.setRootOrgId(ce.getRootOrgId());
+			req.setSyncType("update");
+			dataSyncCloudService.syncCourse(req);
+		}
+
+		return courseIds;
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param ids
+	 * @return
+	 */
+	@ApiOperation(value = "启用课程", notes = "启用")
+	@PutMapping("enable/{ids}")
+	@Transactional
+	public List<Long> enableCourse(@PathVariable String ids) {
+		List<Long> courseIds = Stream.of(ids.split(",")).map(s -> Long.parseLong(s.trim()))
+				.collect(Collectors.toList());
+
+		List<CourseEntity> savedList = Lists.newArrayList();
+		for (Long courseId : courseIds) {
+			CourseEntity course = GlobalHelper.getEntity(courseRepo, courseId, CourseEntity.class);
+			course.setEnable(true);
+			CourseEntity saved = courseRepo.saveAndFlush(course);
+			savedList.add(saved);
+		}
+
+		for (CourseEntity ce : savedList) {
+			SyncCourseReq req = new SyncCourseReq();
+			req.setCode(ce.getCode());
+			req.setEnable(ce.getEnable());
+			req.setId(ce.getId());
+			req.setLevel(ce.getLevel().name());
+			req.setName(ce.getName());
+			req.setRootOrgId(ce.getRootOrgId());
+			req.setSyncType("update");
+			dataSyncCloudService.syncCourse(req);
+		}
+
+		return courseIds;
+	}
+}

+ 138 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/CourseSpeciatlyRelationController.java

@@ -0,0 +1,138 @@
+package cn.com.qmth.examcloud.core.basic.api.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.basic.dao.CourseRepo;
+import cn.com.qmth.examcloud.core.basic.dao.CourseSpeciatlyRelationRepo;
+import cn.com.qmth.examcloud.core.basic.dao.SpecialtyRepo;
+import cn.com.qmth.examcloud.core.basic.dao.entity.CourseEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.CourseSpeciatlyRelationEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.CourseSpeciatlyRelationPK;
+import cn.com.qmth.examcloud.core.basic.dao.entity.SpecialtyEntity;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * @Description 课程专业关联API
+ * @author Administrator
+ * @Date 2017年8月8号
+ */
+@RestController
+@RequestMapping("${$rmp.ctr.basic}/courseSpeciatlyRelation")
+public class CourseSpeciatlyRelationController extends ControllerSupport {
+
+	@Autowired
+	CourseSpeciatlyRelationRepo courseSpeciatlyRelationRepo;
+
+	@Autowired
+	CourseRepo courseRepo;
+
+	@Autowired
+	SpecialtyRepo specialtyRepo;
+
+	@ApiOperation(value = "新增关联课程专业")
+	@PostMapping("add")
+	@Transactional
+	public CourseSpeciatlyRelationEntity add(@RequestParam(required = true) Long courseId,
+			@RequestParam(required = true) Long specialtyId) {
+		User accessUser = getAccessUser();
+
+		if (null == courseId) {
+			throw new StatusException("320001", "courseId is null");
+		}
+
+		if (null == specialtyId) {
+			throw new StatusException("320002", "specialtyId is null");
+		}
+
+		CourseEntity courseEntity = GlobalHelper.getEntity(courseRepo, courseId,
+				CourseEntity.class);
+		if (null == courseEntity) {
+			throw new StatusException("320001", "courseId is wrong");
+		}
+
+		this.validateRootOrgIsolation(courseEntity.getRootOrgId());
+
+		SpecialtyEntity specialtyEntity = GlobalHelper.getEntity(specialtyRepo, specialtyId,
+				SpecialtyEntity.class);
+		if (null == specialtyEntity) {
+			throw new StatusException("320001", "specialtyId is wrong");
+		}
+
+		this.validateRootOrgIsolation(specialtyEntity.getRootOrgId());
+
+		CourseSpeciatlyRelationPK pk = new CourseSpeciatlyRelationPK();
+		pk.setCourseId(courseId);
+		pk.setSpecialtyId(specialtyId);
+
+		CourseSpeciatlyRelationEntity one = GlobalHelper.getEntity(courseSpeciatlyRelationRepo, pk,
+				CourseSpeciatlyRelationEntity.class);
+		if (null != one) {
+			throw new StatusException("320003", "课程专业已关联");
+		}
+
+		one = new CourseSpeciatlyRelationEntity();
+		one.setCourseId(courseId);
+		one.setSpecialtyId(specialtyId);
+		one.setCreator(accessUser.getUserId());
+
+		CourseSpeciatlyRelationEntity saved = courseSpeciatlyRelationRepo.save(one);
+
+		return saved;
+	}
+
+	@ApiOperation(value = "删除关联课程专业")
+	@DeleteMapping("delete")
+	@Transactional
+	public CourseSpeciatlyRelationEntity delete(@RequestParam(required = true) Long courseId,
+			@RequestParam(required = true) Long specialtyId) {
+
+		if (null == courseId) {
+			throw new StatusException("320001", "courseId is null");
+		}
+
+		if (null == specialtyId) {
+			throw new StatusException("320002", "specialtyId is null");
+		}
+
+		CourseEntity courseEntity = GlobalHelper.getEntity(courseRepo, courseId,
+				CourseEntity.class);
+		if (null == courseEntity) {
+			throw new StatusException("320001", "courseId is wrong");
+		}
+
+		this.validateRootOrgIsolation(courseEntity.getRootOrgId());
+
+		SpecialtyEntity specialtyEntity = GlobalHelper.getEntity(specialtyRepo, specialtyId,
+				SpecialtyEntity.class);
+		if (null == specialtyEntity) {
+			throw new StatusException("320001", "specialtyId is wrong");
+		}
+
+		this.validateRootOrgIsolation(specialtyEntity.getRootOrgId());
+
+		CourseSpeciatlyRelationPK pk = new CourseSpeciatlyRelationPK();
+		pk.setCourseId(courseId);
+		pk.setSpecialtyId(specialtyId);
+
+		CourseSpeciatlyRelationEntity one = GlobalHelper.getEntity(courseSpeciatlyRelationRepo, pk,
+				CourseSpeciatlyRelationEntity.class);
+		if (null == one) {
+			throw new StatusException("320003", "课程专业未关联");
+		}
+
+		courseSpeciatlyRelationRepo.delete(one);
+
+		return one;
+	}
+
+}

+ 90 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/FaceController.java

@@ -0,0 +1,90 @@
+package cn.com.qmth.examcloud.core.basic.api.controller;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+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.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.basic.api.controller.bean.StudentFaceDomain;
+import cn.com.qmth.examcloud.core.basic.dao.StudentFaceRepo;
+import cn.com.qmth.examcloud.core.basic.dao.StudentRepo;
+import cn.com.qmth.examcloud.core.basic.dao.entity.FacesetEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.StudentFaceEntity;
+import cn.com.qmth.examcloud.core.basic.service.FaceService;
+import cn.com.qmth.examcloud.core.basic.service.bean.StudentFaceInfo;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * 人脸接口<br>
+ * 废弃 by wangwei 2019年9月2日
+ *
+ * @author WANGWEI
+ * @date 2018年9月4日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+// @RestController
+@RequestMapping("${$rmp.ctr.basic}/face")
+public class FaceController extends ControllerSupport {
+
+	@Autowired
+	StudentFaceRepo studentFaceRepo;
+
+	@Autowired
+	StudentRepo studentRepo;
+
+	@Autowired
+	FaceService studentFaceService;
+
+	@ApiOperation(value = "保存学生人脸数据")
+	@PostMapping("saveStudentFace")
+	@Transactional
+	public StudentFaceEntity saveStudentFace(@RequestBody StudentFaceDomain req) {
+		StudentFaceInfo info = new StudentFaceInfo();
+		info.setFacesetToken(req.getFacesetToken());
+		info.setFaceToken(req.getFaceToken());
+		info.setOperator(req.getOperator());
+		info.setPhotoName(req.getPhotoName());
+		info.setRootOrgId(req.getRootOrgId());
+		info.setStudentId(req.getStudentId());
+		info.setFaceCount(req.getFaceCount());
+
+		StudentFaceEntity saved = studentFaceService.saveStudentFace(info);
+		return saved;
+	}
+
+	@ApiOperation(value = "获取可用的faceset集合")
+	@GetMapping("getUsableFacesetList")
+	public List<FacesetEntity> getUsableFacesetList() {
+
+		List<FacesetEntity> facesetList = studentFaceService.getUsableFacesetList();
+
+		return facesetList;
+	}
+
+	@ApiOperation(value = "获取学生人脸信息")
+	@GetMapping("studentFace/{studentId}")
+	public StudentFaceEntity getStudentFace(@PathVariable Long studentId) {
+
+		if (null == studentId) {
+			throw new StatusException("710001", "studentId is null");
+		}
+
+		StudentFaceEntity studentFaceEntity = GlobalHelper.getEntity(studentFaceRepo, studentId,
+				StudentFaceEntity.class);
+
+		if (null == studentFaceEntity) {
+			throw new StatusException("710001", "studentFaceEntity is null");
+		}
+
+		return studentFaceEntity;
+	}
+
+}

+ 95 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/LogController.java

@@ -0,0 +1,95 @@
+package cn.com.qmth.examcloud.core.basic.api.controller;
+
+import java.util.Map;
+
+import org.apache.commons.lang3.RegExUtils;
+import org.slf4j.MDC;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.google.common.collect.Maps;
+
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.commons.logging.ExamCloudLog;
+import cn.com.qmth.examcloud.commons.logging.ExamCloudLogFactory;
+import cn.com.qmth.examcloud.commons.util.DateUtil;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * 日志服务
+ *
+ * @author WANGWEI
+ * @date 2018年11月30日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@RestController
+@RequestMapping("${$rmp.ctr.basic}/log")
+public class LogController extends ControllerSupport {
+
+	private static Map<String, ExamCloudLog> loggersHolder = Maps.newConcurrentMap();
+
+	private static Object lock = new Object();
+
+	@ApiOperation(value = "学生客户端日志")
+	@PostMapping("studentClient/{level}/{code}")
+	public String logByStudentClient(@PathVariable String level, @PathVariable String code,
+			@RequestBody String info) {
+		log("STUDENT_CLIENT_LOGGER", level, code, info);
+		return DateUtil.chinaNow();
+	}
+
+	/**
+	 * 写日志
+	 *
+	 * @author WANGWEI
+	 * @param logger
+	 * @param level
+	 * @param code
+	 * @param info
+	 */
+	private void log(String logger, String level, String code, String info) {
+		if (!code.matches("\\w{1,2}\\-\\d{6}")) {
+			throw new StatusException("500002",
+					"code must match regular expression:  \\w{1,2}\\-\\d{6}");
+		}
+		info = RegExUtils.replaceAll(info, "[\n\r]", "  ");
+		ExamCloudLog log = getLogger(logger);
+		MDC.put("CODE", code);
+		if ("debug".equalsIgnoreCase(level)) {
+			log.debug(info);
+		} else if ("info".equalsIgnoreCase(level)) {
+			log.info(info);
+		} else if ("error".equalsIgnoreCase(level)) {
+			log.error(info);
+		} else {
+			throw new StatusException("500001", "level must be [debug,info,error]");
+		}
+	}
+
+	/**
+	 * 获取日志对象
+	 *
+	 * @author WANGWEI
+	 * @param logger
+	 * @return
+	 */
+	private ExamCloudLog getLogger(String logger) {
+		ExamCloudLog log = loggersHolder.get(logger);
+		if (null == log) {
+			synchronized (lock) {
+				if (null == log) {
+					log = ExamCloudLogFactory.getLog(logger);
+					loggersHolder.put(logger, log);
+				}
+				return log;
+			}
+		} else {
+			return log;
+		}
+	}
+
+}

+ 1406 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/OrgController.java

@@ -0,0 +1,1406 @@
+package cn.com.qmth.examcloud.core.basic.api.controller;
+
+import cn.com.qmth.examcloud.api.commons.exchange.PageInfo;
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.commons.exception.ExamCloudRuntimeException;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.commons.helpers.DynamicEnum;
+import cn.com.qmth.examcloud.commons.helpers.DynamicEnumManager;
+import cn.com.qmth.examcloud.commons.util.JsonUtil;
+import cn.com.qmth.examcloud.commons.util.PathUtil;
+import cn.com.qmth.examcloud.commons.util.RegExpUtil;
+import cn.com.qmth.examcloud.core.basic.api.controller.bean.OrgDomain;
+import cn.com.qmth.examcloud.core.basic.api.controller.bean.SaveOrgPropertiesDomain;
+import cn.com.qmth.examcloud.core.basic.base.constants.SystemProps;
+import cn.com.qmth.examcloud.core.basic.dao.OrgPropertyRepo;
+import cn.com.qmth.examcloud.core.basic.dao.OrgRepo;
+import cn.com.qmth.examcloud.core.basic.dao.entity.OrgEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.OrgPropertyEntity;
+import cn.com.qmth.examcloud.core.basic.dao.enums.OrgProperty;
+import cn.com.qmth.examcloud.core.basic.service.SystemPropertyService;
+import cn.com.qmth.examcloud.core.basic.service.bean.OrgInfo;
+import cn.com.qmth.examcloud.core.basic.service.cache.OrgCache;
+import cn.com.qmth.examcloud.core.basic.service.cache.RootOrgCache;
+import cn.com.qmth.examcloud.core.basic.service.cache.SystemPropertyCache;
+import cn.com.qmth.examcloud.core.basic.service.impl.OrgServiceImpl;
+import cn.com.qmth.examcloud.support.cache.CacheHelper;
+import cn.com.qmth.examcloud.support.cache.bean.OrgCacheBean;
+import cn.com.qmth.examcloud.support.cache.bean.OrgPropertyCacheBean;
+import cn.com.qmth.examcloud.support.cache.bean.SysPropertyCacheBean;
+import cn.com.qmth.examcloud.support.filestorage.FileStorageUtil;
+import cn.com.qmth.examcloud.task.api.DataSyncCloudService;
+import cn.com.qmth.examcloud.task.api.request.SyncOrgReq;
+import cn.com.qmth.examcloud.web.config.SystemProperties;
+import cn.com.qmth.examcloud.web.filestorage.FileStoragePathEnvInfo;
+import cn.com.qmth.examcloud.web.filestorage.YunPathInfo;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+import cn.com.qmth.examcloud.web.redis.RedisClient;
+import cn.com.qmth.examcloud.web.support.ApiId;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import cn.com.qmth.examcloud.web.support.Naked;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import io.swagger.annotations.ApiOperation;
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.binary.Hex;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.fileupload.disk.DiskFileItem;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.domain.Sort.Direction;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.commons.CommonsMultipartFile;
+
+import javax.persistence.criteria.Predicate;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * {@link StatusException} 状态码范围:001XXX<br>
+ * {@link ApiId}范围: 100-199<br>
+ *
+ *
+ * @author WANGWEI
+ * @date 2019年6月11日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@RestController
+@RequestMapping("${$rmp.ctr.basic}/org")
+public class OrgController extends ControllerSupport {
+
+	@Autowired
+	OrgCache orgCache;
+
+	@Autowired
+	RootOrgCache rootOrgCache;
+
+	@Autowired
+	RedisClient redisClient;
+
+	@Autowired
+	OrgRepo orgRepo;
+
+	@Autowired
+	OrgServiceImpl orgService;
+
+	@Autowired
+	SystemProperties systemConfig;
+
+	@Autowired
+	OrgPropertyRepo orgPropertyRepo;
+
+	@Autowired
+	DataSyncCloudService dataSyncCloudService;
+
+//	@Autowired
+//	UpyunCloudService upyunCloudService;
+
+	@Autowired
+	SystemPropertyCache systemPropertyCache;
+
+	@Autowired
+	SystemPropertyService systemPropertyService;
+
+	@ApiOperation(value = "分页查询所有机构")
+	@GetMapping("fullOrgPage/{curPage}/{pageSize}")
+	public PageInfo<OrgDomain> getFullOrgPage(@PathVariable Integer curPage,
+			@PathVariable Integer pageSize, @RequestParam(required = false) String code,
+			@RequestParam(required = false) String name,
+			@RequestParam(required = false) String propertyKeys) {
+
+		Pageable pageable = PageRequest.of(curPage, pageSize, Sort.Direction.DESC, "updateTime");
+
+		Specification<OrgEntity> specification = (root, query, cb) -> {
+			List<Predicate> predicates = new ArrayList<>();
+
+			if (StringUtils.isNotEmpty(name)) {
+				predicates.add(cb.like(root.get("name"), toSqlSearchPattern(name)));
+			}
+			if (StringUtils.isNotEmpty(code)) {
+				predicates.add(cb.like(root.get("code"), toSqlSearchPattern(code)));
+			}
+			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+		};
+
+		Page<OrgEntity> page = orgRepo.findAll(specification, pageable);
+		List<OrgDomain> list = Lists.newArrayList();
+		Iterator<OrgEntity> iterator = page.iterator();
+
+		List<String> propertyKeyList = null;
+		if (StringUtils.isNotBlank(propertyKeys)) {
+			propertyKeyList = RegExpUtil.findAll(propertyKeys, "\\w+");
+		}
+
+		while (iterator.hasNext()) {
+			OrgEntity next = iterator.next();
+			OrgDomain d = new OrgDomain();
+			list.add(d);
+
+			d.setCode(next.getCode());
+			d.setContacts(next.getContacts());
+			d.setEnable(next.getEnable());
+			d.setId(next.getId());
+			d.setName(next.getName());
+			d.setParentId(next.getParentId());
+			d.setRemark(next.getRemark());
+			d.setRootId(next.getRootId());
+			d.setTelephone(next.getTelephone());
+			d.setCreationTime(next.getCreationTime());
+			d.setUpdateTime(next.getUpdateTime());
+
+			if (CollectionUtils.isNotEmpty(propertyKeyList)) {
+				Map<String, String> properties = getProperties(d.getId(), propertyKeyList);
+				d.setProperties(properties);
+			}
+		}
+
+		PageInfo<OrgDomain> ret = new PageInfo<OrgDomain>();
+		ret.setList(list);
+		ret.setTotal(page.getTotalElements());
+		return ret;
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param curPage
+	 * @param pageSize
+	 * @param code
+	 * @param domainName
+	 * @param name
+	 * @return
+	 */
+	@ApiOperation(value = "分页查询顶级机构")
+	@GetMapping("rootOrgPage/{curPage}/{pageSize}")
+	public PageInfo<OrgDomain> getRootOrgPage(@PathVariable Integer curPage,
+			@PathVariable Integer pageSize, @RequestParam(required = false) String code,
+			@RequestParam(required = false) String domainName,
+			@RequestParam(required = false) String name,
+			@RequestParam(required = false) String propertyKeys) {
+
+		Pageable pageable = PageRequest.of(curPage, pageSize, Sort.Direction.DESC, "updateTime");
+
+		Specification<OrgEntity> specification = (root, query, cb) -> {
+			List<Predicate> predicates = new ArrayList<>();
+			predicates.add(cb.isNull(root.get("parentId")));
+			if (StringUtils.isNotEmpty(name)) {
+				predicates.add(cb.like(root.get("name"), toSqlSearchPattern(name)));
+			}
+			if (StringUtils.isNotEmpty(code)) {
+				predicates.add(cb.like(root.get("code"), toSqlSearchPattern(code)));
+			}
+			if (StringUtils.isNotEmpty(domainName)) {
+				predicates.add(cb.like(root.get("domainName"), toSqlSearchPattern(domainName)));
+			}
+			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+		};
+
+		Page<OrgEntity> page = orgRepo.findAll(specification, pageable);
+		List<OrgDomain> list = Lists.newArrayList();
+		Iterator<OrgEntity> iterator = page.iterator();
+
+		List<String> propertyKeyList = null;
+		if (StringUtils.isNotBlank(propertyKeys)) {
+			propertyKeyList = RegExpUtil.findAll(propertyKeys, "\\w+");
+		}
+
+		while (iterator.hasNext()) {
+			OrgEntity next = iterator.next();
+			OrgDomain d = new OrgDomain();
+			list.add(d);
+
+			d.setCode(next.getCode());
+			d.setContacts(next.getContacts());
+			d.setEnable(next.getEnable());
+			d.setId(next.getId());
+			d.setName(next.getName());
+			d.setParentId(next.getParentId());
+			d.setRemark(next.getRemark());
+			d.setRootId(next.getRootId());
+			d.setTelephone(next.getTelephone());
+			d.setDomainName(next.getDomainName());
+			d.setCreationTime(next.getCreationTime());
+			d.setUpdateTime(next.getUpdateTime());
+
+			if (CollectionUtils.isNotEmpty(propertyKeyList)) {
+				Map<String, String> properties = getProperties(d.getId(), propertyKeyList);
+				d.setProperties(properties);
+			}
+		}
+
+		PageInfo<OrgDomain> ret = new PageInfo<OrgDomain>();
+		ret.setList(list);
+		ret.setTotal(page.getTotalElements());
+		return ret;
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param orgId
+	 * @param propertyKeys
+	 * @return
+	 */
+	private Map<String, String> getProperties(Long orgId, List<String> propertyKeys) {
+		DynamicEnumManager manager = OrgProperty.getDynamicEnumManager();
+		Map<String, String> map = Maps.newHashMap();
+		for (String key : propertyKeys) {
+			DynamicEnum de = manager.getByName(key);
+			OrgPropertyEntity one = orgPropertyRepo.findByOrgIdAndKeyId(orgId, de.getId());
+			if (null != one) {
+				map.put(key, one.getValue());
+			}
+		}
+
+		return map;
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param rootOrgId
+	 * @return
+	 */
+	@ApiOperation(value = "按ID查询顶级机构", notes = "ID查询")
+	@GetMapping("rootOrg/{rootOrgId}")
+	public OrgEntity getRootOrg(@PathVariable Long rootOrgId) {
+		OrgEntity orgEntity = GlobalHelper.getEntity(orgRepo, rootOrgId, OrgEntity.class);
+		if (null == orgEntity) {
+			throw new StatusException("001250", "rootOrgId is wrong");
+		}
+		if (null != orgEntity.getParentId()) {
+			throw new StatusException("001251", "rootOrgId is wrong");
+		}
+		validateRootOrgIsolation(orgEntity.getRootId());
+
+		return orgEntity;
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param orgId
+	 * @return
+	 */
+	@ApiOperation(value = "按ID查询子机构", notes = "ID查询")
+	@GetMapping("subOrg/{orgId}")
+	public OrgEntity getSubOrg(@PathVariable Long orgId) {
+		OrgEntity orgEntity = GlobalHelper.getEntity(orgRepo, orgId, OrgEntity.class);
+		if (null == orgEntity) {
+			throw new StatusException("001250", "orgId is wrong");
+		}
+		if (null == orgEntity.getParentId()) {
+			throw new StatusException("001251", "orgId is wrong");
+		}
+		validateRootOrgIsolation(orgEntity.getRootId());
+
+		return orgEntity;
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param code
+	 * @return
+	 */
+	@Naked
+	@ApiOperation(value = "查询顶级机构")
+	@GetMapping("getRootOrgByCode")
+	public OrgEntity getRootOrgByCode(@RequestParam("code") String code) {
+		OrgEntity org = orgRepo.findByParentIdIsNullAndCode(code);
+		return org;
+	}
+
+	@Naked
+	@ApiOperation(value = "查询顶级机构")
+	@GetMapping("getRootOrgByDomain")
+	public Map<String, Object> getRootOrgByDomain(@RequestParam("domainName") String domainName) {
+		OrgEntity org = orgRepo.findByParentIdIsNullAndDomainName(domainName);
+		if (null == org) {
+			throw new StatusException("001259", "domainName is wrong");
+		}
+		Map<String, Object> map = Maps.newHashMap();
+		map.put("id", org.getId());
+		map.put("name", org.getName());
+		map.put("code", org.getCode());
+		map.put("enable", org.getEnable());
+		return map;
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param curPage
+	 * @param pageSize
+	 * @param parentId
+	 * @param code
+	 * @param name
+	 * @return
+	 */
+	@ApiOperation(value = "分页查询子机构")
+	@GetMapping("subOrgPage/{curPage}/{pageSize}")
+	public PageInfo<OrgDomain> getSubOrgPage(@PathVariable Integer curPage,
+			@PathVariable Integer pageSize, @RequestParam(required = true) Long parentId,
+			@RequestParam(required = false) String code,
+			@RequestParam(required = false) String name,
+			@RequestParam(required = false) String propertyKeys) {
+
+		if (null == parentId) {
+			throw new StatusException("001249", "parentId is null");
+		}
+
+		OrgEntity parentOrg = GlobalHelper.getEntity(orgRepo, parentId, OrgEntity.class);
+		if (null == parentOrg) {
+			throw new StatusException("001250", "parentId is wrong");
+		}
+		validateRootOrgIsolation(parentOrg.getRootId());
+
+		Pageable pageable = PageRequest.of(curPage, pageSize, Sort.Direction.DESC, "updateTime");
+
+		Specification<OrgEntity> specification = (root, query, cb) -> {
+			List<Predicate> predicates = new ArrayList<>();
+			predicates.add(cb.equal(root.get("parentId"), parentId));
+
+			if (StringUtils.isNotEmpty(name)) {
+				predicates.add(cb.like(root.get("name"), toSqlSearchPattern(name)));
+			}
+			if (StringUtils.isNotEmpty(code)) {
+				predicates.add(cb.like(root.get("code"), toSqlSearchPattern(code)));
+			}
+			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+		};
+
+		Page<OrgEntity> page = orgRepo.findAll(specification, pageable);
+		List<OrgDomain> list = Lists.newArrayList();
+		Iterator<OrgEntity> iterator = page.iterator();
+
+		List<String> propertyKeyList = null;
+		if (StringUtils.isNotBlank(propertyKeys)) {
+			propertyKeyList = RegExpUtil.findAll(propertyKeys, "\\w+");
+		}
+
+		while (iterator.hasNext()) {
+			OrgEntity next = iterator.next();
+			OrgDomain d = new OrgDomain();
+			list.add(d);
+
+			d.setCode(next.getCode());
+			d.setContacts(next.getContacts());
+			d.setEnable(next.getEnable());
+			d.setId(next.getId());
+			d.setName(next.getName());
+			d.setParentId(next.getParentId());
+			d.setRemark(next.getRemark());
+			d.setRootId(next.getRootId());
+			d.setTelephone(next.getTelephone());
+			d.setCreationTime(next.getCreationTime());
+			d.setUpdateTime(next.getUpdateTime());
+
+			if (CollectionUtils.isNotEmpty(propertyKeyList)) {
+				Map<String, String> properties = getProperties(d.getId(), propertyKeyList);
+				d.setProperties(properties);
+			}
+		}
+
+		PageInfo<OrgDomain> ret = new PageInfo<OrgDomain>();
+		ret.setList(list);
+		ret.setTotal(page.getTotalElements());
+		return ret;
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param enable
+	 * @return
+	 */
+	@ApiOperation(value = "查询顶级机构")
+	@GetMapping("getRootOrgList")
+	public List<OrgEntity> getRootOrgList(@RequestParam(required = false) Boolean enable) {
+
+		Specification<OrgEntity> specification = (root, query, cb) -> {
+			List<Predicate> predicates = new ArrayList<>();
+			predicates.add(cb.isNull(root.get("parentId")));
+			if (null != enable) {
+				predicates.add(cb.equal(root.get("enable"), enable));
+			}
+			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+		};
+
+		List<OrgEntity> ret = orgRepo.findAll(specification);
+		return ret;
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param name
+	 * @param rootOrgId
+	 * @param enable
+	 * @return
+	 */
+	@ApiOperation(value = "查询一级子机构列表")
+	@GetMapping("query")
+	public List<OrgEntity> querySubOrgList(@RequestParam(required = true) String name,
+			@RequestParam(required = false) Long rootOrgId,
+			@RequestParam(required = false) Boolean enable,
+			@RequestParam(required = false) Boolean returnEmptyWhenNameNull) {
+
+		User accessUser = getAccessUser();
+		if (null == rootOrgId) {
+			rootOrgId = accessUser.getRootOrgId();
+		} else {
+			validateRootOrgIsolation(rootOrgId);
+		}
+
+		Long finalRootOrgId = rootOrgId;
+
+		if (null != returnEmptyWhenNameNull && returnEmptyWhenNameNull
+				&& StringUtils.isBlank(name)) {
+			List<OrgEntity> list = Lists.newArrayList();
+			return list;
+		}
+
+		Specification<OrgEntity> specification = (root, query, cb) -> {
+			List<Predicate> predicates = new ArrayList<>();
+			predicates.add(cb.equal(root.get("rootId"), finalRootOrgId));
+			predicates.add(cb.isNotNull(root.get("parentId")));
+			if (StringUtils.isNotBlank(name)) {
+				Predicate pr1 = cb.like(root.get("name"), toSqlSearchPattern(name));
+				Predicate pr2 = cb.like(root.get("code"), toSqlSearchPattern(name));
+				predicates.add(cb.or(pr1, pr2));
+			}
+			if (null != enable) {
+				predicates.add(cb.equal(root.get("enable"), enable));
+			}
+
+			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+		};
+
+		PageRequest pageRequest = PageRequest.of(0, 50, new Sort(Direction.DESC, "updateTime"));
+		Page<OrgEntity> page = orgRepo.findAll(specification, pageRequest);
+		Iterator<OrgEntity> iterator = page.iterator();
+		List<OrgEntity> list = Lists.newArrayList();
+		while (iterator.hasNext()) {
+			OrgEntity next = iterator.next();
+
+			OrgEntity bean = new OrgEntity();
+			bean.setCode(next.getCode());
+			bean.setContacts(next.getContacts());
+			bean.setEnable(next.getEnable());
+			bean.setId(next.getId());
+			bean.setName(next.getName());
+			bean.setParentId(next.getParentId());
+			bean.setRemark(next.getRemark());
+			bean.setRootId(next.getRootId());
+			bean.setTelephone(next.getTelephone());
+
+			list.add(bean);
+		}
+
+		return list;
+	}
+
+	@ApiOperation(value = "查询全量机构树")
+	@GetMapping("queryFullOrgTree")
+	public List<OrgDomain> queryFullOrgTree(@RequestParam(required = false) String propertyKeys) {
+		User accessUser = getAccessUser();
+		Long rootOrgId = accessUser.getRootOrgId();
+
+		Specification<OrgEntity> specification = (root, query, cb) -> {
+			List<Predicate> predicates = new ArrayList<>();
+			predicates.add(cb.equal(root.get("rootId"), rootOrgId));
+
+			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+		};
+
+		// 过载保护
+		long total = orgRepo.count(specification);
+		if (total > 1000) {
+			throw new StatusException("001251", "total is larger than 1000");
+		}
+
+		Sort sort = new Sort(Direction.ASC, "id");
+		List<OrgEntity> orgEntityList = orgRepo.findAll(specification, sort);
+
+		List<OrgDomain> ret = Lists.newArrayList();
+		Iterator<OrgEntity> iterator = orgEntityList.iterator();
+
+		List<String> propertyKeyList = null;
+		if (StringUtils.isNotBlank(propertyKeys)) {
+			propertyKeyList = RegExpUtil.findAll(propertyKeys, "\\w+");
+		}
+
+		while (iterator.hasNext()) {
+			OrgEntity next = iterator.next();
+			OrgDomain d = new OrgDomain();
+			ret.add(d);
+
+			d.setCode(next.getCode());
+			d.setContacts(next.getContacts());
+			d.setEnable(next.getEnable());
+			d.setId(next.getId());
+			d.setName(next.getName());
+			d.setParentId(next.getParentId());
+			d.setRemark(next.getRemark());
+			d.setRootId(next.getRootId());
+			d.setTelephone(next.getTelephone());
+			d.setCreationTime(next.getCreationTime());
+			d.setUpdateTime(next.getUpdateTime());
+
+			if (CollectionUtils.isNotEmpty(propertyKeyList)) {
+				Map<String, String> properties = getProperties(d.getId(), propertyKeyList);
+				d.setProperties(properties);
+			}
+		}
+
+		return ret;
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param orgId
+	 * @return
+	 */
+	@ApiOperation(value = "查询机构所有属性")
+	@GetMapping("allProperties/{orgId}")
+	public Map<String, String> getAllOrgProperties(@PathVariable Long orgId) {
+
+		OrgEntity orgEntity = GlobalHelper.getEntity(orgRepo, orgId, OrgEntity.class);
+		if (null == orgEntity) {
+			throw new StatusException("001250", "orgId is wrong");
+		}
+		validateRootOrgIsolation(orgEntity.getRootId());
+
+		Map<String, String> map = Maps.newHashMap();
+		List<OrgPropertyEntity> list = orgPropertyRepo.findByOrgId(orgId);
+		DynamicEnumManager manager = OrgProperty.getDynamicEnumManager();
+		for (OrgPropertyEntity cur : list) {
+			DynamicEnum de = manager.getById(cur.getKeyId());
+			map.put(de.getName(), cur.getValue());
+		}
+
+		return map;
+	}
+
+	@Naked
+	@ApiOperation(value = "按属性组查询机构属性集合(无缓存)")
+	@GetMapping("getOrgPropertiesByGroupWithoutCache/{orgId}/{propertyGroupId}")
+	public Map<String, String> getOrgPropertiesByGroupWithoutCache(@PathVariable Long orgId,
+			@PathVariable String propertyGroupId) {
+
+		Map<String, String> properties = getOrgPropertiesByGroup(orgId, propertyGroupId);
+		return properties;
+	}
+
+	@Naked
+	@ApiOperation(value = "按属性组查询机构属性集合(有缓存)")
+	@GetMapping("getOrgPropertiesByGroupWithCache/{orgId}/{propertyGroupId}")
+	public Map<String, String> getOrgPropertiesByGroupWithCache(@PathVariable Long orgId,
+			@PathVariable String propertyGroupId) {
+
+		String redisKey = "PROPERTIES_BY_GROUP:" + orgId + ":" + propertyGroupId;
+		@SuppressWarnings("unchecked")
+		Map<String, String> properties = redisClient.get(redisKey, Map.class);
+		if (null != properties) {
+			return properties;
+		}
+		properties = getOrgPropertiesByGroup(orgId, propertyGroupId);
+
+		redisClient.set(redisKey, properties, 60 * 10);
+		return properties;
+	}
+
+	private List<String> uploadOrgProperties2Upyun(Long orgId, String propertyGroupId) {
+		Map<String, String> properties = getOrgPropertiesByGroup(orgId, propertyGroupId);
+
+		OrgCacheBean org = CacheHelper.getOrg(orgId);
+		properties.put("ROOT_ORG_ID", String.valueOf(org.getRootId()));
+
+		OrgEntity orgEntity = GlobalHelper.getEntity(orgRepo, orgId, OrgEntity.class);
+		String fileSuffix = ".json";
+
+		String filePath = systemConfig.getTempDataDir() + File.separator + "orgProperties"
+				+ File.separator + propertyGroupId + "_" + orgId + fileSuffix;
+		filePath = PathUtil.getCanonicalPath(filePath);
+		File file = new File(filePath);
+
+		Map<String, String> sortedProperties = new TreeMap<String, String>(
+				new java.util.Comparator<String>() {
+					@Override
+					public int compare(String str1, String str2) {
+						return str1.compareTo(str2);
+					}
+
+				});
+
+		sortedProperties.putAll(properties);
+
+		String prettyJson = JsonUtil.toPrettyJson(sortedProperties);
+
+		List<String> urlList = Lists.newArrayList();
+		try {
+			FileUtils.write(file, prettyJson, "UTF-8");
+
+//			PutFileReq req = new PutFileReq();
+//			List<FormFilePart> formFilePartList = new ArrayList<FormFilePart>();
+//			FormFilePart part = new FormFilePart("file", orgId + fileSuffix, file);
+//			formFilePartList.add(part);
+//
+//			req.setFormFilePartList(formFilePartList);
+//
+//			req.setFileSuffix(fileSuffix);
+//			req.setRootOrgId(orgEntity.getId());
+//			req.setRootOrgDomain(orgEntity.getDomainName());
+//			req.setExt1(propertyGroupId);
+//
+//			PutFileResp putFileResp = null;
+//
+//			req.setSiteId("orgPropertiesByOrgId");
+//			putFileResp = upyunCloudService.putFile(req);
+//			urlList.add(putFileResp.getUrl());
+//
+//			req.setSiteId("orgPropertiesByOrgDomain");
+//			putFileResp = upyunCloudService.putFile(req);
+//			urlList.add(putFileResp.getUrl());
+			
+			//通用存储
+			YunPathInfo pi=null;
+			FileStoragePathEnvInfo env=new FileStoragePathEnvInfo();
+			env.setFileSuffix(fileSuffix);
+			env.setRootOrgId(orgEntity.getId().toString());
+			env.setRootOrgDomain(orgEntity.getDomainName());
+			env.setExt1(propertyGroupId);
+			pi=FileStorageUtil.saveFile("orgPropertiesByOrgId", env, file,null);
+			urlList.add(pi.getUrl());
+			pi=FileStorageUtil.saveFile("orgPropertiesByOrgDomain", env, file,null);
+			urlList.add(pi.getUrl());
+
+			return urlList;
+
+		} catch (IOException e) {
+			throw new ExamCloudRuntimeException(e);
+		} finally {
+			FileUtils.deleteQuietly(file);
+		}
+
+	}
+
+	public Map<String, String> getOrgPropertiesByGroup(Long orgId, String propertyGroupId) {
+
+		OrgEntity orgEntity = GlobalHelper.getEntity(orgRepo, orgId, OrgEntity.class);
+		if (null == orgEntity) {
+			throw new StatusException("001250", "orgId is wrong");
+		}
+
+		String key = SystemProps.ORG_PROPERTY_GROUP_PREFIX + propertyGroupId;
+
+		SysPropertyCacheBean groupCacheBean = systemPropertyCache.get(key);
+
+		if (null == groupCacheBean) {
+			throw new StatusException("001280", "unknown propertyGroupId");
+		}
+
+		Object value = groupCacheBean.getValue();
+		if (null == value || StringUtils.isBlank(String.valueOf(value))) {
+			throw new StatusException("001281", "value of  [" + key + "] is blank");
+		}
+
+		List<String> keys = RegExpUtil.findAll(String.valueOf(value), "[^\\,]+");
+		Map<String, String> properties = getProperties(orgEntity.getId(), keys);
+
+		return properties;
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param orgId
+	 * @param key
+	 * @return
+	 */
+	@ApiOperation(value = "查询机构单个属性")
+	@GetMapping("property/{orgId}/{key}")
+	public String getOrgProperty(@PathVariable Long orgId, @PathVariable String key) {
+		OrgCacheBean org = CacheHelper.getOrg(orgId);
+		validateRootOrgIsolation(org.getRootId());
+
+		OrgPropertyCacheBean orgProperty = CacheHelper.getOrgProperty(orgId, key);
+		if (!orgProperty.getHasValue()) {
+			return null;
+		}
+		return orgProperty.getValue();
+	}
+
+	@Naked
+	@ApiOperation(value = "查询机构单个属性(不鉴权)")
+	@GetMapping("propertyNoSession/{key}")
+	public String getOrgPropertyNoSession(@RequestParam(required = false) Long orgId,
+			@RequestParam(required = false) String domainName, @PathVariable String key) {
+		OrgEntity orgEntity = null;
+		if (null != orgId) {
+			orgEntity = GlobalHelper.getEntity(orgRepo, orgId, OrgEntity.class);
+			if (null == orgEntity) {
+				throw new StatusException("001250", "orgId is wrong");
+			}
+		} else if (StringUtils.isNotBlank(domainName)) {
+			orgEntity = orgRepo.findByParentIdIsNullAndDomainName(domainName);
+			if (null == orgEntity) {
+				throw new StatusException("001252", "domainName is wrong");
+			}
+		} else {
+			throw new StatusException("001253", "orgId and domainName are all null");
+		}
+
+		DynamicEnumManager manager = OrgProperty.getDynamicEnumManager();
+		DynamicEnum de = manager.getByName(key);
+		OrgPropertyEntity one = orgPropertyRepo.findByOrgIdAndKeyId(orgEntity.getId(), de.getId());
+		if (null == one) {
+			return null;
+		}
+		return one.getValue();
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param domain
+	 * @return
+	 */
+	@ApiOperation(value = "新增顶级机构", notes = "新增")
+	@PostMapping("addRootOrg")
+	@Transactional
+	public OrgEntity addRootOrg(@RequestBody OrgDomain domain) {
+		trim(domain, true);
+
+		if (!isSuperAdmin()) {
+			throw new StatusException("140001", "非法访问");
+		}
+
+		if (StringUtils.isBlank(domain.getCode())) {
+			throw new StatusException("150001", "code is null");
+		}
+
+		OrgEntity orgEntity = orgRepo.findByParentIdIsNullAndCode(domain.getCode());
+		if (null != orgEntity) {
+			throw new StatusException("150001", "机构代码已存在");
+		}
+
+		OrgInfo info = new OrgInfo();
+		info.setCode(domain.getCode());
+		info.setName(domain.getName());
+		info.setContacts(domain.getContacts());
+		info.setTelephone(domain.getTelephone());
+		info.setDomainName(domain.getDomainName());
+		info.setRemark(domain.getRemark());
+
+		Map<String, String> properties = domain.getProperties();
+		if (null == properties) {
+			properties = Maps.newHashMap();
+		}
+		info.setProperties(properties);
+
+		OrgEntity saved = orgService.saveRootOrg(info);
+		return saved;
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param domain
+	 * @return
+	 */
+	@ApiOperation(value = "更新顶级机构", notes = "更新")
+	@PutMapping("updateRootOrg")
+	@Transactional
+	public OrgEntity updateRootOrg(@RequestBody OrgDomain domain) {
+		trim(domain, true);
+
+		if (!isSuperAdmin()) {
+			throw new StatusException("140001", "非法访问");
+		}
+
+		Long id = domain.getId();
+		if (null == id) {
+			throw new StatusException("140001", "id is null");
+		}
+
+		OrgEntity orgEntity = GlobalHelper.getEntity(orgRepo, id, OrgEntity.class);
+		if (null == orgEntity) {
+			throw new StatusException("140002", "orgEntity is null");
+		}
+
+		validateRootOrgIsolation(orgEntity.getRootId());
+
+		OrgInfo info = new OrgInfo();
+
+		info.setEnable(domain.getEnable());
+		info.setCode(orgEntity.getCode());
+		info.setParentId(orgEntity.getParentId());
+		info.setRootId(orgEntity.getRootId());
+
+		info.setName(domain.getName());
+		info.setContacts(domain.getContacts());
+		info.setTelephone(domain.getTelephone());
+		info.setDomainName(domain.getDomainName());
+		info.setRemark(domain.getRemark());
+
+		Map<String, String> properties = domain.getProperties();
+		if (null == properties) {
+			properties = Maps.newHashMap();
+		}
+		info.setProperties(properties);
+
+		OrgEntity saved = orgService.saveRootOrg(info);
+		return saved;
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param domain
+	 * @return
+	 */
+	@ApiOperation(value = "新增子机构", notes = "新增")
+	@PostMapping("addSubOrg")
+	@Transactional
+	public OrgEntity addSubOrg(@RequestBody OrgDomain domain) {
+		trim(domain, true);
+
+		User accessUser = getAccessUser();
+		Long rootId = domain.getRootId();
+		if (null == rootId) {
+			rootId = accessUser.getRootOrgId();
+		}
+
+		validateRootOrgIsolation(rootId);
+
+		OrgEntity subOrg = orgRepo.findByRootIdAndCode(accessUser.getRootOrgId(), domain.getCode());
+		if (null != subOrg) {
+			throw new StatusException("140001", "机构代码已存在");
+		}
+
+		Long parentId = domain.getParentId();
+		if (null == parentId) {
+			parentId = rootId;
+		}
+
+		OrgInfo info = new OrgInfo();
+		info.setCode(domain.getCode());
+		info.setName(domain.getName());
+		info.setContacts(domain.getContacts());
+		info.setTelephone(domain.getTelephone());
+		info.setParentId(parentId);
+		info.setRootId(rootId);
+		info.setRemark(domain.getRemark());
+		info.setEnable(domain.getEnable());
+
+		Map<String, String> properties = domain.getProperties();
+		if (null == properties) {
+			properties = Maps.newHashMap();
+		}
+		info.setProperties(properties);
+
+		OrgEntity saved = orgService.saveSubOrg(info);
+		return saved;
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param domain
+	 * @return
+	 */
+	@ApiOperation(value = "更新子机构", notes = "更新")
+	@PutMapping("updateSubOrg")
+	@Transactional
+	public OrgEntity updateSubOrg(@RequestBody OrgDomain domain) {
+		trim(domain, true);
+
+		Long id = domain.getId();
+		if (null == id) {
+			throw new StatusException("140001", "id is null");
+		}
+
+		OrgEntity orgEntity = GlobalHelper.getEntity(orgRepo, id, OrgEntity.class);
+		if (null == orgEntity) {
+			throw new StatusException("140002", "orgEntity is null");
+		}
+
+		validateRootOrgIsolation(orgEntity.getRootId());
+
+		OrgInfo info = new OrgInfo();
+		info.setCode(orgEntity.getCode());
+		info.setParentId(orgEntity.getParentId());
+		info.setRootId(orgEntity.getRootId());
+
+		info.setName(domain.getName());
+		info.setContacts(domain.getContacts());
+		info.setTelephone(domain.getTelephone());
+		info.setRemark(domain.getRemark());
+		info.setEnable(domain.getEnable());
+
+		Map<String, String> properties = domain.getProperties();
+		if (null == properties) {
+			properties = Maps.newHashMap();
+		}
+		info.setProperties(properties);
+
+		OrgEntity saved = orgService.saveSubOrg(info);
+		return saved;
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param ids
+	 */
+	@ApiOperation(value = "启用机构", notes = "启用")
+	@PutMapping("enable/{ids}")
+	public void enableOrgs(@PathVariable String ids) {
+		List<Long> orgIdList = Stream.of(ids.split(",")).map(s -> Long.parseLong(s.trim()))
+				.collect(Collectors.toList());
+		setOrgsEnable(orgIdList, true);
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param ids
+	 */
+	@ApiOperation(value = "禁用机构", notes = "禁用")
+	@PutMapping("disable/{ids}")
+	@Transactional
+	public void disableOrgs(@PathVariable String ids) {
+		List<Long> orgIdList = Stream.of(ids.split(",")).map(s -> Long.parseLong(s.trim()))
+				.collect(Collectors.toList());
+		setOrgsEnable(orgIdList, false);
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param orgIdList
+	 * @param enable
+	 */
+	private void setOrgsEnable(List<Long> orgIdList, Boolean enable) {
+		List<OrgEntity> savedList = Lists.newArrayList();
+		for (Long orgId : orgIdList) {
+			OrgEntity org = GlobalHelper.getEntity(orgRepo, orgId, OrgEntity.class);
+			if (null == org.getParentId()) {
+				if (!isSuperAdmin()) {
+					throw new StatusException("001550", "非法操作");
+				}
+			}
+			validateRootOrgIsolation(org.getRootId());
+			org.setEnable(enable);
+			OrgEntity saved = orgRepo.saveAndFlush(org);
+			savedList.add(saved);
+		}
+
+		for (OrgEntity saved : savedList) {
+			SyncOrgReq req = new SyncOrgReq();
+			req.setEnable(saved.getEnable());
+			req.setId(saved.getId());
+			req.setName(saved.getName());
+			req.setParentId(saved.getParentId());
+			req.setRootId(saved.getRootId());
+			req.setSyncType("update");
+			dataSyncCloudService.syncOrg(req);
+		}
+
+		for (OrgEntity saved : savedList) {
+			orgCache.remove(saved.getId());
+			if (null != saved.getDomainName()) {
+				rootOrgCache.remove(saved.getDomainName());
+			}
+		}
+	}
+
+	@ApiOperation(value = "保存机构属性", notes = "")
+	@PutMapping("saveOrgProperties")
+	@Transactional
+	public void saveOrgProperties(@RequestBody SaveOrgPropertiesDomain domain) {
+		Long orgId = domain.getOrgId();
+		OrgCacheBean org = CacheHelper.getOrg(orgId);
+
+		Map<String, String> properties = domain.getProperties();
+
+		if (!isSuperAdmin()) {
+			String editableProperties4SuperAdmin = (String) systemPropertyService
+					.get("editableProperties4SuperAdmin");
+			if (StringUtils.isNotBlank(editableProperties4SuperAdmin)) {
+				editableProperties4SuperAdmin = "," + editableProperties4SuperAdmin + ",";
+				for (String cur : properties.keySet()) {
+					cur = "," + cur + ",";
+					if (editableProperties4SuperAdmin.contains(cur)) {
+						throw new StatusException("140050", "非法操作");
+					}
+				}
+			}
+		}
+
+		properties.put("ROOT_ORG_ID", String.valueOf(org.getRootId()));
+
+		List<String> relatedPropertyGroupIdList = domain.getRelatedPropertyGroupIdList();
+
+		OrgEntity orgEntity = GlobalHelper.getPresentEntity(orgRepo, orgId, OrgEntity.class);
+
+		validateRootOrgIsolation(orgEntity.getRootId());
+
+		orgService.saveOrgProperties(orgEntity.getId(), properties);
+
+		if (CollectionUtils.isNotEmpty(relatedPropertyGroupIdList)) {
+			for (String propertyGroupId : relatedPropertyGroupIdList) {
+				// 删除缓存
+				String redisKey = "PROPERTIES_BY_GROUP:" + orgId + ":" + propertyGroupId;
+				redisClient.delete(redisKey);
+
+				// 上传upyun
+				uploadOrgProperties2Upyun(orgId, propertyGroupId);
+			}
+		}
+
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param domain
+	 * @throws IOException
+	 */
+	@Naked
+	@ApiOperation(value = "获取logo")
+	@GetMapping("logo")
+	public void getLogo(@RequestParam("domain") String domain, HttpServletResponse response)
+			throws IOException {
+		OrgEntity org = orgRepo.findByParentIdIsNullAndDomainName(domain);
+		if (null == org) {
+			throw new StatusException("140002", "orgEntity is null");
+		}
+
+		Long orgId = org.getId();
+
+		DynamicEnumManager manager = OrgProperty.getDynamicEnumManager();
+
+		DynamicEnum logoFileSuffix = manager.getByName("LOGO_FILE_SUFFIX");
+
+		OrgPropertyEntity fileSuffixEntity = orgPropertyRepo.findByOrgIdAndKeyId(orgId,
+				logoFileSuffix.getId());
+
+		if (null == fileSuffixEntity) {
+			throw new StatusException("140003", "no logo");
+		}
+
+		String path = systemConfig.getTempDataDir() + "/logo/" + orgId
+				+ fileSuffixEntity.getValue();
+
+		File file = new File(path);
+
+		if (file.exists()) {
+			FileInputStream in = null;
+			try {
+				in = new FileInputStream(file);
+				IOUtils.copy(in, response.getOutputStream());
+				response.flushBuffer();
+			} finally {
+				IOUtils.closeQuietly(in);
+			}
+			return;
+		}
+
+		DynamicEnum logoFile = manager.getByName("LOGO_FILE");
+		OrgPropertyEntity fileEntity = orgPropertyRepo.findByOrgIdAndKeyId(orgId, logoFile.getId());
+
+		byte[] decodeHex = null;;
+		try {
+			decodeHex = Hex.decodeHex(fileEntity.getValue());
+		} catch (DecoderException e) {
+			throw new ExamCloudRuntimeException(e);
+		}
+
+		FileUtils.writeByteArrayToFile(new File(path), decodeHex);
+
+		if (file.exists()) {
+			FileInputStream in = null;
+			try {
+				in = new FileInputStream(file);
+				IOUtils.copy(in, response.getOutputStream());
+				response.flushBuffer();
+			} finally {
+				IOUtils.closeQuietly(in);
+			}
+			return;
+		}
+
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param request
+	 * @param file
+	 * @throws IOException
+	 */
+	@ApiOperation(value = "导入logo", notes = "导入logo")
+	@PostMapping("importLogo/{orgId}")
+	@Transactional
+	public String importLogo(@PathVariable Long orgId, HttpServletRequest request,
+			@RequestParam CommonsMultipartFile file) throws IOException {
+
+		OrgEntity orgEntity = GlobalHelper.getEntity(orgRepo, orgId, OrgEntity.class);
+		if (null == orgEntity) {
+			throw new StatusException("140002", "orgEntity is null");
+		}
+
+		validateRootOrgIsolation(orgEntity.getRootId());
+
+		DiskFileItem fileItem = (DiskFileItem) file.getFileItem();
+		File storeLocation = fileItem.getStoreLocation();
+		String name = file.getOriginalFilename();
+
+		System.out.println(storeLocation.length());
+		if (999999 < storeLocation.length()) {
+			throw new StatusException("140082", "文件过大");
+		}
+
+		String fileSuffix = null;
+		if (name.endsWith(".jpg")) {
+			fileSuffix = ".jpg";
+		} else if (name.endsWith(".gif")) {
+			fileSuffix = ".gif";
+		} else if (name.endsWith(".png")) {
+			fileSuffix = ".png";
+		} else {
+			throw new StatusException("101001", "文件格式错误");
+		}
+
+		byte[] byteArray = null;
+		InputStream inputStream = null;
+		try {
+			inputStream = new FileInputStream(storeLocation);
+			byteArray = IOUtils.toByteArray(inputStream);
+		} finally {
+			IOUtils.closeQuietly(inputStream);
+		}
+
+		String hexString = Hex.encodeHexString(byteArray);
+
+		DynamicEnumManager manager = OrgProperty.getDynamicEnumManager();
+		DynamicEnum logoFile = manager.getByName("LOGO_FILE");
+		DynamicEnum logoFileSuffix = manager.getByName("LOGO_FILE_SUFFIX");
+		DynamicEnum logoFileUrl = manager.getByName("LOGO_FILE_URL");
+
+		OrgPropertyEntity fileEntity = orgPropertyRepo.findByOrgIdAndKeyId(orgId, logoFile.getId());
+		if (null == fileEntity) {
+			fileEntity = new OrgPropertyEntity();
+			fileEntity.setKeyId(logoFile.getId());
+			fileEntity.setOrgId(orgId);
+		}
+		fileEntity.setValue(hexString);
+		orgPropertyRepo.save(fileEntity);
+
+		OrgPropertyEntity fileSuffixEntity = orgPropertyRepo.findByOrgIdAndKeyId(orgId,
+				logoFileSuffix.getId());
+		if (null == fileSuffixEntity) {
+			fileSuffixEntity = new OrgPropertyEntity();
+			fileSuffixEntity.setKeyId(logoFileSuffix.getId());
+			fileSuffixEntity.setOrgId(orgId);
+		}
+		fileSuffixEntity.setValue(fileSuffix);
+		orgPropertyRepo.save(fileSuffixEntity);
+
+		String path = systemConfig.getTempDataDir() + "/logo/" + orgId
+				+ fileSuffixEntity.getValue();
+
+		FileUtils.copyFile(storeLocation, new File(path));
+
+//		PutFileReq req = new PutFileReq();
+//		List<FormFilePart> formFilePartList = new ArrayList<FormFilePart>();
+//		FormFilePart part = new FormFilePart("file", orgId + fileSuffix, storeLocation);
+//		formFilePartList.add(part);
+//
+//		req.setFormFilePartList(formFilePartList);
+//		req.setSiteId("orgLogo");
+//		req.setFileSuffix(fileSuffix);
+//		req.setRootOrgId(orgEntity.getRootId());
+//		PutFileResp putFileResp = upyunCloudService.putFile(req);
+//		String url = putFileResp.getUrl();
+		
+		//通用存储
+		FileStoragePathEnvInfo env=new FileStoragePathEnvInfo();
+		env.setFileSuffix(fileSuffix);
+		env.setRootOrgId(orgEntity.getRootId().toString());
+		YunPathInfo pi=FileStorageUtil.saveFile("orgLogo", env, storeLocation,null);
+		String url =pi.getUrl();
+
+		OrgPropertyEntity logoFileUrlEntity = orgPropertyRepo.findByOrgIdAndKeyId(orgId,
+				logoFileUrl.getId());
+		if (null == logoFileUrlEntity) {
+			logoFileUrlEntity = new OrgPropertyEntity();
+			logoFileUrlEntity.setKeyId(logoFileUrl.getId());
+			logoFileUrlEntity.setOrgId(orgId);
+		}
+		logoFileUrlEntity.setValue(url);
+		orgPropertyRepo.save(logoFileUrlEntity);
+
+		return url;
+	}
+
+	@ApiOperation(value = "下载导入模板", notes = "下载导入模板")
+	@GetMapping("importTemplate")
+	public void getDownloadTemplate(HttpServletResponse response) {
+		String resoucePath = PathUtil.getResoucePath("templates/subOrgImportTemplate.xlsx");
+		exportFile("学习中心导入模板.xlsx", new File(resoucePath));
+	}
+
+	@ApiOperation(value = "导入子机构", notes = "")
+	@PostMapping("importSubOrg")
+	@Transactional
+	public Map<String, Object> importSubOrg(@RequestParam CommonsMultipartFile file) {
+		DiskFileItem item = (DiskFileItem) file.getFileItem();
+		File storeLocation = item.getStoreLocation();
+		List<Map<String, Object>> failRecords = orgService.importSubOrg(getRootOrgId(),
+				storeLocation);
+		Map<String, Object> map = Maps.newHashMap();
+		map.put("hasError", CollectionUtils.isNotEmpty(failRecords));
+		map.put("failRecords", failRecords);
+		return map;
+	}
+
+	/**
+	 * 导入自定义菜单logo
+	 *
+	 * @author lideyin
+	 * @param request
+	 * @param file
+	 * @throws IOException
+	 */
+	@ApiOperation(value = "导入自定义菜单logo", notes = "导入自定义菜单logo")
+	@PostMapping("importCusMenuLogo/{orgId}")
+	@Transactional
+	public String importCusMenuLogo(@PathVariable Long orgId, HttpServletRequest request,
+							 @RequestParam CommonsMultipartFile file) throws IOException {
+
+		OrgEntity orgEntity = GlobalHelper.getEntity(orgRepo, orgId, OrgEntity.class);
+		if (null == orgEntity) {
+			throw new StatusException("140002", "orgEntity is null");
+		}
+
+		validateRootOrgIsolation(orgEntity.getRootId());
+
+		DiskFileItem fileItem = (DiskFileItem) file.getFileItem();
+		File storeLocation = fileItem.getStoreLocation();
+		String name = file.getOriginalFilename();
+
+		System.out.println(storeLocation.length());
+		if (999999 < storeLocation.length()) {
+			throw new StatusException("140082", "文件过大");
+		}
+
+		String fileSuffix = null;
+		if (name.endsWith(".jpg")) {
+			fileSuffix = ".jpg";
+		} else if (name.endsWith(".gif")) {
+			fileSuffix = ".gif";
+		} else if (name.endsWith(".png")) {
+			fileSuffix = ".png";
+		} else {
+			throw new StatusException("101001", "文件格式错误");
+		}
+
+		byte[] byteArray = null;
+		InputStream inputStream = null;
+		try {
+			inputStream = new FileInputStream(storeLocation);
+			byteArray = IOUtils.toByteArray(inputStream);
+		} finally {
+			IOUtils.closeQuietly(inputStream);
+		}
+
+		String hexString = Hex.encodeHexString(byteArray);
+
+		DynamicEnumManager manager = OrgProperty.getDynamicEnumManager();
+		DynamicEnum logoFile = manager.getByName("CUS_MENU_LOGO_FILE");
+		DynamicEnum logoFileSuffix = manager.getByName("CUS_MENU_LOGO_FILE_SUFFIX");
+		DynamicEnum logoFileUrl = manager.getByName("CUS_MENU_LOGO_FILE_URL");
+
+		OrgPropertyEntity fileEntity = orgPropertyRepo.findByOrgIdAndKeyId(orgId, logoFile.getId());
+		if (null == fileEntity) {
+			fileEntity = new OrgPropertyEntity();
+			fileEntity.setKeyId(logoFile.getId());
+			fileEntity.setOrgId(orgId);
+		}
+		fileEntity.setValue(hexString);
+		orgPropertyRepo.save(fileEntity);
+
+		OrgPropertyEntity fileSuffixEntity = orgPropertyRepo.findByOrgIdAndKeyId(orgId,
+				logoFileSuffix.getId());
+		if (null == fileSuffixEntity) {
+			fileSuffixEntity = new OrgPropertyEntity();
+			fileSuffixEntity.setKeyId(logoFileSuffix.getId());
+			fileSuffixEntity.setOrgId(orgId);
+		}
+		fileSuffixEntity.setValue(fileSuffix);
+		orgPropertyRepo.save(fileSuffixEntity);
+
+		String path = systemConfig.getTempDataDir() + "/cusMenuLogo/" + orgId
+				+ fileSuffixEntity.getValue();
+
+		FileUtils.copyFile(storeLocation, new File(path));
+
+		//通用存储
+		FileStoragePathEnvInfo env=new FileStoragePathEnvInfo();
+		env.setFileSuffix(fileSuffix);
+		env.setRootOrgId(orgEntity.getRootId().toString());
+		YunPathInfo pi=FileStorageUtil.saveFile("orgLogo", env, storeLocation,null);
+		String url =pi.getUrl();
+
+		OrgPropertyEntity logoFileUrlEntity = orgPropertyRepo.findByOrgIdAndKeyId(orgId,
+				logoFileUrl.getId());
+		if (null == logoFileUrlEntity) {
+			logoFileUrlEntity = new OrgPropertyEntity();
+			logoFileUrlEntity.setKeyId(logoFileUrl.getId());
+			logoFileUrlEntity.setOrgId(orgId);
+		}
+		logoFileUrlEntity.setValue(url);
+		orgPropertyRepo.save(logoFileUrlEntity);
+
+		return url;
+	}
+}

+ 194 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/ResourceController.java

@@ -0,0 +1,194 @@
+package cn.com.qmth.examcloud.core.basic.api.controller;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Optional;
+import java.util.regex.Pattern;
+
+import javax.persistence.criteria.Predicate;
+import javax.validation.constraints.NotNull;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.domain.Sort.Direction;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.transaction.annotation.Transactional;
+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.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RequestPart;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+import com.google.common.collect.Lists;
+
+import cn.com.qmth.examcloud.api.commons.exchange.PageInfo;
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.basic.api.controller.bean.ResourceDomain;
+import cn.com.qmth.examcloud.core.basic.dao.ResourceRepo;
+import cn.com.qmth.examcloud.core.basic.dao.entity.ResourceEntity;
+import cn.com.qmth.examcloud.core.basic.service.ResourceService;
+import cn.com.qmth.examcloud.core.basic.service.bean.ResourceInfo;
+import cn.com.qmth.examcloud.support.filestorage.FileStorageUtil;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+@Api(tags = "资源管理")
+@RestController
+@RequestMapping("${$rmp.ctr.basic}/resource")
+public class ResourceController extends ControllerSupport {
+
+	@Autowired
+	private ResourceRepo resourceRepo;
+
+	@Autowired
+	private ResourceService resourceService;
+
+	@ApiOperation(value = "分页查询资源")
+	@GetMapping("page/{pageNo}/{pageSize}")
+	public PageInfo<ResourceDomain> getPage(@PathVariable Integer pageNo,
+			@PathVariable Integer pageSize, @RequestParam(required = false) String name,
+			@RequestParam(required = false) Long parentId,
+			@RequestParam(required = true) Long rootOrgId) {
+
+		if (!isSuperAdmin()) {
+			User accessUser = getAccessUser();
+			if (!rootOrgId.equals(accessUser.getRootOrgId())) {
+				throw new StatusException("100000", "无效的请求");
+			}
+		}
+
+		Specification<ResourceEntity> specification = (root, query, cb) -> {
+			List<Predicate> predicates = new ArrayList<>();
+
+			predicates.add(cb.equal(root.get("rootOrgId"), rootOrgId));
+
+			if (StringUtils.isNotBlank(name)) {
+				predicates.add(cb.like(root.get("name"), toSqlSearchPattern(name)));
+			}
+			if (null == parentId || -1 == parentId) {
+				predicates.add(cb.isNull(root.get("parentId")));
+			} else {
+				predicates.add(cb.equal(root.get("parentId"), parentId));
+			}
+
+			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+		};
+		PageRequest pageRequest = PageRequest.of(pageNo, pageSize,
+				new Sort(Direction.ASC, "isFile").and(new Sort(Direction.DESC, "creationTime")));
+
+		Page<ResourceEntity> page = resourceRepo.findAll(specification, pageRequest);
+		Iterator<ResourceEntity> iterator = page.iterator();
+
+		List<ResourceDomain> resourceDomainList = Lists.newArrayList();
+
+//		String upyunFileUrl = PropertyHolder.getString("$upyun.site.1.domain");
+		while (iterator.hasNext()) {
+			ResourceEntity next = iterator.next();
+			ResourceDomain bean = new ResourceDomain();
+			BeanUtils.copyProperties(next, bean);
+//			bean.setFileUrl(upyunFileUrl + bean.getFilePath());
+			//通用存储
+			bean.setFileUrl(FileStorageUtil.realPath(bean.getFileTreatyPath()));
+			resourceDomainList.add(bean);
+		}
+		PageInfo<ResourceDomain> ret = new PageInfo<ResourceDomain>();
+		ret.setList(resourceDomainList);
+		ret.setTotal(page.getTotalElements());
+		return ret;
+	}
+
+	@ApiOperation(value = "新增资源")
+	@PostMapping("addFile")
+	@Transactional
+	public void addResource(@RequestParam @NotNull(message = "RootOrgId不能为空!") Long rootOrgId,
+			@RequestParam @NotNull(message = "parentId不能为空!") Long parentId,
+			@RequestPart @NotNull(message = "上传文件不能为空!") MultipartFile dataFile) {
+
+		if (-1 == parentId) {
+			parentId = null;
+		}
+
+		if (!isSuperAdmin()) {
+			User accessUser = getAccessUser();
+			if (!rootOrgId.equals(accessUser.getRootOrgId())) {
+				throw new StatusException("100021", "无效的请求");
+			}
+		}
+		String fileName = dataFile.getOriginalFilename();
+		String regex = "^[a-zA-Z\\-_0-9.]{1,50}$";
+		if (!Pattern.matches(regex, fileName)) {
+			throw new StatusException("100022", "文件名长度最大为50且只能包含字母,数字,'-','_','.'");
+		}
+		resourceService.addFile(rootOrgId, parentId, dataFile);
+	}
+
+	@ApiOperation(value = "删除资源")
+	@DeleteMapping("/{id}")
+	@Transactional
+	public void delResource(@PathVariable Long id) {
+		Optional<ResourceEntity> op = resourceRepo.findById(id);
+		if (op.isPresent()) {
+			ResourceEntity e = op.get();
+			if (!isSuperAdmin()) {
+				User accessUser = getAccessUser();
+				if (!e.getRootOrgId().equals(accessUser.getRootOrgId())) {
+					throw new StatusException("100031", "无效的请求");
+				}
+			}
+			if (!e.getIsFile()
+					&& resourceRepo.countByRootOrgIdAndParentId(e.getRootOrgId(), id) > 0) {
+				throw new StatusException("100032", "请先删除该目录下的文件及目录");
+			}
+			resourceRepo.deleteById(id);
+		} else {
+			throw new StatusException("100033", "文件或目录不存在");
+
+		}
+	}
+
+	@ApiOperation(value = "新增目录")
+	@PostMapping("addDir")
+	@Transactional
+	public void addDir(@RequestBody ResourceDomain domain) {
+		if (StringUtils.isBlank(domain.getName())) {
+			throw new StatusException("100001", "目录名称不能为空");
+		}
+		String regex = "^[a-zA-Z\\-_0-9]{1,50}$";
+		if (!Pattern.matches(regex, domain.getName())) {
+			throw new StatusException("100005", "目录名称必须为1-50位字母,数字,'-','_'组合");
+		}
+		if (domain.getRootOrgId() == null) {
+			throw new StatusException("100002", "RootOrgId不能为空");
+		}
+		if (domain.getParentId() == null) {
+			throw new StatusException("100003", "ParentId不能为空");
+		}
+		if (!isSuperAdmin()) {
+			User accessUser = getAccessUser();
+			if (!domain.getRootOrgId().equals(accessUser.getRootOrgId())) {
+				throw new StatusException("100004", "无效的请求");
+			}
+		}
+		Long parentId = domain.getParentId();
+		if (-1 == parentId) {
+			domain.setParentId(null);
+		}
+
+		ResourceInfo info = new ResourceInfo();
+		BeanUtils.copyProperties(domain, info);
+		resourceService.addDir(info);
+	}
+
+}

+ 804 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/RolePrivilegeController.java

@@ -0,0 +1,804 @@
+package cn.com.qmth.examcloud.core.basic.api.controller;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import cn.com.qmth.examcloud.core.basic.api.controller.bean.*;
+import org.apache.commons.collections.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+import cn.com.qmth.examcloud.api.commons.enums.PrivilegeGroupType;
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.api.commons.security.enums.RoleMeta;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.commons.util.RegExpUtil;
+import cn.com.qmth.examcloud.core.basic.api.bean.RoleBean;
+import cn.com.qmth.examcloud.core.basic.base.constants.BasicConsts;
+import cn.com.qmth.examcloud.core.basic.dao.PrivilegeGroupRepo;
+import cn.com.qmth.examcloud.core.basic.dao.PrivilegeRepo;
+import cn.com.qmth.examcloud.core.basic.dao.RolePrivilegeRelationRepo;
+import cn.com.qmth.examcloud.core.basic.dao.RoleRepo;
+import cn.com.qmth.examcloud.core.basic.dao.RootOrgPrivilegeRelationRepo;
+import cn.com.qmth.examcloud.core.basic.dao.entity.PrivilegeEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.PrivilegeGroupEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.RoleEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.RolePrivilegeRelationEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.RootOrgPrivilegeRelationEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.RootOrgPrivilegeRelationPK;
+import cn.com.qmth.examcloud.core.basic.service.RolePrivilegeService;
+import cn.com.qmth.examcloud.core.basic.service.bean.RoleInfo;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+import cn.com.qmth.examcloud.web.helpers.tree.EleTreeNode;
+import cn.com.qmth.examcloud.web.helpers.tree.TreeUtil;
+import cn.com.qmth.examcloud.web.interceptor.GlobalSequenceLock;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * 角色权限
+ *
+ * @author WANGWEI
+ * @date 2018年6月12日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@Api("角色权限")
+@RestController
+@RequestMapping("${$rmp.ctr.basic}/rolePrivilege")
+public class RolePrivilegeController extends ControllerSupport {
+
+	private static List<String> disabledPrivilegeCodeList = Arrays
+			.asList(new String[]{"index_privilege_group_list", "index_app_list", "index_school",
+					"school_config", "index_school_privilege_settings", "system"});
+
+	@Autowired
+	PrivilegeGroupRepo privilegeGroupRepo;
+
+	@Autowired
+	PrivilegeRepo privilegeRepo;
+
+	@Autowired
+	RoleRepo roleRepo;
+
+	@Autowired
+	RolePrivilegeRelationRepo rolePrivilegeRelationRepo;
+
+	@Autowired
+	RootOrgPrivilegeRelationRepo rootOrgPrivilegeRelationRepo;
+
+	@Autowired
+	RolePrivilegeService rolePrivilegeService;
+
+	@ApiOperation(value = "查询角色", notes = "")
+	@PostMapping("getRoles")
+	public List<RoleBean> getRoles(@RequestParam Boolean includeSuperAdmin,
+			@RequestParam(required = false) Long rootOrgId) {
+		User accessUser = getAccessUser();
+
+		List<RoleEntity> roleList = roleRepo.findByRootOrgIdIsNull();
+
+		if (null != rootOrgId) {
+			validateRootOrgIsolation(rootOrgId);
+			List<RoleEntity> rootOrgRoleList = roleRepo.findByRootOrgId(rootOrgId);
+			roleList.addAll(rootOrgRoleList);
+		} else {
+			List<RoleEntity> rootOrgRoleList = roleRepo.findByRootOrgId(accessUser.getRootOrgId());
+			roleList.addAll(rootOrgRoleList);
+		}
+		List<RoleBean> roleBeanList = Lists.newArrayList();
+
+		for (RoleEntity cur : roleList) {
+
+			if ((!includeSuperAdmin) && cur.getCode().equals(RoleMeta.SUPER_ADMIN.name())) {
+				continue;
+			}
+
+			RoleBean bean = new RoleBean();
+			bean.setRoleId(cur.getId());
+			bean.setRoleName(cur.getName());
+			bean.setRoleCode(cur.getCode());
+			bean.setRootOrgId(cur.getRootOrgId());
+
+			roleBeanList.add(bean);
+		}
+
+		return roleBeanList;
+	}
+
+	@ApiOperation(value = "查询用户的权限树", notes = "")
+	@PostMapping("getUserPrivileges")
+	public List<PrivilegeDomain> getPrivileges(@RequestParam String groupCode,
+			@RequestParam boolean full) {
+
+		User accessUser = getAccessUser();
+		Long rootOrgId = accessUser.getRootOrgId();
+
+		PrivilegeGroupEntity privilegeGroup = privilegeGroupRepo.findByCode(groupCode);
+
+		if (null == privilegeGroup) {
+			throw new StatusException("002001", "groupCode is not existing");
+		}
+
+		List<Long> roleIdList = getAccessUserRoleIdList();
+
+		List<RolePrivilegeRelationEntity> rolePrivRelationList = rolePrivilegeRelationRepo
+				.findAllByRoleIdInAndRootOrgId(roleIdList, rootOrgId);
+
+		Set<String> pIdSet = Sets.newHashSet();
+		for (RolePrivilegeRelationEntity cur : rolePrivRelationList) {
+			pIdSet.add(String.valueOf(cur.getPrivilegeId()));
+		}
+
+		List<PrivilegeEntity> privilegeList = privilegeRepo
+				.findAllByGroupIdOrderByWeightDesc(privilegeGroup.getId());
+
+		List<PrivilegeDomain> privilegeInfoList = Lists.newArrayList();
+
+		for (PrivilegeEntity cur : privilegeList) {
+			boolean hasPrivilege = pIdSet.contains(String.valueOf(cur.getId()));
+			// 超级管理员拥有所有权限
+			if (isSuperAdmin()) {
+				hasPrivilege = true;
+			} else if (disabledPrivilegeCodeList.contains(cur.getCode())) {
+				hasPrivilege = false;
+			}
+			if ((!full) && (!hasPrivilege)) {
+				continue;
+			}
+
+			PrivilegeDomain privilegeInfo = new PrivilegeDomain();
+			privilegeInfo.setHasPrivilege(hasPrivilege);
+			privilegeInfo.setCode(cur.getCode());
+			privilegeInfo.setCreationTime(cur.getCreationTime());
+			privilegeInfo.setGroupId(cur.getGroupId());
+			privilegeInfo.setGroupCode(privilegeGroup.getCode());
+			privilegeInfo.setId(cur.getId());
+			privilegeInfo.setName(cur.getName());
+			privilegeInfo.setParentId(cur.getParentId());
+			privilegeInfo.setUpdateTime(cur.getUpdateTime());
+			privilegeInfo.setDescription(cur.getDescription());
+			privilegeInfo.setWeight(cur.getWeight());
+			privilegeInfo.setExt1(cur.getExt1());
+			privilegeInfo.setExt2(cur.getExt2());
+			privilegeInfo.setExt3(cur.getExt3());
+			privilegeInfo.setExt4(cur.getExt4());
+			privilegeInfo.setExt5(cur.getExt5());
+
+			privilegeInfoList.add(privilegeInfo);
+
+		}
+
+		return privilegeInfoList;
+	}
+
+	@ApiOperation(value = "查询权限组")
+	@GetMapping("getPrivilegeGroupList")
+	public List<PrivilegeGroupDomain> getPrivilegeGroupList() {
+
+		List<PrivilegeGroupEntity> list = privilegeGroupRepo.findAllByRootOrgIdIsNullOrderById();
+
+		List<PrivilegeGroupDomain> ret = Lists.newArrayList();
+
+		if (null != list && !list.isEmpty()) {
+			list = list.stream().filter(p -> PrivilegeGroupType.STUDENT_CLIENT_MENU != p.getType())
+					.collect(Collectors.toList());
+		}
+
+		for (PrivilegeGroupEntity cur : list) {
+			PrivilegeGroupDomain bean = new PrivilegeGroupDomain();
+			bean.setId(cur.getId());
+			bean.setCode(cur.getCode());
+			bean.setName(cur.getName());
+			bean.setExt1(cur.getExt1());
+			bean.setExt2(cur.getExt2());
+			bean.setExt3(cur.getExt3());
+			bean.setExt4(cur.getExt4());
+			bean.setExt5(cur.getExt5());
+
+			ret.add(bean);
+		}
+
+		return ret;
+	}
+
+	@ApiOperation(value = "查询权限树")
+	@GetMapping("getPrivilegeTree/{groupId}")
+	public EleTreeNode getPrivilegeTree(@PathVariable Long groupId,
+			@RequestParam(required = false) Boolean includeDisabledCodes) {
+		PrivilegeGroupEntity group = GlobalHelper.getEntity(privilegeGroupRepo, groupId,
+				PrivilegeGroupEntity.class);
+		List<PrivilegeEntity> privilegeList = privilegeRepo
+				.findAllByGroupIdOrderByWeightDesc(groupId);
+		EleTreeNode rootNode = new EleTreeNode(BasicConsts.ROOT_PRIVILEGE_ID, group.getName());
+
+		if (null != includeDisabledCodes && includeDisabledCodes) {
+			if (!isSuperAdmin()) {
+				throw new StatusException("012001", "非法请求");
+			}
+			TreeUtil.convert2OneEleTreeNode(rootNode, privilegeList, Lists.newArrayList());
+		} else {
+			TreeUtil.convert2OneEleTreeNode(rootNode, privilegeList, disabledPrivilegeCodeList);
+		}
+
+		return rootNode;
+	}
+
+	@ApiOperation(value = "查询顶级机构的权限ID集合")
+	@GetMapping("getRootOrgPrivilegeIdList/{rootOrgId}/{privilegeGroupId}")
+	public List<Long> getRootOrgPrivilegeIdList(@PathVariable Long rootOrgId,
+			@PathVariable Long privilegeGroupId) {
+
+		List<RootOrgPrivilegeRelationEntity> list = rootOrgPrivilegeRelationRepo
+				.findAllByRootOrgIdAndGroupId(rootOrgId, privilegeGroupId);
+
+		List<Long> pList = list.stream().map(RootOrgPrivilegeRelationEntity::getPrivilegeId)
+				.collect(Collectors.toList());
+
+		return pList;
+	}
+
+	/**
+	 * 2020年2月19日
+	 *
+	 * @author WANGWEI
+	 * @param rootOrgId
+	 * @param includeDisabledCodes
+	 * @return
+	 */
+	@ApiOperation(value = "查询菜单树")
+	@GetMapping("getStudentClientMenuTree/{rootOrgId}")
+	@GlobalSequenceLock
+	@Transactional
+	public OrgPrivilegeTreeDomain getStudentClientMenuTree(@PathVariable Long rootOrgId,
+														   @RequestParam(required = false) Boolean includeDisabledCodes) {
+
+		PrivilegeGroupEntity group = privilegeGroupRepo.findByRootOrgIdAndType(rootOrgId,
+				PrivilegeGroupType.STUDENT_CLIENT_MENU);
+
+		//如果考生自定义菜单未初始化,默认初始化一份
+		if (null == group) {
+			PrivilegeGroupEntity globalGroup = privilegeGroupRepo
+					.findByCode(PrivilegeGroupType.STUDENT_CLIENT_MENU.name());
+
+			group = new PrivilegeGroupEntity();
+			group.setCode(PrivilegeGroupType.STUDENT_CLIENT_MENU.name() + "_" + rootOrgId);
+			group.setName(globalGroup.getName());
+			group.setRootOrgId(rootOrgId);
+			group.setType(PrivilegeGroupType.STUDENT_CLIENT_MENU);
+
+			group.setExt1(globalGroup.getExt1());
+			group.setExt2(globalGroup.getExt2());
+			group.setExt3(globalGroup.getExt3());
+			group.setExt4(globalGroup.getExt4());
+			group.setExt5(globalGroup.getExt5());
+
+			group = privilegeGroupRepo.save(group);
+
+			List<PrivilegeEntity> globalPrivilegeList = privilegeRepo
+					.findAllByGroupIdOrderByWeightDesc(globalGroup.getId());
+			Map<Long, Integer> idIndex = Maps.newHashMap();
+			List<PrivilegeEntity> privilegeList = Lists.newArrayList();
+			Set<Long> privilegeIdSet = Sets.newHashSet();
+			int c = 0;
+			for (PrivilegeEntity cur : globalPrivilegeList) {
+				idIndex.put(cur.getId(), c);
+				c++;
+
+				PrivilegeEntity privilegeEntity = new PrivilegeEntity();
+				privilegeEntity.setCode(cur.getCode() + "_" + rootOrgId);
+				privilegeEntity.setDescription(cur.getDescription());
+				privilegeEntity.setGroupId(group.getId());
+				privilegeEntity.setName(cur.getName());
+				privilegeEntity.setWeight(cur.getWeight());
+
+				privilegeEntity.setExt1(cur.getExt1());
+				privilegeEntity.setExt2(cur.getExt2());
+				privilegeEntity.setExt3(cur.getExt3());
+				privilegeEntity.setExt4(cur.getExt4());
+				privilegeEntity.setExt5(cur.getExt5());
+
+				PrivilegeEntity saved = privilegeRepo.save(privilegeEntity);
+				privilegeList.add(saved);
+
+				privilegeIdSet.add(saved.getId());
+
+			}
+
+			for (int i = 0; i < globalPrivilegeList.size(); i++) {
+				PrivilegeEntity p = privilegeList.get(i);
+				if (null == p.getParentId()) {
+					continue;
+				}
+
+				Integer j = idIndex.get(globalPrivilegeList.get(i).getId());
+				p.setParentId(privilegeList.get(j).getId());
+				privilegeRepo.save(p);
+			}
+
+			rolePrivilegeService.updateRootOrgPrivilegeRelations(rootOrgId, group.getId(),
+					privilegeIdSet);
+		}
+
+		List<PrivilegeEntity> privilegeList = privilegeRepo
+				.findAllByGroupIdOrderByWeightDesc(group.getId());
+
+		EleTreeNode rootNode = new EleTreeNode(BasicConsts.ROOT_PRIVILEGE_ID, group.getName());
+
+		if (null != includeDisabledCodes && includeDisabledCodes) {
+			if (!isSuperAdmin()) {
+				throw new StatusException("012001", "非法请求");
+			}
+			TreeUtil.convert2OneEleTreeNode(rootNode, privilegeList, Lists.newArrayList());
+		} else {
+			TreeUtil.convert2OneEleTreeNode(rootNode, privilegeList, disabledPrivilegeCodeList);
+		}
+
+		OrgPrivilegeTreeDomain domain= new OrgPrivilegeTreeDomain();
+		domain.setPrivilegeGroupId(group.getId());
+		domain.setPrivilegeGroupType(group.getType().name());
+		domain.setTreeData(rootNode);
+
+		List<RootOrgPrivilegeRelationEntity> roprlist = rootOrgPrivilegeRelationRepo
+				.findAllByRootOrgIdAndGroupId(rootOrgId, group.getId());
+
+		List<Long> pList = roprlist.stream().map(RootOrgPrivilegeRelationEntity::getPrivilegeId)
+				.collect(Collectors.toList());
+
+		domain.setOwnedPrivilegeIds(pList);
+
+		return domain;
+	}
+
+	/**
+	 * 2020年2月11日
+	 *
+	 * @author WANGWEI
+	 * @param rootOrgId
+	 * @return
+	 */
+	@ApiOperation(value = "查询学生端菜单")
+	@GetMapping("getStudentClientMenu")
+	public List<CustomPrivilegeDomain> getStudentClientMenu(
+			@RequestParam(required = false) Long rootOrgId) {
+
+		if (null == rootOrgId) {
+			rootOrgId = getRootOrgId();
+		} else {
+			validateRootOrgIsolation(rootOrgId);
+		}
+
+		String code = PrivilegeGroupType.STUDENT_CLIENT_MENU.name() + "_" + rootOrgId;
+
+		boolean undefined = false;
+
+		PrivilegeGroupEntity privilegeGroup = privilegeGroupRepo.findByCode(code);
+
+		if (null == privilegeGroup) {
+			privilegeGroup = privilegeGroupRepo
+					.findByCode(PrivilegeGroupType.STUDENT_CLIENT_MENU.name());
+			undefined = true;
+		} else {
+			if (null == privilegeGroup.getRootOrgId()) {
+				throw new StatusException("002101", "data error");
+			}
+			GlobalHelper.uniformRootOrg(rootOrgId, privilegeGroup.getRootOrgId());
+		}
+
+		List<Long> pList = null;
+		if (!undefined) {
+			List<RootOrgPrivilegeRelationEntity> list = rootOrgPrivilegeRelationRepo
+					.findAllByRootOrgIdAndGroupId(rootOrgId, privilegeGroup.getId());
+
+			pList = list.stream().map(RootOrgPrivilegeRelationEntity::getPrivilegeId)
+					.collect(Collectors.toList());
+		}
+
+		List<PrivilegeEntity> privilegeList = privilegeRepo
+				.findAllByGroupId(privilegeGroup.getId());
+
+		List<CustomPrivilegeDomain> privilegeInfoList = Lists.newArrayList();
+
+		for (PrivilegeEntity cur : privilegeList) {
+			boolean hasPrivilege = undefined ? true : (pList!=null && pList.contains(cur.getId()));
+
+			if (!hasPrivilege) {
+				continue;
+			}
+
+			CustomPrivilegeDomain privilegeInfo = new CustomPrivilegeDomain();
+			privilegeInfo.setHasPrivilege(hasPrivilege);
+			privilegeInfo.setCode(cur.getCode());
+			privilegeInfo.setCreationTime(cur.getCreationTime());
+			privilegeInfo.setGroupId(cur.getGroupId());
+			privilegeInfo.setGroupCode(privilegeGroup.getCode());
+			privilegeInfo.setId(cur.getId());
+			privilegeInfo.setName(cur.getName());
+			privilegeInfo.setParentId(cur.getParentId());
+			privilegeInfo.setUpdateTime(cur.getUpdateTime());
+			privilegeInfo.setDescription(cur.getDescription());
+			privilegeInfo.setWeight(cur.getWeight());
+			privilegeInfo.setExt1(cur.getExt1());
+			privilegeInfo.setExt2(cur.getExt2());
+			privilegeInfo.setExt3(cur.getExt3());
+			privilegeInfo.setExt4(cur.getExt4());
+			privilegeInfo.setExt5(cur.getExt5());
+
+			//如果未自定义过菜单,则routeCode和权限编码一致
+			String routeCode;
+			if (undefined) {
+				routeCode=cur.getCode();
+			}
+			//如果自定义过菜单,则特殊处理
+			else{
+				routeCode= cur.getCode().replace("_"+rootOrgId,"");
+			}
+			privilegeInfo.setRouteCode(routeCode);
+
+			privilegeInfoList.add(privilegeInfo);
+
+		}
+
+		return privilegeInfoList;
+	}
+
+	@ApiOperation(value = "查询角色的权限ID集合")
+	@GetMapping("getPrivilegeIdList/{rootOrgId}/{roleId}")
+	public Set<Long> getPrivilegeIdList(@PathVariable Long rootOrgId, @PathVariable Long roleId) {
+		List<RolePrivilegeRelationEntity> rolePrivRelationList = rolePrivilegeRelationRepo
+				.findAllByRoleIdAndRootOrgId(roleId, rootOrgId);
+
+		Set<Long> pIdSet = Sets.newHashSet();
+		for (RolePrivilegeRelationEntity cur : rolePrivRelationList) {
+			pIdSet.add(cur.getPrivilegeId());
+		}
+
+		return pIdSet;
+	}
+
+	@ApiOperation(value = "查询角色的权限ID集合(限制权限组)")
+	@GetMapping("getPrivilegeIdList/{rootOrgId}/{roleId}/{privilegeGroupId}")
+	public Set<Long> getPrivilegeIdList(@PathVariable Long rootOrgId, @PathVariable Long roleId,
+			@PathVariable Long privilegeGroupId) {
+		List<RolePrivilegeRelationEntity> rolePrivRelationList = rolePrivilegeRelationRepo
+				.findAllByRoleIdAndRootOrgId(roleId, rootOrgId);
+
+		Set<Long> pIdSet = Sets.newHashSet();
+		for (RolePrivilegeRelationEntity cur : rolePrivRelationList) {
+			PrivilegeEntity privilegeEntity = GlobalHelper.getPresentEntity(privilegeRepo,
+					cur.getPrivilegeId(), PrivilegeEntity.class);
+			if (privilegeEntity.getGroupId().equals(privilegeGroupId)) {
+				pIdSet.add(cur.getPrivilegeId());
+			}
+		}
+
+		return pIdSet;
+	}
+
+	@ApiOperation(value = "更新角色权限关联")
+	@PostMapping("updateRolePrivilegeRelations")
+	@Transactional
+	public void updateRolePrivilegeRelations(@RequestBody UpdateRolePrivilegeRelationsDomain req) {
+		Long rootOrgId = Long.parseLong(req.getRootOrgId());
+		Long roleId = Long.parseLong(req.getRoleId());
+		Long privilegeGroupId = Long.parseLong(req.getPrivilegeGroupId());
+		Set<Long> privilegeIdSet = req.getPrivilegeIdSet();
+
+		User accessUser = getAccessUser();
+		Long userRootOrgId = accessUser.getRootOrgId();
+		if ((!rootOrgId.equals(userRootOrgId)) && (!isSuperAdmin())) {
+			throw new StatusException("012001", "非法请求");
+		}
+
+		rolePrivilegeService.updateRolePrivilegeRelations(rootOrgId, roleId, privilegeGroupId,
+				privilegeIdSet);
+	}
+
+	@ApiOperation(value = "更新顶级机构权限关联")
+	@PostMapping("updateRootOrgPrivilegeRelations")
+	@Transactional
+	public void updateRootOrgPrivilegeRelations(
+			@RequestBody UpdateRootOrgPrivilegeRelationsDomain req) {
+		Long rootOrgId = Long.parseLong(req.getRootOrgId());
+		Long privilegeGroupId = req.getPrivilegeGroupId();
+		Set<Long> privilegeIdSet = req.getPrivilegeIdSet();
+
+		User accessUser = getAccessUser();
+        Long userRootOrgId = accessUser.getRootOrgId();
+		if ((!rootOrgId.equals(userRootOrgId)) && !isSuperAdmin()) {
+			throw new StatusException("012601", "非法请求");
+		}
+
+		rolePrivilegeService.updateRootOrgPrivilegeRelations(rootOrgId, privilegeGroupId,
+				privilegeIdSet);
+	}
+
+	@ApiOperation(value = "增加权限")
+	@PostMapping("addPrivilege")
+	@Transactional
+	public PrivilegeEntity addPrivilege(@RequestBody PrivilegeEntity privilege) {
+
+		privilege.setName(privilege.getName().trim());
+		privilege.setCode(privilege.getCode().trim());
+		privilege.setDescription(privilege.getDescription());
+
+		if (!privilege.getCode().matches("[0-9a-zA-Z_]+")) {
+			throw new StatusException("020101", "权限编码必须由字母,数字和下划线组成");
+		}
+
+		PrivilegeGroupEntity group = GlobalHelper.getEntity(privilegeGroupRepo,
+				privilege.getGroupId(), PrivilegeGroupEntity.class);
+		if (null == group) {
+			throw new StatusException("020001", "权限组不存在");
+		}
+
+		if (null == privilege.getParentId()
+				|| BasicConsts.ROOT_PRIVILEGE_ID == privilege.getParentId()) {
+			privilege.setParentId(null);
+		} else {
+			PrivilegeEntity parentPrivilege = GlobalHelper.getEntity(privilegeRepo,
+					privilege.getParentId(), PrivilegeEntity.class);
+			if (null == parentPrivilege) {
+				throw new StatusException("020002", "父权限不存在");
+			}
+			if (!parentPrivilege.getGroupId().equals(privilege.getGroupId())) {
+				throw new StatusException("020003", "权限组错误");
+			}
+		}
+
+		PrivilegeEntity ret = privilegeRepo.save(privilege);
+
+		return ret;
+	}
+
+	@ApiOperation(value = "更新权限")
+	@PutMapping("updatePrivilege")
+	@Transactional
+	public PrivilegeEntity updatePrivilege(@RequestBody PrivilegeEntity privilege) {
+
+		privilege.setName(privilege.getName().trim());
+		privilege.setCode(privilege.getCode().trim());
+		privilege.setDescription(privilege.getDescription());
+		PrivilegeGroupEntity group = GlobalHelper.getEntity(privilegeGroupRepo,
+				privilege.getGroupId(), PrivilegeGroupEntity.class);
+		if (null == group) {
+			throw new StatusException("020001", "权限组不存在");
+		}
+
+		if (null != privilege.getParentId()) {
+			PrivilegeEntity parentPrivilege = GlobalHelper.getEntity(privilegeRepo,
+					privilege.getParentId(), PrivilegeEntity.class);
+			if (null == parentPrivilege) {
+				throw new StatusException("020002", "父权限不存在");
+			}
+			if (!parentPrivilege.getGroupId().equals(privilege.getGroupId())) {
+				throw new StatusException("020003", "权限组错误");
+			}
+		}
+
+		privilege = privilegeRepo.save(privilege);
+
+		if (group.getType().equals(PrivilegeGroupType.STUDENT_CLIENT_MENU)
+				&& group.getCode().equals(PrivilegeGroupType.STUDENT_CLIENT_MENU.name())) {
+
+			List<PrivilegeGroupEntity> gList = privilegeGroupRepo
+					.findAllByType(PrivilegeGroupType.STUDENT_CLIENT_MENU);
+
+			for (PrivilegeGroupEntity cur : gList) {
+				if (null == cur.getRootOrgId()) {
+					continue;
+				}
+				PrivilegeEntity p = privilegeRepo
+						.findByCode(privilege.getCode() + "_" + cur.getRootOrgId());
+				if (null != p) {
+					p.setDescription(privilege.getDescription());
+
+					p.setExt1(privilege.getExt1());
+					p.setExt2(privilege.getExt2());
+					p.setExt3(privilege.getExt3());
+					p.setExt4(privilege.getExt4());
+					p.setExt5(privilege.getExt5());
+
+					privilegeRepo.save(p);
+				}
+			}
+
+		}
+
+		return privilege;
+	}
+
+	@ApiOperation(value = "删除权限")
+	@DeleteMapping("deletePrivilege/{id}")
+	@Transactional
+	public void deletePrivilege(@PathVariable Long id) {
+		PrivilegeEntity privilege = GlobalHelper.getEntity(privilegeRepo, id,
+				PrivilegeEntity.class);
+		if (null == privilege) {
+			throw new StatusException("020004", "权限不存在");
+		}
+
+		List<PrivilegeEntity> children = privilegeRepo.findAllByParentId(privilege.getId());
+		if (CollectionUtils.isNotEmpty(children)) {
+			throw new StatusException("020005", "存在子权限");
+		}
+
+		privilegeRepo.deleteById(id);
+		rolePrivilegeRelationRepo.deleteByPrivilegeId(id);
+		rootOrgPrivilegeRelationRepo.deleteByPrivilegeId(id);
+
+		PrivilegeGroupEntity groupEntity = GlobalHelper.getPresentEntity(privilegeGroupRepo,
+				privilege.getGroupId(), PrivilegeGroupEntity.class);
+
+		if (groupEntity.getType().equals(PrivilegeGroupType.STUDENT_CLIENT_MENU)
+				&& groupEntity.getCode().equals(PrivilegeGroupType.STUDENT_CLIENT_MENU.name())) {
+
+			List<PrivilegeGroupEntity> gList = privilegeGroupRepo
+					.findAllByType(PrivilegeGroupType.STUDENT_CLIENT_MENU);
+
+			for (PrivilegeGroupEntity cur : gList) {
+				if (null == cur.getRootOrgId()) {
+					continue;
+				}
+				PrivilegeEntity p = privilegeRepo
+						.findByCode(privilege.getCode() + "_" + cur.getRootOrgId());
+				if (null != p) {
+					privilegeRepo.deleteById(p.getId());
+					rolePrivilegeRelationRepo.deleteByPrivilegeId(p.getId());
+					rootOrgPrivilegeRelationRepo.deleteByPrivilegeId(p.getId());
+				}
+			}
+
+		}
+
+	}
+
+	@ApiOperation(value = "查询权限")
+	@GetMapping("getPrivilege/{id}")
+	public PrivilegeEntity getPrivilege(@PathVariable Long id) {
+		PrivilegeEntity privilege = GlobalHelper.getEntity(privilegeRepo, id,
+				PrivilegeEntity.class);
+		if (null == privilege) {
+			throw new StatusException("020004", "权限不存在");
+		}
+		return privilege;
+	}
+
+	@ApiOperation(value = "校验权限(角色判断)")
+	@PostMapping("checkPrivileges")
+	public Map<String, Boolean> checkPrivileges(@RequestParam String privilegeCodes) {
+
+		List<String> privilegeCodeList = RegExpUtil.findAll(privilegeCodes, "[0-9A-Za-z_]+");
+
+		User accessUser = getAccessUser();
+
+		List<Long> roleIdList = getAccessUserRoleIdList();
+
+		List<PrivilegeEntity> privilegeEntityList = privilegeRepo
+				.findAllByCodeIn(privilegeCodeList);
+
+		Map<String, Boolean> ret = Maps.newHashMap();
+
+		boolean isSuperAdmin = isSuperAdmin();
+
+		for (PrivilegeEntity cur : privilegeEntityList) {
+			if (isSuperAdmin) {
+				ret.put(cur.getCode(), true);
+			} else {
+				List<RolePrivilegeRelationEntity> relationEntityList = rolePrivilegeRelationRepo
+						.findAllByRoleIdInAndRootOrgIdAndPrivilegeId(roleIdList,
+								accessUser.getRootOrgId(), cur.getId());
+				ret.put(cur.getCode(), CollectionUtils.isNotEmpty(relationEntityList));
+			}
+		}
+
+		return ret;
+	}
+
+	@ApiOperation(value = "校验权限(机构判断)")
+	@PostMapping("checkRootOrgPrivileges")
+	public Map<String, Boolean> checkRootOrgPrivileges(@RequestParam String privilegeCodes) {
+
+		List<String> privilegeCodeList = RegExpUtil.findAll(privilegeCodes, "[0-9A-Za-z_]+");
+
+		User accessUser = getAccessUser();
+
+		List<PrivilegeEntity> privilegeEntityList = privilegeRepo
+				.findAllByCodeIn(privilegeCodeList);
+
+		Map<String, Boolean> ret = Maps.newHashMap();
+
+		for (PrivilegeEntity cur : privilegeEntityList) {
+
+			Optional<RootOrgPrivilegeRelationEntity> optional = rootOrgPrivilegeRelationRepo
+					.findById(
+							new RootOrgPrivilegeRelationPK(cur.getId(), accessUser.getRootOrgId()));
+			ret.put(cur.getCode(), optional.isPresent());
+		}
+
+		return ret;
+	}
+
+	@ApiOperation(value = "添加角色")
+	@PostMapping("addRole")
+	@Transactional
+	public RoleEntity addRole(@RequestBody RoleDomain req) {
+		Long rootOrgId = req.getRootOrgId();
+		String code = req.getCode();
+		String name = req.getName();
+
+		validateRootOrgIsolation(rootOrgId);
+
+		RoleEntity roleByCode = roleRepo.findByCode(code);
+		if (null != roleByCode) {
+			throw new StatusException("620001", "角色编码已被占用");
+		}
+
+		RoleEntity roleByName = roleRepo.findByNameAndRootOrgId(name, rootOrgId);
+		if (null != roleByName) {
+			throw new StatusException("620002", "角色名称已被占用");
+		}
+
+		RoleInfo info = new RoleInfo();
+		info.setCode(code);
+		info.setName(name);
+		info.setRootOrgId(rootOrgId);
+
+		RoleEntity saved = rolePrivilegeService.saveRole(info);
+
+		return saved;
+	}
+
+	@ApiOperation(value = "更新角色")
+	@PutMapping("updateRole")
+	@Transactional
+	public RoleEntity updateRole(@RequestBody RoleDomain req) {
+		Long rootOrgId = req.getRootOrgId();
+		String code = req.getCode();
+		String name = req.getName();
+
+		validateRootOrgIsolation(rootOrgId);
+
+		RoleInfo info = new RoleInfo();
+		info.setCode(code);
+		info.setName(name);
+		info.setRootOrgId(rootOrgId);
+
+		RoleEntity saved = rolePrivilegeService.saveRole(info);
+
+		return saved;
+	}
+
+	@ApiOperation(value = "删除角色")
+	@DeleteMapping("deleteRole/{roleId}")
+	@Transactional
+	public RoleEntity deleteRole(@PathVariable Long roleId) {
+		User accessUser = getAccessUser();
+
+		RoleInfo info = new RoleInfo();
+		info.setId(roleId);
+		info.setRootOrgId(accessUser.getRootOrgId());
+
+		RoleEntity saved = rolePrivilegeService.deleteRole(info, false);
+
+		return saved;
+	}
+
+}

+ 386 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/SpecialtyController.java

@@ -0,0 +1,386 @@
+package cn.com.qmth.examcloud.core.basic.api.controller;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+import javax.persistence.criteria.Subquery;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.fileupload.disk.DiskFileItem;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.domain.Sort.Direction;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.commons.CommonsMultipartFile;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.commons.helpers.poi.ExcelWriter;
+import cn.com.qmth.examcloud.commons.util.PathUtil;
+import cn.com.qmth.examcloud.core.basic.api.controller.bean.SpecialtyDomain;
+import cn.com.qmth.examcloud.core.basic.dao.CourseRepo;
+import cn.com.qmth.examcloud.core.basic.dao.CourseSpeciatlyRelationRepo;
+import cn.com.qmth.examcloud.core.basic.dao.SpecialtyRepo;
+import cn.com.qmth.examcloud.core.basic.dao.entity.CourseSpeciatlyRelationEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.SpecialtyEntity;
+import cn.com.qmth.examcloud.core.basic.service.SpecialtyService;
+import cn.com.qmth.examcloud.core.basic.service.bean.SpecialtyInfo;
+import cn.com.qmth.examcloud.web.config.SystemProperties;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * @Description: 专业服务API
+ * @author weiwenhai
+ * @Date 2017年8月7号
+ */
+@RestController
+@RequestMapping("${$rmp.ctr.basic}/specialty")
+public class SpecialtyController extends ControllerSupport {
+
+	@Autowired
+	SpecialtyService specialtyService;
+
+	@Autowired
+	SystemProperties systemConfig;
+
+	@Autowired
+	SpecialtyRepo specialtyRepo;
+
+	@Autowired
+	CourseRepo courseRepo;
+
+	@Autowired
+	CourseSpeciatlyRelationRepo courseSpeciatlyRelationRepo;
+
+	private static final String[] EXCEL_HEADER = new String[]{"专业名称", "专业代码"};
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param curPage
+	 * @param pageSize
+	 * @param name
+	 * @param code
+	 * @param enable
+	 * @param courseId
+	 * @return
+	 */
+	@ApiOperation(value = "分页查询专业")
+	@GetMapping("specialtyPage/{curPage}/{pageSize}")
+	public Page<SpecialtyEntity> getSpecialtyPage(@PathVariable Integer curPage,
+			@PathVariable Integer pageSize, @RequestParam(required = false) String name,
+			@RequestParam(required = false) String code,
+			@RequestParam(required = false) Boolean enable,
+			@RequestParam(required = false) Long courseId) {
+		User accessUser = getAccessUser();
+
+		Specification<SpecialtyEntity> specification = (root, query, cb) -> {
+			List<Predicate> predicates = new ArrayList<>();
+
+			predicates.add(cb.equal(root.get("rootOrgId"), accessUser.getRootOrgId()));
+
+			if (StringUtils.isNotBlank(name)) {
+				predicates.add(cb.like(root.get("name"), toSqlSearchPattern(name)));
+			}
+			if (StringUtils.isNotBlank(code)) {
+				predicates.add(cb.like(root.get("code"), toSqlSearchPattern(code)));
+			}
+			if (null != enable) {
+				predicates.add(cb.equal(root.get("enable"), enable));
+			}
+
+			if (null != courseId) {
+				Subquery<CourseSpeciatlyRelationEntity> subquery = query
+						.subquery(CourseSpeciatlyRelationEntity.class);
+				Root<CourseSpeciatlyRelationEntity> subRoot = subquery
+						.from(CourseSpeciatlyRelationEntity.class);
+				subquery.select(subRoot.get("specialtyId"));
+				Predicate p1 = cb.equal(subRoot.get("courseId"), courseId);
+				Predicate p2 = cb.equal(subRoot.get("specialtyId"), root.get("id"));
+				subquery.where(cb.and(p1, p2));
+				predicates.add(cb.exists(subquery));
+			}
+
+			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+		};
+
+		PageRequest pageRequest = PageRequest.of(curPage, pageSize,
+				new Sort(Direction.DESC, "updateTime", "id"));
+
+		Page<SpecialtyEntity> page = specialtyRepo.findAll(specification, pageRequest);
+		return page;
+	}
+
+	@ApiOperation(value = "查询专业")
+	@GetMapping("query")
+	public List<SpecialtyEntity> query(@RequestParam(required = false) String name,
+			@RequestParam(required = false) String code,
+			@RequestParam(required = false) Boolean enable,
+			@RequestParam(required = false) Long courseId) {
+
+		User accessUser = getAccessUser();
+		Long rootOrgId = accessUser.getRootOrgId();
+
+		Specification<SpecialtyEntity> specification = (root, query, cb) -> {
+			List<Predicate> predicates = new ArrayList<>();
+
+			predicates.add(cb.equal(root.get("rootOrgId"), rootOrgId));
+
+			if (StringUtils.isNotBlank(name)) {
+				predicates.add(cb.like(root.get("name"), toSqlSearchPattern(name)));
+			}
+			if (StringUtils.isNotBlank(code)) {
+				predicates.add(cb.like(root.get("code"), toSqlSearchPattern(code)));
+			}
+			if (null != enable) {
+				predicates.add(cb.equal(root.get("enable"), enable));
+			}
+
+			if (null != courseId) {
+				Subquery<CourseSpeciatlyRelationEntity> subquery = query
+						.subquery(CourseSpeciatlyRelationEntity.class);
+				Root<CourseSpeciatlyRelationEntity> subRoot = subquery
+						.from(CourseSpeciatlyRelationEntity.class);
+				subquery.select(subRoot.get("specialtyId"));
+				Predicate p1 = cb.equal(subRoot.get("courseId"), courseId);
+				Predicate p2 = cb.equal(subRoot.get("specialtyId"), root.get("id"));
+				subquery.where(cb.and(p1, p2));
+				predicates.add(cb.exists(subquery));
+			}
+
+			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+		};
+
+		// 过载保护
+		long total = specialtyRepo.count(specification);
+		if (total > 1000) {
+			List<SpecialtyEntity> list = Lists.newArrayList();
+			return list;
+		}
+
+		List<SpecialtyEntity> list = specialtyRepo.findAll(specification,
+				new Sort(Direction.DESC, "updateTime"));
+		return list;
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param orgId
+	 * @param code
+	 * @return
+	 */
+	@ApiOperation(value = "查询专业")
+	@GetMapping("byCode")
+	public SpecialtyEntity getByCode(@RequestParam Long orgId, @RequestParam String code) {
+		SpecialtyEntity specialty = specialtyRepo.findByRootOrgIdAndCode(orgId, code);
+		if (null == specialty) {
+			throw new StatusException("620002", "专业不存在");
+		}
+		validateRootOrgIsolation(specialty.getRootOrgId());
+		return specialty;
+	}
+
+	/**
+	 * 修正
+	 *
+	 * @author WANGWEI
+	 * @param domain
+	 * @return
+	 */
+	@ApiOperation(value = "新增专业", notes = "新增")
+	@PostMapping
+	@Transactional
+	public Long addSpecialty(@RequestBody SpecialtyDomain domain) {
+		trim(domain, true);
+
+		User accessUser = getAccessUser();
+		Long rootOrgId = accessUser.getRootOrgId();
+
+		String code = domain.getCode();
+		if (StringUtils.isBlank(code)) {
+			throw new StatusException("620001", "code is blank");
+		}
+		SpecialtyEntity course = specialtyRepo.findByRootOrgIdAndCode(rootOrgId, code);
+		if (null != course) {
+			throw new StatusException("620002", "专业编码已被占用");
+		}
+
+		SpecialtyInfo info = new SpecialtyInfo();
+		info.setRootOrgId(rootOrgId);
+		info.setCode(domain.getCode());
+		info.setEnable(domain.getEnable());
+		info.setId(domain.getId());
+		info.setName(domain.getName());
+		info.setEnable(domain.getEnable());
+
+		SpecialtyEntity saved = specialtyService.saveSpecialty(info);
+		return saved.getId();
+	}
+
+	/**
+	 * 修正
+	 *
+	 * @author WANGWEI
+	 * @param domain
+	 * @return
+	 */
+	@ApiOperation(value = "修改专业", notes = "新增")
+	@PutMapping
+	@Transactional
+	public Long updateSpecialty(@RequestBody SpecialtyDomain domain) {
+		trim(domain, true);
+
+		User accessUser = getAccessUser();
+		Long rootOrgId = accessUser.getRootOrgId();
+
+		SpecialtyInfo info = new SpecialtyInfo();
+		info.setId(info.getId());
+		info.setRootOrgId(rootOrgId);
+		info.setCode(domain.getCode());
+		info.setEnable(domain.getEnable());
+		info.setId(domain.getId());
+		info.setName(domain.getName());
+		info.setEnable(domain.getEnable());
+
+		SpecialtyEntity saved = specialtyService.saveSpecialty(info);
+		return saved.getId();
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param ids
+	 */
+	@ApiOperation(value = "删除专业")
+	@DeleteMapping("{ids}")
+	@Transactional
+	public void delete(@PathVariable String ids) {
+		List<Long> specialtyIds = Stream.of(ids.split(",")).map(s -> Long.parseLong(s.trim()))
+				.collect(Collectors.toList());
+		for (Long specialtyId : specialtyIds) {
+			SpecialtyEntity one = GlobalHelper.getEntity(specialtyRepo, specialtyId,
+					SpecialtyEntity.class);
+			if (null == one) {
+				continue;
+			}
+			validateRootOrgIsolation(one.getRootOrgId());
+			specialtyRepo.delete(one);
+
+			courseSpeciatlyRelationRepo.deleteBySpecialtyId(specialtyId);
+		}
+	}
+
+	@ApiOperation(value = "下载导入模板", notes = "下载导入模板")
+	@GetMapping("importTemplate")
+	public void getDownloadTemplate(HttpServletResponse response) {
+		String resoucePath = PathUtil.getResoucePath("templates/specialtyImportTemplate.xlsx");
+		exportFile("专业导入模板.xlsx", new File(resoucePath));
+	}
+
+	@ApiOperation(value = "导入", notes = "导入")
+	@PostMapping("import")
+	@Transactional
+	public Map<String, Object> importSpecialty(@RequestParam CommonsMultipartFile file) {
+		DiskFileItem item = (DiskFileItem) file.getFileItem();
+		File storeLocation = item.getStoreLocation();
+		List<Map<String, Object>> failRecords = specialtyService.importSpecialty(getRootOrgId(),
+				storeLocation);
+		Map<String, Object> map = Maps.newHashMap();
+		map.put("hasError", CollectionUtils.isNotEmpty(failRecords));
+		map.put("failRecords", failRecords);
+		return map;
+	}
+
+	@ApiOperation(value = "导出专业")
+	@GetMapping("export")
+	public void export(@RequestParam(required = false) String name,
+			@RequestParam(required = false) String code,
+			@RequestParam(required = false) Boolean enable,
+			@RequestParam(required = false) Long courseId) {
+		User accessUser = getAccessUser();
+
+		Specification<SpecialtyEntity> specification = (root, query, cb) -> {
+			List<Predicate> predicates = new ArrayList<>();
+
+			predicates.add(cb.equal(root.get("rootOrgId"), accessUser.getRootOrgId()));
+
+			if (StringUtils.isNotBlank(name)) {
+				predicates.add(cb.like(root.get("name"), toSqlSearchPattern(name)));
+			}
+			if (StringUtils.isNotBlank(code)) {
+				predicates.add(cb.like(root.get("code"), toSqlSearchPattern(code)));
+			}
+			if (null != enable) {
+				predicates.add(cb.equal(root.get("enable"), enable));
+			}
+
+			if (null != courseId) {
+				Subquery<CourseSpeciatlyRelationEntity> subquery = query
+						.subquery(CourseSpeciatlyRelationEntity.class);
+				Root<CourseSpeciatlyRelationEntity> subRoot = subquery
+						.from(CourseSpeciatlyRelationEntity.class);
+				subquery.select(subRoot.get("specialtyId"));
+				Predicate p1 = cb.equal(subRoot.get("courseId"), courseId);
+				Predicate p2 = cb.equal(subRoot.get("specialtyId"), root.get("id"));
+				subquery.where(cb.and(p1, p2));
+				predicates.add(cb.exists(subquery));
+			}
+
+			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+		};
+
+		long count = specialtyRepo.count(specification);
+		if (100000 < count) {
+			throw new StatusException("620200", "数据量过大,无法导出");
+		}
+
+		List<SpecialtyEntity> list = specialtyRepo.findAll(specification);
+
+		List<Object[]> datas = Lists.newArrayList();
+
+		for (SpecialtyEntity cur : list) {
+			datas.add(new Object[]{cur.getName(), cur.getCode()});
+		}
+
+		String filePath = systemConfig.getTempDataDir() + File.separator
+				+ System.currentTimeMillis() + ".xlsx";
+		File file = new File(filePath);
+
+		ExcelWriter.write(EXCEL_HEADER, new Class[]{String.class, String.class}, datas,
+				new File(filePath));
+
+		exportFile("专业列表-" + getRootOrgId() + ".xlsx", file);
+
+		FileUtils.deleteQuietly(file);
+	}
+}

+ 654 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/StudentController.java

@@ -0,0 +1,654 @@
+package cn.com.qmth.examcloud.core.basic.api.controller;
+
+import cn.com.qmth.examcloud.api.commons.enums.BooleanSelect;
+import cn.com.qmth.examcloud.api.commons.exchange.PageInfo;
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.api.commons.security.enums.RoleMeta;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.commons.helpers.poi.ExcelWriter;
+import cn.com.qmth.examcloud.core.basic.api.controller.bean.StudentDomain;
+import cn.com.qmth.examcloud.core.basic.base.constants.BasicConsts;
+import cn.com.qmth.examcloud.core.basic.dao.OrgRepo;
+import cn.com.qmth.examcloud.core.basic.dao.StudentCodeRepo;
+import cn.com.qmth.examcloud.core.basic.dao.StudentRepo;
+import cn.com.qmth.examcloud.core.basic.dao.UserRepo;
+import cn.com.qmth.examcloud.core.basic.dao.entity.OrgEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.StudentCodeEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.StudentEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.UserEntity;
+import cn.com.qmth.examcloud.core.basic.service.StudentService;
+import cn.com.qmth.examcloud.core.basic.service.bean.StudentInfo;
+import cn.com.qmth.examcloud.core.basic.service.cache.StudentCache;
+import cn.com.qmth.examcloud.support.cache.CacheHelper;
+import cn.com.qmth.examcloud.support.cache.bean.OrgCacheBean;
+import cn.com.qmth.examcloud.support.cache.bean.StudentCacheBean;
+import cn.com.qmth.examcloud.support.filestorage.FileStorageUtil;
+import cn.com.qmth.examcloud.support.helper.IdentityNumberHelper;
+import cn.com.qmth.examcloud.support.privilege.PrivilegeDefine.DataAccess.ExamWorkData.StudentInfoData.AllStudentData;
+import cn.com.qmth.examcloud.support.privilege.PrivilegeDefine.DataAccess.ExamWorkData.StudentInfoData.OrgStudentData;
+import cn.com.qmth.examcloud.support.privilege.PrivilegeManager;
+import cn.com.qmth.examcloud.task.api.DataSyncCloudService;
+import cn.com.qmth.examcloud.web.config.SystemProperties;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import com.google.common.collect.Lists;
+import io.swagger.annotations.ApiOperation;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.domain.Sort.Direction;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+import javax.persistence.criteria.Subquery;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * 学生服务API Created by songyue on 17/1/14.
+ */
+@RestController
+@RequestMapping("${$rmp.ctr.basic}/student")
+public class StudentController extends ControllerSupport {
+
+    @Autowired
+    StudentRepo studentRepo;
+
+    @Autowired
+    StudentCodeRepo studentCodeRepo;
+
+    @Autowired
+    SystemProperties systemConfig;
+
+    @Autowired
+    StudentService studentService;
+
+    @Autowired
+    UserRepo userRepo;
+
+    @Autowired
+    OrgRepo orgRepo;
+
+    @Autowired
+    DataSyncCloudService dataSyncCloudService;
+
+    @Autowired
+    StudentCache studentCache;
+
+    private static final String[] EXCEL_HEADER = new String[]{"ID", "姓名", "学号", "身份证", "学习中心代码",
+            "学习中心名称"};
+
+    /**
+     * 方法注释
+     *
+     * @param name
+     * @param studentCode
+     * @param identityNumber
+     * @param curPage
+     * @param pageSize
+     * @return
+     * @author WANGWEI
+     */
+    @ApiOperation(value = "查询学生分页数据", notes = "分页")
+    @GetMapping("studentPage/{curPage}/{pageSize}")
+    public PageInfo<StudentDomain> getStudentPage(
+            @PathVariable Integer curPage,
+            @PathVariable Integer pageSize, @RequestParam String name,
+            @RequestParam String studentCode, @RequestParam String identityNumber,
+            @RequestParam(required = false) Long rootOrgId,
+            @RequestParam(required = false) Long orgId,
+            @RequestParam(required = false) BooleanSelect hasPhoto) {
+
+        User accessUser = getAccessUser();
+        if (null == rootOrgId) {
+            rootOrgId = accessUser.getRootOrgId();
+        }
+
+        validateRootOrgIsolation(rootOrgId);
+
+        final Long finalRootOrgId = rootOrgId;
+
+        Specification<StudentEntity> specification = (root, query, cb) -> {
+            List<Predicate> predicates = new ArrayList<>();
+            predicates.add(cb.equal(root.get("rootOrgId"), finalRootOrgId));
+
+            if (StringUtils.isNotEmpty(name)) {
+                predicates.add(cb.like(root.get("name"), toSqlSearchPattern(name)));
+            }
+            if (StringUtils.isNotBlank(identityNumber)) {
+                predicates.add(cb.like(root.get("identityNumber"), toSqlRightLike(identityNumber)));
+            }
+
+            if (null != hasPhoto) {
+                Boolean hasPhotoBoolean = hasPhoto.getBoolean();
+                if (null != hasPhotoBoolean) {
+                    if (hasPhotoBoolean) {
+                        predicates.add(cb.isNotNull(root.get("photoPath")));
+                    } else {
+                        predicates.add(cb.isNull(root.get("photoPath")));
+                    }
+                }
+            }
+
+            if (StringUtils.isNotBlank(studentCode)) {
+                Subquery<StudentCodeEntity> subquery = query.subquery(StudentCodeEntity.class);
+                Root<StudentCodeEntity> subRoot = subquery.from(StudentCodeEntity.class);
+                subquery.select(subRoot.get("id"));
+                Predicate p1 = cb.equal(subRoot.get("studentId"), root.get("id"));
+                Predicate p2 = cb.like(subRoot.get("studentCode"), toSqlRightLike(studentCode));
+                subquery.where(cb.and(p1, p2));
+                predicates.add(cb.exists(subquery));
+            }
+
+            Boolean orgStudentData = PrivilegeManager.judge(finalRootOrgId,
+                    accessUser.getRoleList(), OrgStudentData.CODE);
+            Boolean allStudentData = PrivilegeManager.judge(finalRootOrgId,
+                    accessUser.getRoleList(), AllStudentData.CODE);
+
+            // 学习中心过滤
+            if (isSuperAdmin()) {
+                if (null != orgId) {
+                    predicates.add(cb.equal(root.get("orgId"), orgId));
+                }
+            } else if (allStudentData) {
+                if (null != orgId) {
+                    predicates.add(cb.equal(root.get("orgId"), orgId));
+                }
+            } else {
+                if (orgStudentData) {
+                    if (hasRole(RoleMeta.LC_USER)) {
+                        UserEntity user = GlobalHelper.getEntity(userRepo, accessUser.getUserId(),
+                                UserEntity.class);
+                        if (null != user.getOrgId()) {
+                            predicates.add(cb.equal(root.get("orgId"), user.getOrgId()));
+                        } else {
+                            predicates.add(cb.equal(root.get("orgId"), -1));
+                        }
+                    } else {
+                        predicates.add(cb.equal(root.get("orgId"), -1));
+                    }
+                } else {
+                    predicates.add(cb.equal(root.get("orgId"), -1));
+                }
+            }
+
+            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+        };
+
+        PageRequest pageRequest = PageRequest.of(curPage, pageSize,
+                new Sort(Direction.DESC, "updateTime", "id"));
+
+        Page<StudentEntity> studentList = studentRepo.findAll(specification, pageRequest);
+
+        Iterator<StudentEntity> iterator = studentList.iterator();
+
+        List<StudentDomain> studentDomainList = Lists.newArrayList();
+
+//		String upyunDomain = PropertyHolder.getString("$upyun.site.1.domain");
+//		if (StringUtils.isBlank(upyunDomain)) {
+//			throw new StatusException("560111", "property[$upyun.site.1.domain] is not configured");
+//		}
+
+        while (iterator.hasNext()) {
+            StudentEntity next = iterator.next();
+            StudentDomain bean = new StudentDomain();
+            bean.setId(next.getId());
+            bean.setName(next.getName());
+            bean.setRootOrgId(next.getRootOrgId());
+            bean.setUpdateTime(next.getUpdateTime());
+            bean.setCreationTime(next.getCreationTime());
+            bean.setEnable(next.getEnable());
+            List<StudentCodeEntity> studentCodeEntityList = studentCodeRepo
+                    .findByStudentId(next.getId());
+            List<String> studentCodeList = Lists.newArrayList();
+            for (StudentCodeEntity cur : studentCodeEntityList) {
+                studentCodeList.add(cur.getStudentCode());
+            }
+            bean.setStudentCodeList(studentCodeList);
+            bean.setStudentCodesStr(StringUtils.join(studentCodeList, "<br />"));
+
+            bean.setIdentityNumber(next.getIdentityNumber());
+            bean.setPrivateIdentityNumber(IdentityNumberHelper.conceal(next.getRootOrgId(), next.getIdentityNumber()));
+            if (StringUtils.isNotBlank(next.getPhotoPath())) {
+//				bean.setPhotoPath(
+//						UrlUtil.joinUrl(upyunDomain, "student_base_photo", next.getPhotoPath()));
+                //通用存储
+                bean.setPhotoPath(FileStorageUtil.realPath(FileStorageUtil.getIntactPath("student_base_photo", next.getPhotoPath())));
+            }
+            bean.setPhoneNumber(next.getPhoneNumber());
+            bean.setSecurityPhone(next.getSecurityPhone());
+            bean.setRemark(next.getRemark());
+
+            bean.setOrgId(next.getOrgId());
+            if (null != bean.getOrgId()) {
+                OrgEntity org = GlobalHelper.getEntity(orgRepo, Long.valueOf(bean.getOrgId()),
+                        OrgEntity.class);
+                if (null != org) {
+                    bean.setOrgName(org.getName());
+                    bean.setOrgCode(org.getCode());
+                }
+            }
+
+            studentDomainList.add(bean);
+        }
+
+        PageInfo<StudentDomain> ret = new PageInfo<StudentDomain>();
+        ret.setList(studentDomainList);
+        ret.setTotal(studentList.getTotalElements());
+        return ret;
+    }
+
+    @ApiOperation(value = "导出学生", notes = "")
+    @GetMapping("export")
+    public void exportStudents(@RequestParam String name,
+                               @RequestParam String studentCode,
+                               @RequestParam String identityNumber,
+                               @RequestParam(required = false) Long rootOrgId,
+                               @RequestParam(required = false) Long orgId,
+                               @RequestParam(required = false) BooleanSelect hasPhoto) {
+
+        User accessUser = getAccessUser();
+        if (null == rootOrgId) {
+            rootOrgId = accessUser.getRootOrgId();
+        }
+
+        validateRootOrgIsolation(rootOrgId);
+
+        final Long finalRootOrgId = rootOrgId;
+
+        Specification<StudentEntity> specification = (root, query, cb) -> {
+            List<Predicate> predicates = new ArrayList<>();
+            predicates.add(cb.equal(root.get("rootOrgId"), finalRootOrgId));
+
+            if (StringUtils.isNotEmpty(name)) {
+                predicates.add(cb.like(root.get("name"), toSqlSearchPattern(name)));
+            }
+            if (StringUtils.isNotBlank(identityNumber)) {
+                predicates.add(cb.like(root.get("identityNumber"), toSqlRightLike(identityNumber)));
+            }
+
+            if (null != hasPhoto) {
+                Boolean hasPhotoBoolean = hasPhoto.getBoolean();
+                if (null != hasPhotoBoolean) {
+                    if (hasPhotoBoolean) {
+                        predicates.add(cb.isNotNull(root.get("photoPath")));
+                    } else {
+                        predicates.add(cb.isNull(root.get("photoPath")));
+                    }
+                }
+            }
+
+            if (StringUtils.isNotBlank(studentCode)) {
+                Subquery<StudentCodeEntity> subquery = query.subquery(StudentCodeEntity.class);
+                Root<StudentCodeEntity> subRoot = subquery.from(StudentCodeEntity.class);
+                subquery.select(subRoot.get("id"));
+                Predicate p1 = cb.equal(subRoot.get("studentId"), root.get("id"));
+                Predicate p2 = cb.like(subRoot.get("studentCode"), toSqlRightLike(studentCode));
+                subquery.where(cb.and(p1, p2));
+                predicates.add(cb.exists(subquery));
+            }
+
+            Boolean orgStudentData = PrivilegeManager.judge(finalRootOrgId,
+                    accessUser.getRoleList(), OrgStudentData.CODE);
+            Boolean allStudentData = PrivilegeManager.judge(finalRootOrgId,
+                    accessUser.getRoleList(), AllStudentData.CODE);
+
+            // 学习中心过滤
+            if (isSuperAdmin()) {
+                if (null != orgId) {
+                    predicates.add(cb.equal(root.get("orgId"), orgId));
+                }
+            } else if (allStudentData) {
+                if (null != orgId) {
+                    predicates.add(cb.equal(root.get("orgId"), orgId));
+                }
+            } else {
+                if (orgStudentData) {
+                    if (hasRole(RoleMeta.LC_USER)) {
+                        UserEntity user = GlobalHelper.getEntity(userRepo, accessUser.getUserId(),
+                                UserEntity.class);
+                        if (null != user.getOrgId()) {
+                            predicates.add(cb.equal(root.get("orgId"), user.getOrgId()));
+                        } else {
+                            predicates.add(cb.equal(root.get("orgId"), -1));
+                        }
+                    } else {
+                        predicates.add(cb.equal(root.get("orgId"), -1));
+                    }
+                } else {
+                    predicates.add(cb.equal(root.get("orgId"), -1));
+                }
+            }
+
+            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+        };
+
+        long count = studentRepo.count(specification);
+        if (100000 < count) {
+            throw new StatusException("520200", "数据量超过100000,无法导出");
+        }
+
+        List<StudentEntity> studentList = studentRepo.findAll(specification,
+                new Sort(Direction.DESC, "updateTime"));
+
+        List<Object[]> datas = Lists.newArrayList();
+
+        for (StudentEntity cur : studentList) {
+            String orgCode = null;
+            String orgName = null;
+            if (null != cur.getOrgId()) {
+                OrgEntity org = GlobalHelper.getEntity(orgRepo, Long.valueOf(cur.getOrgId()),
+                        OrgEntity.class);
+                if (null != org) {
+                    orgCode = org.getCode();
+                    orgName = org.getName();
+                }
+            }
+
+            List<StudentCodeEntity> studentCodeEntityList = studentCodeRepo
+                    .findByStudentId(cur.getId());
+            List<String> studentCodeList = Lists.newArrayList();
+            for (StudentCodeEntity sc : studentCodeEntityList) {
+                studentCodeList.add(sc.getStudentCode());
+            }
+
+            datas.add(
+                    new Object[]{cur.getId(), cur.getName(), StringUtils.join(studentCodeList, ";"),
+                            cur.getIdentityNumber(), orgCode, orgName});
+        }
+
+        String filePath = systemConfig.getTempDataDir() + File.separator
+                + System.currentTimeMillis() + ".xlsx";
+        File file = new File(filePath);
+
+        ExcelWriter.write(EXCEL_HEADER, new Class[]{Long.class, String.class, String.class,
+                String.class, String.class, String.class}, datas, new File(filePath));
+
+        exportFile("学生-" + getRootOrgId() + ".xlsx", file);
+
+        FileUtils.deleteQuietly(file);
+    }
+
+    /**
+     * 方法注释
+     *
+     * @param ids
+     * @return
+     * @author WANGWEI
+     */
+    @ApiOperation(value = "启用学生")
+    @PutMapping("enable/{ids}")
+    @Transactional
+    public List<String> enableStudent(@PathVariable String ids) {
+        List<Long> studentIds = Stream.of(ids.split(",")).map(s -> Long.parseLong(s.trim()))
+                .collect(Collectors.toList());
+        List<String> ret = Lists.newArrayList();
+        for (Long cur : studentIds) {
+            StudentEntity s = GlobalHelper.getEntity(studentRepo, cur, StudentEntity.class);
+            s.setEnable(true);
+            studentRepo.save(s);
+            ret.add(s.getId() + ":" + s.getName());
+        }
+        for (Long cur : studentIds) {
+            studentCache.remove(cur);
+        }
+        return ret;
+    }
+
+    /**
+     * 方法注释
+     *
+     * @param ids
+     * @return
+     * @author WANGWEI
+     */
+    @ApiOperation(value = "禁用学生")
+    @PutMapping("disable/{ids}")
+    @Transactional
+    public List<String> disableStudent(@PathVariable String ids) {
+        List<Long> studentIds = Stream.of(ids.split(",")).map(s -> Long.parseLong(s.trim()))
+                .collect(Collectors.toList());
+        List<String> ret = Lists.newArrayList();
+        for (Long cur : studentIds) {
+            StudentEntity s = GlobalHelper.getEntity(studentRepo, cur, StudentEntity.class);
+            s.setEnable(false);
+            studentRepo.save(s);
+            ret.add(s.getId() + ":" + s.getName());
+        }
+        for (Long cur : studentIds) {
+            studentCache.remove(cur);
+        }
+        return ret;
+    }
+
+    @ApiOperation(value = "重置学生密码", notes = "重置学生密码")
+    @PutMapping("resetPass/{ids}")
+    @Transactional
+    public void resetPassword(@PathVariable String ids) {
+        List<Long> studentIds = Stream.of(ids.split(",")).map(s -> Long.parseLong(s.trim()))
+                .collect(Collectors.toList());
+        for (Long cur : studentIds) {
+            StudentEntity s = GlobalHelper.getEntity(studentRepo, cur, StudentEntity.class);
+            if (null == s) {
+                throw new StatusException("450110", "学生不存在");
+            }
+            String identityNumber = s.getIdentityNumber();
+            if (StringUtils.isNotEmpty(identityNumber)
+                    && identityNumber.matches("[0-9a-zA-Z]{6,}")) {
+                s.setPassword(StringUtils.substring(identityNumber, -6, identityNumber.length()));
+            } else {
+                s.setPassword(BasicConsts.DEFAULT_PASSWORD);
+            }
+            s.setUpdateTime(new Date());
+            studentRepo.save(s);
+        }
+    }
+
+    @ApiOperation(value = "重置机构下所有学生密码", notes = "重置机构下所有学生密码")
+    @PutMapping("resetPasswordByOrgId/{orgId}")
+    @Transactional
+    public void resetPasswordByOrgId(@PathVariable Long orgId) {
+        OrgEntity org = GlobalHelper.getPresentEntity(orgRepo, orgId, OrgEntity.class);
+
+        validateRootOrgIsolation(org.getRootId());
+
+        List<StudentEntity> stuentList = studentRepo.findByOrgId(org.getId());
+        for (StudentEntity s : stuentList) {
+            String identityNumber = s.getIdentityNumber();
+            if (StringUtils.isNotEmpty(identityNumber)
+                    && identityNumber.matches("[0-9a-zA-Z]{6,}")) {
+                s.setPassword(StringUtils.substring(identityNumber, -6, identityNumber.length()));
+            } else {
+                s.setPassword(BasicConsts.DEFAULT_PASSWORD);
+            }
+            s.setUpdateTime(new Date());
+            studentRepo.save(s);
+        }
+    }
+
+    /**
+     * 方法注释
+     *
+     * @param newPassword
+     * @return
+     * @author WANGWEI
+     */
+    @ApiOperation(value = "修改学生密码", notes = "修改密码")
+    @PutMapping("password/direct")
+    @Transactional
+    public Long updateStudentPassword(@RequestParam("newPassword") String newPassword) {
+        User accessUser = getAccessUser();
+        if (StringUtils.isEmpty(newPassword)) {
+            throw new StatusException("450201", "新密码为空");
+        }
+
+        if (!newPassword.matches("[a-zA-Z0-9]{6,18}")) {
+            throw new StatusException("450203", "密码必须是6至18位字母或数字");
+        }
+
+        StudentEntity s = GlobalHelper.getEntity(studentRepo, accessUser.getUserId(),
+                StudentEntity.class);
+        s.setPassword(newPassword);
+        studentRepo.save(s);
+        return s.getId();
+    }
+
+    /**
+     * 方法注释
+     *
+     * @param password
+     * @param newPassword
+     * @return
+     * @author WANGWEI
+     */
+    @ApiOperation(value = "修改学生密码", notes = "修改密码")
+    @PutMapping("password")
+    @Transactional
+    public Long updateStudentPassword(@RequestParam("password") String password,
+                                      @RequestParam("newPassword") String newPassword) {
+        User accessUser = getAccessUser();
+        if (StringUtils.isEmpty(password)) {
+            throw new StatusException("450110", "旧密码为空");
+        }
+        if (StringUtils.isEmpty(newPassword)) {
+            throw new StatusException("450111", "新密码为空");
+        }
+        if (!newPassword.matches("[a-zA-Z0-9]{6,18}")) {
+            throw new StatusException("450111", "密码必须是6至18位字母或数字");
+        }
+
+        StudentEntity s = GlobalHelper.getEntity(studentRepo, accessUser.getUserId(),
+                StudentEntity.class);
+
+        if (StringUtils.isNotBlank(s.getPassword()) && !s.getPassword().equals(password)) {
+            throw new StatusException("450111", "当前密码错误");
+        }
+        s.setPassword(newPassword);
+        studentRepo.save(s);
+        return s.getId();
+    }
+
+    /**
+     * 查询学生
+     *
+     * @return
+     * @author WANGWEI
+     */
+    @ApiOperation(value = "查询学生")
+    @GetMapping("getStudentInfoBySession")
+    public StudentInfo getStudentInfoBySession() {
+        User accessUser = getAccessUser();
+        Long rootOrgId = accessUser.getRootOrgId();
+        Long studentId = accessUser.getUserId();
+
+        StudentCacheBean s = CacheHelper.getStudent(studentId);
+
+        OrgCacheBean org = CacheHelper.getOrg(s.getOrgId());
+        OrgCacheBean rootOrg = CacheHelper.getOrg(rootOrgId);
+
+        StudentInfo info = new StudentInfo();
+
+        info.setEnable(s.getEnable());
+        info.setId(s.getId());
+        info.setIdentityNumber(s.getIdentityNumber());
+        info.setName(s.getName());
+        info.setOrgCode(org.getCode());
+        info.setOrgId(org.getId());
+        info.setOrgName(org.getName());
+        info.setPhoneNumber(s.getPhoneNumber());
+        info.setPhotoPath(s.getPhotoPath());
+        info.setRemark(s.getRemark());
+        info.setRootOrgId(rootOrgId);
+        info.setRootOrgName(rootOrg.getName());
+        info.setSecurityPhone(s.getSecurityPhone());
+        info.setStudentCodeList(s.getStudentCodeList());
+
+        return info;
+    }
+
+    @ApiOperation(value = "查询学生")
+    @GetMapping("getStudentInfo")
+    public StudentInfo getStudentInfo(@RequestParam(required = false) Long rootOrgId,
+                                      @RequestParam(required = false) Long studentId,
+                                      @RequestParam(required = false) String identityNumber,
+                                      @RequestParam(required = false) String studentCode,
+                                      @RequestParam(required = false) String securityPhone) {
+        if (null == rootOrgId) {
+            rootOrgId = getRootOrgId();
+        } else {
+            validateRootOrgIsolation(rootOrgId);
+        }
+
+        StudentInfo studentInfo = studentService.getStudentInfo(rootOrgId, studentId,
+                identityNumber, studentCode, securityPhone);
+
+        if (null != studentInfo) {
+            studentInfo.setIdentityNumber(IdentityNumberHelper.conceal(rootOrgId, studentInfo.getIdentityNumber()));
+        }
+
+        return studentInfo;
+    }
+
+    @ApiOperation(value = "解绑学号", notes = "")
+    @PostMapping("unbindStudentCode")
+    @Transactional
+    public List<Long> unbindStudentCode(@RequestParam(required = false) String studentCode,
+                                        @RequestParam(required = false) String identityNumber,
+                                        @RequestParam(required = true) Long rootOrgId) {
+
+        if (null == rootOrgId) {
+            rootOrgId = getRootOrgId();
+        } else {
+            OrgEntity rootOrg = GlobalHelper.getEntity(orgRepo, rootOrgId, OrgEntity.class);
+            if (null == rootOrg) {
+                throw new StatusException("450110", "rootOrgId is wrong");
+            }
+            validateRootOrgIsolation(rootOrgId);
+        }
+
+        List<Long> studentIdList = studentService.unbindStudentCode(rootOrgId, studentCode,
+                identityNumber);
+
+        for (Long cur : studentIdList) {
+            studentCache.remove(cur);
+        }
+
+        return studentIdList;
+    }
+
+    @ApiOperation(value = "解绑安全手机", notes = "")
+    @PutMapping("unbindSecurityPhone/{ids}")
+    @Transactional
+    public void unbindSecurityPhone(@PathVariable String ids) {
+        List<Long> studentIds = Stream.of(ids.split(",")).map(s -> Long.parseLong(s.trim()))
+                .collect(Collectors.toList());
+        for (Long cur : studentIds) {
+            StudentEntity s = GlobalHelper.getEntity(studentRepo, cur, StudentEntity.class);
+            if (null == s) {
+                throw new StatusException("450110", "学生不存在");
+            }
+            validateRootOrgIsolation(s.getRootOrgId());
+        }
+
+        for (Long cur : studentIds) {
+            studentService.unbindSecurityPhone(cur);
+        }
+
+        for (Long cur : studentIds) {
+            studentCache.remove(cur);
+        }
+
+    }
+
+}

+ 144 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/SystemPropertyController.java

@@ -0,0 +1,144 @@
+package cn.com.qmth.examcloud.core.basic.api.controller;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.persistence.criteria.Predicate;
+import javax.validation.Valid;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.domain.Sort.Direction;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.google.common.collect.Lists;
+
+import cn.com.qmth.examcloud.core.basic.api.controller.bean.SystemPropertyDomain;
+import cn.com.qmth.examcloud.core.basic.dao.SystemPropertyRepo;
+import cn.com.qmth.examcloud.core.basic.dao.entity.SystemPropertyEntity;
+import cn.com.qmth.examcloud.core.basic.service.SystemPropertyService;
+import cn.com.qmth.examcloud.core.basic.service.bean.SystemPropertyInfo;
+import cn.com.qmth.examcloud.core.basic.service.cache.SystemPropertyCache;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * 系统配置
+ *
+ * @author WANGWEI
+ * @date 2018年12月3日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@RestController
+@RequestMapping("${$rmp.ctr.basic}/systemProperty")
+public class SystemPropertyController extends ControllerSupport {
+
+	@Autowired
+	SystemPropertyService systemPropertyService;
+
+	@Autowired
+	SystemPropertyRepo systemPropertyRepo;
+
+	@Autowired
+	SystemPropertyCache systemPropertyCache;
+
+	@ApiOperation(value = "查询系统配置")
+	@GetMapping("{key}")
+	public Object get(@PathVariable String key) {
+		Object object = systemPropertyService.get(key);
+		return object;
+	}
+
+	@ApiOperation(value = "查询系统配置")
+	@GetMapping("page/{curPage}/{pageSize}")
+	public Page<SystemPropertyEntity> get(@PathVariable Integer curPage,
+			@PathVariable Integer pageSize, @RequestParam(required = false) String propKey,
+			@RequestParam(required = false) String description,
+			@RequestParam(required = false) String propValue) {
+
+		Specification<SystemPropertyEntity> specification = (root, query, cb) -> {
+			List<Predicate> predicates = new ArrayList<>();
+
+			if (StringUtils.isNotBlank(propKey)) {
+				predicates.add(cb.like(root.get("propKey"), toSqlSearchPattern(propKey)));
+			}
+			if (StringUtils.isNotBlank(description)) {
+				predicates.add(cb.like(root.get("description"), toSqlSearchPattern(description)));
+			}
+			if (StringUtils.isNotBlank(propValue)) {
+				predicates.add(cb.like(root.get("propValue"), toSqlSearchPattern(propValue)));
+			}
+
+			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+		};
+
+		PageRequest pageRequest = PageRequest.of(curPage, pageSize,
+				new Sort(Direction.DESC, "updateTime", "propKey"));
+
+		Page<SystemPropertyEntity> page = systemPropertyRepo.findAll(specification, pageRequest);
+
+		return page;
+	}
+
+	@ApiOperation(value = "新增系统参数")
+	@PostMapping
+	@Transactional
+	public void addSysProp(@RequestBody @Valid SystemPropertyDomain domain) {
+		SystemPropertyInfo info = new SystemPropertyInfo();
+		info.setDescription(domain.getDescription());
+		info.setPropKey(domain.getPropKey());
+		info.setPropValue(domain.getPropValue());
+		info.setPropValueType(domain.getPropValueType());
+		systemPropertyService.addSysProp(info);
+	}
+
+	@ApiOperation(value = "更新系统参数")
+	@PutMapping
+	@Transactional
+	public void updateSysProp(@RequestBody @Valid SystemPropertyDomain domain) {
+		SystemPropertyInfo info = new SystemPropertyInfo();
+		info.setDescription(domain.getDescription());
+		info.setPropKey(domain.getPropKey());
+		info.setPropValue(domain.getPropValue());
+		info.setPropValueType(domain.getPropValueType());
+		systemPropertyService.updateSysProp(info);
+	}
+
+	@ApiOperation(value = "删除系统参数")
+	@DeleteMapping("{ids}")
+	@Transactional
+	public void deleteSysProp(@PathVariable @Required String ids) {
+		List<String> propKeys = Stream.of(ids.split(",")).map(s -> s.trim())
+				.collect(Collectors.toList());
+		List<SystemPropertyEntity> list = Lists.newArrayList();
+		for (String key : propKeys) {
+			SystemPropertyEntity prop = GlobalHelper.getPresentEntity(systemPropertyRepo, key,
+					SystemPropertyEntity.class);
+			systemPropertyRepo.delete(prop);
+			list.add(prop);
+		}
+
+		for (SystemPropertyEntity cur : list) {
+			systemPropertyCache.refresh(cur.getPropKey());
+		}
+
+	}
+
+}

+ 60 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/TestController.java

@@ -0,0 +1,60 @@
+package cn.com.qmth.examcloud.core.basic.api.controller;
+
+import java.util.Collections;
+
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.commons.util.DateUtil;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+
+/**
+ * 测试状态.勿修改或删除
+ *
+ * @author WANGWEI
+ * @date 2018年8月23日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@RestController
+@RequestMapping("${$rmp.ctr.basic}")
+public class TestController extends ControllerSupport {
+
+	@RequestMapping(value = {"/", ""}, method = RequestMethod.GET)
+	public String get() {
+		return DateUtil.chinaNow();
+	}
+
+	@RequestMapping(value = {"/", ""}, method = RequestMethod.HEAD)
+	public ResponseEntity<?> head() {
+		return new ResponseEntity<Object>(HttpStatus.NO_CONTENT);
+	}
+
+	@RequestMapping(value = {"/", ""}, method = RequestMethod.OPTIONS)
+	public HttpEntity<?> options() {
+		HttpHeaders headers = new HttpHeaders();
+		headers.setAllow(Collections.singleton(HttpMethod.GET));
+		return new ResponseEntity<Object>(headers, HttpStatus.OK);
+	}
+
+	@RequestMapping(value = {"/", ""}, method = RequestMethod.POST)
+	public String post() {
+		return DateUtil.chinaNow();
+	}
+
+	@RequestMapping(value = {"/", ""}, method = RequestMethod.PUT)
+	public String put() {
+		return DateUtil.chinaNow();
+	}
+
+	@RequestMapping(value = {"/", ""}, method = RequestMethod.DELETE)
+	public String delete() {
+		return DateUtil.chinaNow();
+	}
+
+}

+ 711 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/UserController.java

@@ -0,0 +1,711 @@
+package cn.com.qmth.examcloud.core.basic.api.controller;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+import javax.persistence.criteria.Subquery;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.domain.Sort.Direction;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+import cn.com.qmth.examcloud.api.commons.exchange.PageInfo;
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.api.commons.security.enums.RoleMeta;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.basic.api.controller.bean.UserDomain;
+import cn.com.qmth.examcloud.core.basic.api.controller.bean.UserFormDomain;
+import cn.com.qmth.examcloud.core.basic.base.constants.BasicConsts;
+import cn.com.qmth.examcloud.core.basic.dao.OrgRepo;
+import cn.com.qmth.examcloud.core.basic.dao.RoleRepo;
+import cn.com.qmth.examcloud.core.basic.dao.UserRepo;
+import cn.com.qmth.examcloud.core.basic.dao.UserRoleRelationRepo;
+import cn.com.qmth.examcloud.core.basic.dao.entity.OrgEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.RoleEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.UserEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.UserRoleRelationEntity;
+import cn.com.qmth.examcloud.core.basic.service.UserService;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * 用户服务API Created by songyue on 17/1/13.
+ */
+@RestController
+@RequestMapping("${$rmp.ctr.basic}/user")
+public class UserController extends ControllerSupport {
+
+	@Autowired
+	UserService userService;
+
+	@Autowired
+	UserRepo userRepo;
+
+	@Autowired
+	OrgRepo orgRepo;
+
+	@Autowired
+	RoleRepo roleRepo;
+
+	@Autowired
+	UserRoleRelationRepo userRoleRelationRepo;
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param curPage
+	 * @param pageSize
+	 * @param rootOrgId
+	 * @param loginName
+	 * @param name
+	 * @param enable
+	 * @param roleId
+	 * @param roleCode
+	 * @return
+	 */
+	@ApiOperation(value = "查询所有用户", notes = "")
+	@GetMapping("all/{curPage}/{pageSize}")
+	public PageInfo<UserDomain> getUserPage(@PathVariable Integer curPage,
+			@PathVariable Integer pageSize, @RequestParam(required = false) Long rootOrgId,
+			@RequestParam String loginName, @RequestParam String name,
+			@RequestParam(required = false) Boolean enable,
+			@RequestParam(required = false) Long roleId, @RequestParam(required = false) Long orgId,
+			@RequestParam(required = false) String roleCode) {
+
+		User accessUser = getAccessUser();
+
+		if (null == rootOrgId) {
+			rootOrgId = accessUser.getRootOrgId();
+		} else {
+			validateRootOrgIsolation(rootOrgId);
+		}
+
+		final Long finalRootOrgId = rootOrgId;
+
+		OrgEntity rootOrg = GlobalHelper.getEntity(orgRepo, rootOrgId, OrgEntity.class);
+		if (null == rootOrg) {
+			throw new StatusException("150003", "机构不存在");
+		}
+		if (null != rootOrg.getParentId()) {
+			throw new StatusException("150004", "机构错误");
+		}
+
+		if (null != roleId) {
+			RoleEntity roleEntity = GlobalHelper.getEntity(roleRepo, roleId, RoleEntity.class);
+			if (null == roleEntity) {
+				throw new StatusException("150002", "角色不存在");
+			}
+		} else if (StringUtils.isNotBlank(roleCode)) {
+			RoleEntity roleEntity = roleRepo.findByCodeAndRootOrgIdIsNull(roleCode.trim());
+			if (null == roleEntity) {
+				roleEntity = roleRepo.findByCodeAndRootOrgId(roleCode.trim(),
+						accessUser.getRootOrgId());
+			}
+			if (null == roleEntity) {
+				throw new StatusException("150002", "角色不存在");
+			}
+			roleId = roleEntity.getId();
+		}
+
+		final Long finalRoleId = roleId;
+
+		Specification<UserEntity> specification = (root, query, cb) -> {
+			List<Predicate> predicates = new ArrayList<>();
+			predicates.add(cb.equal(root.get("rootOrgId"), finalRootOrgId));
+
+			if (StringUtils.isNotBlank(loginName)) {
+				predicates.add(cb.like(root.get("loginName"), toSqlSearchPattern(loginName)));
+			}
+			if (StringUtils.isNotBlank(name)) {
+				predicates.add(cb.like(root.get("name"), toSqlSearchPattern(name)));
+			}
+			if (null != orgId) {
+				predicates.add(cb.equal(root.get("orgId"), orgId));
+			}
+			if (null != enable) {
+				predicates.add(cb.equal(root.get("enable"), enable));
+			}
+			if (null != finalRoleId) {
+				Subquery<UserRoleRelationEntity> subquery = query
+						.subquery(UserRoleRelationEntity.class);
+				Root<UserRoleRelationEntity> subRoot = subquery.from(UserRoleRelationEntity.class);
+				subquery.select(subRoot.get("userId"));
+				Predicate p1 = cb.equal(subRoot.get("roleId"), finalRoleId);
+				Predicate p2 = cb.equal(subRoot.get("userId"), root.get("id"));
+				subquery.where(cb.and(p1, p2));
+				predicates.add(cb.exists(subquery));
+			}
+			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+		};
+		Pageable pageable = PageRequest.of(curPage - 1, pageSize, Sort.Direction.DESC, "updateTime",
+				"id");
+
+		Page<UserEntity> userList = userRepo.findAll(specification, pageable);
+
+		Iterator<UserEntity> iterator = userList.iterator();
+
+		List<UserDomain> fullUserInfoList = Lists.newArrayList();
+		while (iterator.hasNext()) {
+			UserEntity next = iterator.next();
+			UserDomain bean = new UserDomain();
+			bean.setId(next.getId());
+			bean.setLoginName(next.getLoginName());
+			bean.setName(next.getName());
+			bean.setRootOrgId(next.getRootOrgId());
+			bean.setUpdateTime(next.getUpdateTime());
+			bean.setCreationTime(next.getCreationTime());
+			bean.setOrgId(next.getOrgId());
+			bean.setPhoneNumber(next.getPhoneNumber());
+			if (null != bean.getOrgId()) {
+				OrgEntity org = GlobalHelper.getEntity(orgRepo, Long.valueOf(bean.getOrgId()),
+						OrgEntity.class);
+				if (null != org) {
+					bean.setOrgName(org.getName());
+					bean.setOrgCode(org.getCode());
+				}
+			}
+			bean.setRootOrgName(rootOrg.getName());
+			bean.setEnable(next.getEnable());
+
+			List<UserRoleRelationEntity> relationList = userRoleRelationRepo
+					.findAllByUserId(next.getId());
+			List<String> roleNameList = Lists.newArrayList();
+			List<Long> roleIdList = Lists.newArrayList();
+			List<String> roleCodeList = Lists.newArrayList();
+			for (UserRoleRelationEntity cur : relationList) {
+				RoleEntity curRoleEntity = GlobalHelper.getEntity(roleRepo, cur.getRoleId(),
+						RoleEntity.class);
+				if (null == curRoleEntity) {
+					throw new StatusException("150002", "角色错误");
+				}
+				roleNameList.add(curRoleEntity.getName());
+				roleIdList.add(curRoleEntity.getId());
+				roleCodeList.add(curRoleEntity.getCode());
+			}
+			bean.setRoleNamesStr(StringUtils.join(roleNameList, ","));
+			bean.setRoleIds(roleIdList);
+			bean.setRoleCodes(roleCodeList);
+
+			fullUserInfoList.add(bean);
+		}
+
+		PageInfo<UserDomain> ret = new PageInfo<UserDomain>();
+		ret.setList(fullUserInfoList);
+		ret.setTotal(userList.getTotalElements());
+		return ret;
+	}
+
+	@ApiOperation(value = "模糊查询用户", notes = "")
+	@GetMapping("query")
+	public List<UserDomain> query(@RequestParam(required = false) Long rootOrgId,
+			@RequestParam(required = false) String rootOrgCode,
+			@RequestParam(required = false) String loginName,
+			@RequestParam(required = false) String name,
+			@RequestParam(required = false) Boolean enable,
+			@RequestParam(required = false) Long roleId, @RequestParam(required = false) Long orgId,
+			@RequestParam(required = false) String roleCode) {
+
+		OrgEntity rootOrg = null;
+
+		if (null != rootOrgId) {
+			rootOrg = GlobalHelper.getEntity(orgRepo, rootOrgId, OrgEntity.class);
+			if (null == rootOrg) {
+				throw new StatusException("150003", "机构不存在");
+			}
+		} else if (StringUtils.isNotBlank(rootOrgCode)) {
+			rootOrg = orgRepo.findByParentIdIsNullAndCode(rootOrgCode);
+			if (null == rootOrg) {
+				throw new StatusException("150003", "机构不存在");
+			}
+		}
+
+		if (null != rootOrg) {
+			if (null != rootOrg.getParentId()) {
+				throw new StatusException("150004", "机构错误");
+			}
+			rootOrgId = rootOrg.getId();
+		}
+
+		final Long finalRootOrgId = rootOrgId;
+
+		if (null != roleId) {
+			RoleEntity roleEntity = GlobalHelper.getEntity(roleRepo, roleId, RoleEntity.class);
+			if (null == roleEntity) {
+				throw new StatusException("150002", "角色不存在");
+			}
+		} else if (StringUtils.isNotBlank(roleCode)) {
+			RoleEntity roleEntity = roleRepo.findByCodeAndRootOrgIdIsNull(roleCode.trim());
+			if (null == roleEntity) {
+				roleEntity = roleRepo.findByCodeAndRootOrgId(roleCode.trim(), rootOrgId);
+			}
+			if (null == roleEntity) {
+				throw new StatusException("150002", "角色不存在");
+			}
+			roleId = roleEntity.getId();
+		}
+
+		final Long finalRoleId = roleId;
+
+		Specification<UserEntity> specification = (root, query, cb) -> {
+			List<Predicate> predicates = new ArrayList<>();
+
+			if (null != finalRootOrgId) {
+				predicates.add(cb.equal(root.get("rootOrgId"), finalRootOrgId));
+			}
+
+			if (StringUtils.isNotBlank(loginName)) {
+				predicates.add(cb.like(root.get("loginName"), toSqlSearchPattern(loginName)));
+			}
+			if (StringUtils.isNotBlank(name)) {
+				predicates.add(cb.like(root.get("name"), toSqlSearchPattern(name)));
+			}
+			if (null != orgId) {
+				predicates.add(cb.equal(root.get("orgId"), orgId));
+			}
+			if (null != enable) {
+				predicates.add(cb.equal(root.get("enable"), enable));
+			}
+			if (null != finalRoleId) {
+				Subquery<UserRoleRelationEntity> subquery = query
+						.subquery(UserRoleRelationEntity.class);
+				Root<UserRoleRelationEntity> subRoot = subquery.from(UserRoleRelationEntity.class);
+				subquery.select(subRoot.get("userId"));
+				Predicate p1 = cb.equal(subRoot.get("roleId"), finalRoleId);
+				Predicate p2 = cb.equal(subRoot.get("userId"), root.get("id"));
+				subquery.where(cb.and(p1, p2));
+				predicates.add(cb.exists(subquery));
+			}
+			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+		};
+
+		PageRequest pageRequest = PageRequest.of(0, 50, new Sort(Direction.DESC, "updateTime"));
+
+		Page<UserEntity> userList = userRepo.findAll(specification, pageRequest);
+
+		Iterator<UserEntity> iterator = userList.iterator();
+
+		List<UserDomain> fullUserInfoList = Lists.newArrayList();
+		while (iterator.hasNext()) {
+			UserEntity next = iterator.next();
+			UserDomain bean = new UserDomain();
+			bean.setId(next.getId());
+			bean.setLoginName(next.getLoginName());
+			bean.setName(next.getName());
+			bean.setRootOrgId(next.getRootOrgId());
+			bean.setUpdateTime(next.getUpdateTime());
+			bean.setCreationTime(next.getCreationTime());
+			bean.setOrgId(next.getOrgId());
+			if (null != bean.getOrgId()) {
+				OrgEntity org = GlobalHelper.getEntity(orgRepo, Long.valueOf(bean.getOrgId()),
+						OrgEntity.class);
+				if (null != org) {
+					bean.setOrgName(org.getName());
+					bean.setOrgCode(org.getCode());
+				}
+			}
+			bean.setRootOrgName(rootOrg.getName());
+			bean.setEnable(next.getEnable());
+
+			List<UserRoleRelationEntity> relationList = userRoleRelationRepo
+					.findAllByUserId(next.getId());
+			List<String> roleNameList = Lists.newArrayList();
+			List<Long> roleIdList = Lists.newArrayList();
+			List<String> roleCodeList = Lists.newArrayList();
+			for (UserRoleRelationEntity cur : relationList) {
+				RoleEntity curRoleEntity = GlobalHelper.getEntity(roleRepo, cur.getRoleId(),
+						RoleEntity.class);
+				if (null == curRoleEntity) {
+					throw new StatusException("150002", "角色错误");
+				}
+				roleNameList.add(curRoleEntity.getName());
+				roleIdList.add(curRoleEntity.getId());
+				roleCodeList.add(curRoleEntity.getCode());
+			}
+			bean.setRoleNamesStr(StringUtils.join(roleNameList, ","));
+			bean.setRoleIds(roleIdList);
+			bean.setRoleCodes(roleCodeList);
+
+			fullUserInfoList.add(bean);
+		}
+
+		return fullUserInfoList;
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param id
+	 * @return
+	 */
+	@ApiOperation(value = "查询登陆用户", notes = "")
+	@GetMapping("getUserBySession")
+	public UserDomain getUserBySession() {
+		User accessUser = getAccessUser();
+		UserEntity userEntity = GlobalHelper.getEntity(userRepo, accessUser.getUserId(),
+				UserEntity.class);
+
+		UserDomain bean = new UserDomain();
+		bean.setId(userEntity.getId());
+		bean.setLoginName(userEntity.getLoginName());
+		bean.setName(userEntity.getName());
+		bean.setRootOrgId(userEntity.getRootOrgId());
+		bean.setUpdateTime(userEntity.getUpdateTime());
+		bean.setCreationTime(userEntity.getCreationTime());
+		bean.setOrgId(userEntity.getOrgId());
+		if (null != bean.getOrgId()) {
+			OrgEntity org = GlobalHelper.getEntity(orgRepo, Long.valueOf(bean.getOrgId()),
+					OrgEntity.class);
+			if (null != org) {
+				bean.setOrgName(org.getName());
+				bean.setOrgCode(org.getCode());
+			}
+		}
+		bean.setEnable(userEntity.getEnable());
+		return bean;
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param id
+	 * @return
+	 */
+	@ApiOperation(value = "按id查询用户", notes = "id查询")
+	@GetMapping("/{id}")
+	public UserDomain getUserById(@PathVariable long id) {
+		UserEntity userEntity = GlobalHelper.getEntity(userRepo, id, UserEntity.class);
+
+		UserDomain bean = new UserDomain();
+		bean.setId(userEntity.getId());
+		bean.setLoginName(userEntity.getLoginName());
+		bean.setName(userEntity.getName());
+		bean.setRootOrgId(userEntity.getRootOrgId());
+		bean.setUpdateTime(userEntity.getUpdateTime());
+		bean.setCreationTime(userEntity.getCreationTime());
+		bean.setOrgId(userEntity.getOrgId());
+		if (null != bean.getOrgId()) {
+			OrgEntity org = GlobalHelper.getEntity(orgRepo, Long.valueOf(bean.getOrgId()),
+					OrgEntity.class);
+			if (null != org) {
+				bean.setOrgName(org.getName());
+				bean.setOrgCode(org.getCode());
+			}
+		}
+		bean.setEnable(userEntity.getEnable());
+		return bean;
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param orgId
+	 * @return
+	 */
+	@ApiOperation(value = "按orgId查询用户", notes = "机构id查询机构用户")
+	@GetMapping("/org/{orgId}")
+	public List<UserEntity> getUserByOrgId(@PathVariable long orgId) {
+		List<UserEntity> userList = userRepo.findByOrgId(orgId);
+		return userList;
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param rootOrgId
+	 * @return
+	 */
+	@ApiOperation(value = "按rootOrgId查询用户", notes = "根机构id查询机构用户")
+	@GetMapping("/rootOrg/{rootOrgId}")
+	public List<UserEntity> getUserByRootOrgId(@PathVariable long rootOrgId) {
+		List<UserEntity> userList = userRepo.findByRootOrgId(rootOrgId);
+		return userList;
+	}
+
+	/**
+	 * 重构 2018年6月26日
+	 *
+	 * @author WANGWEI
+	 * @param userForm
+	 * @return
+	 */
+	@ApiOperation(value = "新增用户", notes = "新增")
+	@PostMapping
+	@Transactional
+	public Map<String, Object> addUser(@RequestBody UserFormDomain userForm) {
+		trim(userForm, true);
+		userForm.setId(null);
+		return saveUser(userForm);
+	}
+
+	/**
+	 * 重构 2018年6月26日
+	 *
+	 * @author WANGWEI
+	 * @param userForm
+	 * @return
+	 */
+	@ApiOperation(value = "更新用户", notes = "更新")
+	@PutMapping
+	@Transactional
+	public Map<String, Object> updateUser(@RequestBody UserFormDomain userForm) {
+		trim(userForm, true);
+		if (null == userForm.getId()) {
+			throw new StatusException("150009", "user ID is null");
+		}
+
+		UserEntity userEntity = GlobalHelper.getEntity(userRepo, userForm.getId(),
+				UserEntity.class);
+		if (null == userEntity) {
+			throw new StatusException("150010", "用户不存在");
+		}
+
+		if (!userEntity.getRootOrgId().equals(userForm.getRootOrgId())) {
+			throw new StatusException("150010", "顶级机构错误");
+		}
+
+		userForm.setPassword(userEntity.getPassword());
+
+		return saveUser(userForm);
+	}
+
+	/**
+	 * 新增或更新用户
+	 *
+	 * @author WANGWEI
+	 * @param userForm
+	 * @return
+	 */
+	private Map<String, Object> saveUser(UserFormDomain userForm) {
+
+		Long rootOrgId = userForm.getRootOrgId();
+		Long orgId = userForm.getOrgId();
+
+		if (StringUtils.isBlank(userForm.getName())) {
+			throw new StatusException("130001", "用户名不能为空");
+		}
+		if (StringUtils.isBlank(userForm.getLoginName())) {
+			throw new StatusException("130002", "登陆名不能为空");
+		}
+		if (StringUtils.isBlank(userForm.getPassword())) {
+			throw new StatusException("130003", "密码不能为空");
+		}
+
+		if (null == rootOrgId) {
+			throw new StatusException("150002", "rootOrgId is null");
+		}
+		OrgEntity rootOrg = GlobalHelper.getEntity(orgRepo, rootOrgId, OrgEntity.class);
+		if (null == rootOrg) {
+			throw new StatusException("150003", "机构不存在");
+		}
+		if (null != rootOrg.getParentId()) {
+			throw new StatusException("150004", "机构错误");
+		}
+
+		validateRootOrgIsolation(rootOrgId);
+
+		if (null != orgId) {
+			OrgEntity org = GlobalHelper.getEntity(orgRepo, orgId, OrgEntity.class);
+			if (null == org) {
+				throw new StatusException("150101", "子机构不存在");
+			}
+			if (null == org.getParentId()) {
+				throw new StatusException("150102", "子机构错误");
+			}
+			if (!org.getRootId().equals(rootOrgId)) {
+				throw new StatusException("150103", "子机构错误");
+			}
+		}
+
+		UserEntity userEntity = new UserEntity();
+		userEntity.setId(userForm.getId());
+		userEntity.setEnable(userForm.getEnable());
+		userEntity.setLoginName(userForm.getLoginName());
+		userEntity.setPhoneNumber(userForm.getPhoneNumber());
+		userEntity.setName(userForm.getName());
+		userEntity.setOrgId(orgId);
+		userEntity.setRootOrgId(rootOrgId);
+		userEntity.setPassword(userForm.getPassword());
+		userEntity.setUpdateTime(new Date());
+
+		UserEntity saved = userService.save(userEntity);
+
+		List<UserRoleRelationEntity> userRoles = Lists.newArrayList();
+		List<Long> roleIds = userForm.getRoleIds();
+		for (Long cur : roleIds) {
+			RoleEntity curRoleEntity = GlobalHelper.getEntity(roleRepo, cur, RoleEntity.class);
+			if (null == curRoleEntity) {
+				throw new StatusException("150005", "角色错误");
+			}
+
+			Long roleRootOrgId = curRoleEntity.getRootOrgId();
+			if (null != roleRootOrgId && (!roleRootOrgId.equals(rootOrgId))) {
+				throw new StatusException("150006", "角色错误");
+			}
+
+			if (curRoleEntity.getCode().equals(RoleMeta.SUPER_ADMIN.name())) {
+				throw new StatusException("150007", "不允许新增或修改超级管理员");
+			}
+
+			if (curRoleEntity.getCode().equals(RoleMeta.LC_USER.name())) {
+				if (null == saved.getOrgId()) {
+					throw new StatusException("150008", "学习中心角色必须指定学习中心");
+				}
+			}
+
+			UserRoleRelationEntity relation = new UserRoleRelationEntity(saved.getId(),
+					curRoleEntity.getId());
+			userRoles.add(relation);
+		}
+
+		userRoleRelationRepo.deleteByUserId(saved.getId());
+		List<UserRoleRelationEntity> savedRelationList = userRoleRelationRepo.saveAll(userRoles);
+
+		Map<String, Object> ret = Maps.newHashMap();
+		ret.put("userId", saved.getId());
+		ret.put("loginName", saved.getLoginName());
+		ret.put("roleList", savedRelationList);
+		return ret;
+	}
+
+	/**
+	 * 判断是否是超级管理员
+	 *
+	 * @author WANGWEI
+	 * @param userId
+	 * @return
+	 */
+	private boolean isSuperAdmin(Long userId) {
+		List<UserRoleRelationEntity> relationList = userRoleRelationRepo.findAllByUserId(userId);
+		if (CollectionUtils.isNotEmpty(relationList)) {
+			for (UserRoleRelationEntity cur : relationList) {
+				Long roleId = cur.getRoleId();
+				RoleEntity roleEntity = GlobalHelper.getEntity(roleRepo, roleId, RoleEntity.class);
+				if (roleEntity.getCode().equals(RoleMeta.SUPER_ADMIN.name())) {
+					return true;
+				}
+			}
+		}
+
+		return false;
+	}
+
+	@ApiOperation(value = "重置用户密码", notes = "重置密码")
+	@PutMapping("/resetPass/{id}")
+	@Transactional
+	public void resetPass(@PathVariable String id) {
+		List<Long> ids = Stream.of(id.split(",")).map(s -> Long.parseLong(s.trim()))
+				.collect(Collectors.toList());
+
+		for (Long userId : ids) {
+			if (isSuperAdmin(userId)) {
+				throw new StatusException("150410", "超级管理员账号不允许修改");
+			}
+			UserEntity user = GlobalHelper.getEntity(userRepo, userId, UserEntity.class);
+			user.setPassword(BasicConsts.DEFAULT_PASSWORD);
+			userRepo.save(user);
+		}
+	}
+
+	@ApiOperation(value = "启用用户", notes = "启用用户")
+	@PutMapping("/enable/{ids}")
+	@Transactional
+	public List<String> enableUser(@PathVariable String ids) {
+		List<Long> userIds = Stream.of(ids.split(",")).map(s -> Long.parseLong(s.trim()))
+				.collect(Collectors.toList());
+		List<String> ret = Lists.newArrayList();
+		for (Long userId : userIds) {
+			if (isSuperAdmin(userId)) {
+				throw new StatusException("150410", "超级管理员账号不允许修改");
+			}
+			UserEntity user = GlobalHelper.getEntity(userRepo, userId, UserEntity.class);
+			user.setEnable(true);
+			userRepo.save(user);
+			ret.add(user.getId() + ":" + user.getName());
+		}
+		return ret;
+	}
+
+	@ApiOperation(value = "禁用用户", notes = "禁用用户")
+	@PutMapping("/disable/{ids}")
+	@Transactional
+	public List<String> disableUser(@PathVariable String ids) {
+		List<Long> userIds = Stream.of(ids.split(",")).map(s -> Long.parseLong(s.trim()))
+				.collect(Collectors.toList());
+		List<String> ret = Lists.newArrayList();
+		for (Long userId : userIds) {
+			if (isSuperAdmin(userId)) {
+				throw new StatusException("150410", "超级管理员账号不允许修改");
+			}
+			UserEntity user = GlobalHelper.getEntity(userRepo, userId, UserEntity.class);
+			user.setEnable(false);
+			userRepo.save(user);
+			ret.add(user.getId() + ":" + user.getName());
+		}
+		return ret;
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param userId
+	 * @param password
+	 */
+	@ApiOperation(value = "修改用户密码", notes = "修改密码")
+	@PutMapping("/password")
+	@Transactional
+	public void updatePass(@RequestParam long userId, @RequestParam String password) {
+		String realPassword = StringEscapeUtils.unescapeJavaScript(password);
+		userRepo.updatePasswordById(userId, realPassword);
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 * @param ids
+	 */
+	@ApiOperation(value = "按id删除用户", notes = "删除")
+	@DeleteMapping("/{ids}")
+	public void deleteUser(@PathVariable String ids) {
+		List<Long> userIds = Stream.of(ids.split(",")).map(s -> Long.parseLong(s.trim()))
+				.collect(Collectors.toList());
+		for (Long userId : userIds) {
+			userRepo.deleteById(userId);
+		}
+	}
+
+}

+ 87 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/CourseDomain.java

@@ -0,0 +1,87 @@
+package cn.com.qmth.examcloud.core.basic.api.controller.bean;
+
+import cn.com.qmth.examcloud.api.commons.enums.CourseLevel;
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 类注释
+ *
+ * @author WANGWEI
+ * @date 2018年8月27日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public class CourseDomain implements JsonSerializable {
+
+	private static final long serialVersionUID = -6261302618070108336L;
+
+	private Long id;
+
+	private Long rootOrgId;
+
+	private String code;
+
+	private String name;
+
+	private String displayName;
+
+	private CourseLevel level;
+
+	private Boolean enable;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+	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 getDisplayName() {
+		return displayName;
+	}
+
+	public void setDisplayName(String displayName) {
+		this.displayName = displayName;
+	}
+
+	public CourseLevel getLevel() {
+		return level;
+	}
+
+	public void setLevel(CourseLevel level) {
+		this.level = level;
+	}
+
+	public Boolean getEnable() {
+		return enable;
+	}
+
+	public void setEnable(Boolean enable) {
+		this.enable = enable;
+	}
+
+}

+ 30 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/CustomPrivilegeDomain.java

@@ -0,0 +1,30 @@
+package cn.com.qmth.examcloud.core.basic.api.controller.bean;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import cn.com.qmth.examcloud.web.helpers.tree.TreeNode;
+
+import java.util.Date;
+
+/**
+ * @Description 可自定义的PrivilegeDomain
+ * @Author lideyin
+ * @Date 2020/5/12 17:16
+ * @Version 1.0
+ */
+public class CustomPrivilegeDomain extends PrivilegeDomain {
+
+	private static final long serialVersionUID = -8828760710678965127L;
+
+	/**
+	 * 路由编码码
+	 */
+	private String routeCode;
+
+	public String getRouteCode() {
+		return routeCode;
+	}
+
+	public void setRouteCode(String routeCode) {
+		this.routeCode = routeCode;
+	}
+}

+ 115 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/ExamSiteDomain.java

@@ -0,0 +1,115 @@
+package cn.com.qmth.examcloud.core.basic.api.controller.bean;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 类注释
+ *
+ * @author WANGWEI
+ * @date 2018年8月31日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public class ExamSiteDomain implements JsonSerializable {
+
+	private static final long serialVersionUID = 1353662143659247163L;
+
+	private Long id;
+
+	private Long rootOrgId;
+
+	private Long orgId;
+
+	private String code;
+
+	private String name;
+
+	private Boolean enable;
+
+	/**
+	 * 联系电话
+	 */
+	private String telephone;
+
+	/**
+	 * 联系人
+	 */
+	private String contacts;
+
+	/**
+	 * 备注
+	 */
+	private String remark;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+	public Long getOrgId() {
+		return orgId;
+	}
+
+	public void setOrgId(Long orgId) {
+		this.orgId = orgId;
+	}
+
+	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 Boolean getEnable() {
+		return enable;
+	}
+
+	public void setEnable(Boolean enable) {
+		this.enable = enable;
+	}
+
+	public String getTelephone() {
+		return telephone;
+	}
+
+	public void setTelephone(String telephone) {
+		this.telephone = telephone;
+	}
+
+	public String getContacts() {
+		return contacts;
+	}
+
+	public void setContacts(String contacts) {
+		this.contacts = contacts;
+	}
+
+	public String getRemark() {
+		return remark;
+	}
+
+	public void setRemark(String remark) {
+		this.remark = remark;
+	}
+
+}

+ 149 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/OrgDomain.java

@@ -0,0 +1,149 @@
+package cn.com.qmth.examcloud.core.basic.api.controller.bean;
+
+import java.util.Date;
+import java.util.Map;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 类注释
+ *
+ * @author WANGWEI
+ * @date 2018年8月23日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public class OrgDomain implements JsonSerializable {
+
+	private static final long serialVersionUID = -3035023820638726879L;
+
+	private Long id;
+
+	private Long rootId;
+
+	private Long parentId;
+
+	private String name;
+
+	private String code;
+
+	private Boolean enable;
+
+	private String telephone;
+
+	private String contacts;
+
+	private String remark;
+
+	private String domainName;
+
+	private Map<String, String> properties;
+
+	private Date updateTime;
+
+	private Date creationTime;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public Long getRootId() {
+		return rootId;
+	}
+
+	public void setRootId(Long rootId) {
+		this.rootId = rootId;
+	}
+
+	public Long getParentId() {
+		return parentId;
+	}
+
+	public void setParentId(Long parentId) {
+		this.parentId = parentId;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getCode() {
+		return code;
+	}
+
+	public void setCode(String code) {
+		this.code = code;
+	}
+
+	public Boolean getEnable() {
+		return enable;
+	}
+
+	public void setEnable(Boolean enable) {
+		this.enable = enable;
+	}
+
+	public String getTelephone() {
+		return telephone;
+	}
+
+	public void setTelephone(String telephone) {
+		this.telephone = telephone;
+	}
+
+	public String getContacts() {
+		return contacts;
+	}
+
+	public void setContacts(String contacts) {
+		this.contacts = contacts;
+	}
+
+	public String getRemark() {
+		return remark;
+	}
+
+	public void setRemark(String remark) {
+		this.remark = remark;
+	}
+
+	public String getDomainName() {
+		return domainName;
+	}
+
+	public void setDomainName(String domainName) {
+		this.domainName = domainName;
+	}
+
+	public Map<String, String> getProperties() {
+		return properties;
+	}
+
+	public void setProperties(Map<String, String> properties) {
+		this.properties = properties;
+	}
+
+	public Date getUpdateTime() {
+		return updateTime;
+	}
+
+	public void setUpdateTime(Date updateTime) {
+		this.updateTime = updateTime;
+	}
+
+	public Date getCreationTime() {
+		return creationTime;
+	}
+
+	public void setCreationTime(Date creationTime) {
+		this.creationTime = creationTime;
+	}
+
+}

+ 73 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/OrgPrivilegeTreeDomain.java

@@ -0,0 +1,73 @@
+package cn.com.qmth.examcloud.core.basic.api.controller.bean;
+
+import cn.com.qmth.examcloud.api.commons.enums.PrivilegeGroupType;
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import cn.com.qmth.examcloud.web.helpers.tree.EleTreeNode;
+import cn.com.qmth.examcloud.web.helpers.tree.TreeNode;
+import java.util.List;
+
+import java.util.Date;
+
+/**
+ * @Description 组织机构权限树
+ * @Author lideyin
+ * @Date 2020/5/11 15:50
+ * @Version 1.0
+ */
+public class OrgPrivilegeTreeDomain implements JsonSerializable {
+
+	private static final long serialVersionUID = 6079872470102312782L;
+
+
+	/**
+	 * 权限组ID
+	 */
+	private Long privilegeGroupId;
+
+	/**
+	 * 权限组类型
+	 */
+	private String privilegeGroupType;
+
+	/**
+	 * 组织机构拥有的权限id
+	 */
+	private List<Long> ownedPrivilegeIds;
+
+	/**
+	 * 权限树
+	 */
+	private EleTreeNode treeData;
+
+	public Long getPrivilegeGroupId() {
+		return privilegeGroupId;
+	}
+
+	public void setPrivilegeGroupId(Long privilegeGroupId) {
+		this.privilegeGroupId = privilegeGroupId;
+	}
+
+	public String getPrivilegeGroupType() {
+		return privilegeGroupType;
+	}
+
+	public void setPrivilegeGroupType(String privilegeGroupType) {
+		this.privilegeGroupType = privilegeGroupType;
+	}
+
+	public EleTreeNode getTreeData() {
+		return treeData;
+	}
+
+	public void setTreeData(EleTreeNode treeData) {
+		this.treeData = treeData;
+	}
+
+	public List<Long> getOwnedPrivilegeIds() {
+		return ownedPrivilegeIds;
+	}
+
+	public void setOwnedPrivilegeIds(List<Long> ownedPrivilegeIds) {
+		this.ownedPrivilegeIds = ownedPrivilegeIds;
+	}
+}

+ 273 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/PrivilegeDomain.java

@@ -0,0 +1,273 @@
+package cn.com.qmth.examcloud.core.basic.api.controller.bean;
+
+import java.util.Date;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import cn.com.qmth.examcloud.web.helpers.tree.TreeNode;
+
+/**
+ * 权限信息
+ *
+ * @author WANGWEI
+ * @date 2018年5月23日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public class PrivilegeDomain implements JsonSerializable, TreeNode {
+
+	private static final long serialVersionUID = -8828760710678965127L;
+
+	/**
+	 * 权限ID
+	 */
+	private Long id;
+
+	/**
+	 * 权限码
+	 */
+	private String code;
+
+	/**
+	 * 权限名称
+	 */
+	private String name;
+
+	/**
+	 * 父权限ID
+	 */
+	private Long parentId;
+
+	/**
+	 * 组ID(权限分组,同一组的权限构成权限树)
+	 */
+	private Long groupId;
+
+	/**
+	 * 组编码
+	 */
+	private String groupCode;
+
+	/**
+	 * 是否有权限
+	 */
+	private boolean hasPrivilege;
+
+	/**
+	 * 描述
+	 */
+	private String description;
+
+	private Date updateTime;
+
+	private Date creationTime;
+
+	private Long weight = 0L;
+
+	/**
+	 * 扩展属性
+	 */
+	private String ext1;
+
+	/**
+	 * 扩展属性
+	 */
+	private String ext2;
+
+	/**
+	 * 扩展属性
+	 */
+	private String ext3;
+
+	/**
+	 * 扩展属性
+	 */
+	private String ext4;
+
+	/**
+	 * 扩展属性
+	 */
+	private String ext5;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	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 Long getParentId() {
+		return parentId;
+	}
+
+	public void setParentId(Long parentId) {
+		this.parentId = parentId;
+	}
+
+	public Long getGroupId() {
+		return groupId;
+	}
+
+	public void setGroupId(Long groupId) {
+		this.groupId = groupId;
+	}
+
+	public String getGroupCode() {
+		return groupCode;
+	}
+
+	public void setGroupCode(String groupCode) {
+		this.groupCode = groupCode;
+	}
+
+	public boolean isHasPrivilege() {
+		return hasPrivilege;
+	}
+
+	public void setHasPrivilege(boolean hasPrivilege) {
+		this.hasPrivilege = hasPrivilege;
+	}
+
+	public String getDescription() {
+		return description;
+	}
+
+	public void setDescription(String description) {
+		this.description = description;
+	}
+
+	public Date getUpdateTime() {
+		return updateTime;
+	}
+
+	public void setUpdateTime(Date updateTime) {
+		this.updateTime = updateTime;
+	}
+
+	public Date getCreationTime() {
+		return creationTime;
+	}
+
+	public void setCreationTime(Date creationTime) {
+		this.creationTime = creationTime;
+	}
+
+	public Long getWeight() {
+		return weight;
+	}
+
+	public void setWeight(Long weight) {
+		this.weight = weight;
+	}
+
+	public String getExt1() {
+		return ext1;
+	}
+
+	public void setExt1(String ext1) {
+		this.ext1 = ext1;
+	}
+
+	public String getExt2() {
+		return ext2;
+	}
+
+	public void setExt2(String ext2) {
+		this.ext2 = ext2;
+	}
+
+	public String getExt3() {
+		return ext3;
+	}
+
+	public void setExt3(String ext3) {
+		this.ext3 = ext3;
+	}
+
+	public String getExt4() {
+		return ext4;
+	}
+
+	public void setExt4(String ext4) {
+		this.ext4 = ext4;
+	}
+
+	public String getExt5() {
+		return ext5;
+	}
+
+	public void setExt5(String ext5) {
+		this.ext5 = ext5;
+	}
+
+	@Override
+	public String getNodeId() {
+		if (null == this.id) {
+			return null;
+		}
+		return String.valueOf(this.id);
+	}
+
+	@Override
+	public void setNodeId(String nodeId) {
+		if (null == nodeId) {
+			this.id = null;
+		}
+		this.id = Long.parseLong(nodeId);
+	}
+
+	@Override
+	public String getNodeName() {
+		if (null == this.name) {
+			return null;
+		}
+		return String.valueOf(this.name);
+	}
+
+	@Override
+	public void setNodeName(String nodeName) {
+		this.name = nodeName;
+	}
+
+	@Override
+	public String getParentNodeId() {
+		if (null == this.parentId) {
+			return null;
+		}
+		return String.valueOf(this.parentId);
+	}
+
+	@Override
+	public void setParentNodeId(String parentNodeId) {
+		if (null == parentNodeId) {
+			this.parentId = null;
+		}
+		this.parentId = Long.parseLong(parentNodeId);
+	}
+
+	@Override
+	public String getNodeCode() {
+		return this.code;
+	}
+
+	@Override
+	public void setNodeCode(String code) {
+		this.code = code;
+	}
+
+}

+ 153 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/PrivilegeGroupDomain.java

@@ -0,0 +1,153 @@
+package cn.com.qmth.examcloud.core.basic.api.controller.bean;
+
+import javax.persistence.Column;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 权限组
+ *
+ * @author WANGWEI
+ * @date 2018年5月23日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public class PrivilegeGroupDomain implements JsonSerializable {
+
+	private static final long serialVersionUID = -3654724059677675683L;
+
+	/**
+	 * 权限组ID
+	 */
+	private Long id;
+
+	/**
+	 * 权限组名称
+	 */
+	private String name;
+
+	/**
+	 * 权限组编码
+	 */
+	private String code;
+
+	/**
+	 * 顶级机构ID
+	 */
+	private Long rootOrgId;
+
+	/**
+	 * 顶级机构名称
+	 */
+	private String rootOrgName;
+
+	/**
+	 * 扩展属性
+	 */
+	@Column(unique = false, nullable = true)
+	private String ext1;
+
+	/**
+	 * 扩展属性
+	 */
+	@Column(unique = false, nullable = true)
+	private String ext2;
+
+	/**
+	 * 扩展属性
+	 */
+	@Column(unique = false, nullable = true)
+	private String ext3;
+
+	/**
+	 * 扩展属性
+	 */
+	@Column(unique = false, nullable = true)
+	private String ext4;
+
+	/**
+	 * 扩展属性
+	 */
+	@Column(unique = false, nullable = true)
+	private String ext5;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getCode() {
+		return code;
+	}
+
+	public void setCode(String code) {
+		this.code = code;
+	}
+
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+	public String getRootOrgName() {
+		return rootOrgName;
+	}
+
+	public void setRootOrgName(String rootOrgName) {
+		this.rootOrgName = rootOrgName;
+	}
+
+	public String getExt1() {
+		return ext1;
+	}
+
+	public void setExt1(String ext1) {
+		this.ext1 = ext1;
+	}
+
+	public String getExt2() {
+		return ext2;
+	}
+
+	public void setExt2(String ext2) {
+		this.ext2 = ext2;
+	}
+
+	public String getExt3() {
+		return ext3;
+	}
+
+	public void setExt3(String ext3) {
+		this.ext3 = ext3;
+	}
+
+	public String getExt4() {
+		return ext4;
+	}
+
+	public void setExt4(String ext4) {
+		this.ext4 = ext4;
+	}
+
+	public String getExt5() {
+		return ext5;
+	}
+
+	public void setExt5(String ext5) {
+		this.ext5 = ext5;
+	}
+
+}

+ 113 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/ResourceDomain.java

@@ -0,0 +1,113 @@
+package cn.com.qmth.examcloud.core.basic.api.controller.bean;
+
+import java.util.Date;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+public class ResourceDomain implements JsonSerializable {
+
+	private static final long serialVersionUID = -8364653908915038360L;
+
+	private Long id;
+
+	private Long rootOrgId;
+
+	private String name;
+
+	private Boolean isFile;
+
+	private String suffix;
+
+	private String filePath;
+
+	private String fileUrl;
+
+	private Long parentId;
+
+	private Date creationTime;
+	
+	private String fileTreatyPath;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public Boolean getIsFile() {
+		return isFile;
+	}
+
+	public void setIsFile(Boolean isFile) {
+		this.isFile = isFile;
+	}
+
+	public String getSuffix() {
+		return suffix;
+	}
+
+	public void setSuffix(String suffix) {
+		this.suffix = suffix;
+	}
+
+	public String getFilePath() {
+		return filePath;
+	}
+
+	public void setFilePath(String filePath) {
+		this.filePath = filePath;
+	}
+
+	public String getFileUrl() {
+		return fileUrl;
+	}
+
+	public void setFileUrl(String fileUrl) {
+		this.fileUrl = fileUrl;
+	}
+
+	public Long getParentId() {
+		return parentId;
+	}
+
+	public void setParentId(Long parentId) {
+		this.parentId = parentId;
+	}
+
+	public Date getCreationTime() {
+		return creationTime;
+	}
+
+	public void setCreationTime(Date creationTime) {
+		this.creationTime = creationTime;
+	}
+
+    
+    public String getFileTreatyPath() {
+        return fileTreatyPath;
+    }
+
+    
+    public void setFileTreatyPath(String fileTreatyPath) {
+        this.fileTreatyPath = fileTreatyPath;
+    }
+
+}

+ 68 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/RoleDomain.java

@@ -0,0 +1,68 @@
+package cn.com.qmth.examcloud.core.basic.api.controller.bean;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 角色
+ *
+ * @author WANGWEI
+ * @date 2018年5月23日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public class RoleDomain implements JsonSerializable {
+
+	private static final long serialVersionUID = -2167420238674588632L;
+
+	/**
+	 * 角色ID
+	 */
+	private Long id;
+
+	/**
+	 * 角色码
+	 */
+	private String code;
+
+	/**
+	 * 角色名称
+	 */
+	private String name;
+
+	/**
+	 * 顶级机构.为null时,为全局角色
+	 */
+	private Long rootOrgId;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	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 Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+}

+ 49 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/SaveOrgPropertiesDomain.java

@@ -0,0 +1,49 @@
+package cn.com.qmth.examcloud.core.basic.api.controller.bean;
+
+import java.util.List;
+import java.util.Map;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 类注释
+ *
+ * @author WANGWEI
+ * @date 2018年8月23日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public class SaveOrgPropertiesDomain implements JsonSerializable {
+
+	private static final long serialVersionUID = -3035023820638726879L;
+
+	private Long orgId;
+
+	private Map<String, String> properties;
+
+	private List<String> relatedPropertyGroupIdList;
+
+	public Long getOrgId() {
+		return orgId;
+	}
+
+	public void setOrgId(Long orgId) {
+		this.orgId = orgId;
+	}
+
+	public Map<String, String> getProperties() {
+		return properties;
+	}
+
+	public void setProperties(Map<String, String> properties) {
+		this.properties = properties;
+	}
+
+	public List<String> getRelatedPropertyGroupIdList() {
+		return relatedPropertyGroupIdList;
+	}
+
+	public void setRelatedPropertyGroupIdList(List<String> relatedPropertyGroupIdList) {
+		this.relatedPropertyGroupIdList = relatedPropertyGroupIdList;
+	}
+
+}

+ 59 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/SpecialtyDomain.java

@@ -0,0 +1,59 @@
+package cn.com.qmth.examcloud.core.basic.api.controller.bean;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+public class SpecialtyDomain implements JsonSerializable {
+
+	private static final long serialVersionUID = 239263456816448160L;
+
+	private Long id;
+
+	private String code;
+
+	private String name;
+
+	private Long rootOrgId;
+
+	private Boolean enable;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	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 Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+	public Boolean getEnable() {
+		return enable;
+	}
+
+	public void setEnable(Boolean enable) {
+		this.enable = enable;
+	}
+
+}

+ 199 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/StudentDomain.java

@@ -0,0 +1,199 @@
+package cn.com.qmth.examcloud.core.basic.api.controller.bean;
+
+import java.util.Date;
+import java.util.List;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 类注释
+ *
+ * @author WANGWEI
+ * @date 2018年8月22日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public class StudentDomain implements JsonSerializable {
+
+	private static final long serialVersionUID = 757531976286006550L;
+
+	private Long id;
+
+	private String name;
+
+	private String password;
+
+	private List<String> studentCodeList;
+
+	private String studentCodesStr;
+
+	private String identityNumber;
+
+	private String privateIdentityNumber;//隐私身份证
+
+	private String photoPath;
+
+	private String remark;
+
+	private Boolean enable;
+
+	private Long orgId;
+
+	private String orgCode;
+
+	private String orgName;
+
+	private Long rootOrgId;
+
+	private String phoneNumber;
+
+	private String securityPhone;
+
+	private Date updateTime;
+
+	private Date creationTime;
+
+	public String getPrivateIdentityNumber() {
+		return privateIdentityNumber;
+	}
+
+	public void setPrivateIdentityNumber(String privateIdentityNumber) {
+		this.privateIdentityNumber = privateIdentityNumber;
+	}
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getPassword() {
+		return password;
+	}
+
+	public void setPassword(String password) {
+		this.password = password;
+	}
+
+	public String getStudentCodesStr() {
+		return studentCodesStr;
+	}
+
+	public void setStudentCodesStr(String studentCodesStr) {
+		this.studentCodesStr = studentCodesStr;
+	}
+
+	public List<String> getStudentCodeList() {
+		return studentCodeList;
+	}
+
+	public void setStudentCodeList(List<String> studentCodeList) {
+		this.studentCodeList = studentCodeList;
+	}
+
+	public String getIdentityNumber() {
+		return identityNumber;
+	}
+
+	public void setIdentityNumber(String identityNumber) {
+		this.identityNumber = identityNumber;
+	}
+
+	public String getPhotoPath() {
+		return photoPath;
+	}
+
+	public void setPhotoPath(String photoPath) {
+		this.photoPath = photoPath;
+	}
+
+	public String getRemark() {
+		return remark;
+	}
+
+	public void setRemark(String remark) {
+		this.remark = remark;
+	}
+
+	public Boolean getEnable() {
+		return enable;
+	}
+
+	public void setEnable(Boolean enable) {
+		this.enable = enable;
+	}
+
+	public Long getOrgId() {
+		return orgId;
+	}
+
+	public void setOrgId(Long orgId) {
+		this.orgId = orgId;
+	}
+
+	public String getOrgName() {
+		return orgName;
+	}
+
+	public void setOrgName(String orgName) {
+		this.orgName = orgName;
+	}
+
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+	public String getPhoneNumber() {
+		return phoneNumber;
+	}
+
+	public void setPhoneNumber(String phoneNumber) {
+		this.phoneNumber = phoneNumber;
+	}
+
+	public String getSecurityPhone() {
+		return securityPhone;
+	}
+
+	public void setSecurityPhone(String securityPhone) {
+		this.securityPhone = securityPhone;
+	}
+
+	public Date getUpdateTime() {
+		return updateTime;
+	}
+
+	public void setUpdateTime(Date updateTime) {
+		this.updateTime = updateTime;
+	}
+
+	public Date getCreationTime() {
+		return creationTime;
+	}
+
+	public void setCreationTime(Date creationTime) {
+		this.creationTime = creationTime;
+	}
+
+	public String getOrgCode() {
+		return orgCode;
+	}
+
+	public void setOrgCode(String orgCode) {
+		this.orgCode = orgCode;
+	}
+
+}

+ 86 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/StudentFaceDomain.java

@@ -0,0 +1,86 @@
+package cn.com.qmth.examcloud.core.basic.api.controller.bean;
+
+import cn.com.qmth.examcloud.api.commons.exchange.BaseRequest;
+
+/**
+ * 类注释
+ *
+ * @author WANGWEI
+ * @date 2018年7月18日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public class StudentFaceDomain extends BaseRequest {
+
+	private static final long serialVersionUID = -8260481601116583797L;
+
+	private Long rootOrgId;
+
+	private Long studentId;
+
+	private String faceToken;
+
+	private String facesetToken;
+
+	private String photoName;
+
+	private String operator;
+
+	private Long faceCount;
+
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+	public Long getStudentId() {
+		return studentId;
+	}
+
+	public void setStudentId(Long studentId) {
+		this.studentId = studentId;
+	}
+
+	public String getFaceToken() {
+		return faceToken;
+	}
+
+	public void setFaceToken(String faceToken) {
+		this.faceToken = faceToken;
+	}
+
+	public String getFacesetToken() {
+		return facesetToken;
+	}
+
+	public void setFacesetToken(String facesetToken) {
+		this.facesetToken = facesetToken;
+	}
+
+	public String getPhotoName() {
+		return photoName;
+	}
+
+	public void setPhotoName(String photoName) {
+		this.photoName = photoName;
+	}
+
+	public String getOperator() {
+		return operator;
+	}
+
+	public void setOperator(String operator) {
+		this.operator = operator;
+	}
+
+	public Long getFaceCount() {
+		return faceCount;
+	}
+
+	public void setFaceCount(Long faceCount) {
+		this.faceCount = faceCount;
+	}
+
+}

+ 66 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/SystemPropertyDomain.java

@@ -0,0 +1,66 @@
+package cn.com.qmth.examcloud.core.basic.api.controller.bean;
+
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.validation.constraints.NotNull;
+
+import cn.com.qmth.examcloud.api.commons.enums.BasicDataType;
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * 系统参数信息
+ */
+public class SystemPropertyDomain implements JsonSerializable {
+	private static final long serialVersionUID = 4454755569906692085L;
+
+	@NotNull(message = "参数名称不能为空")
+	@ApiModelProperty(value = "参数名称")
+	private String propKey;
+
+	@NotNull(message = "参数描述不能为空")
+	@ApiModelProperty(value = "参数描述")
+	private String description;
+
+	@NotNull(message = "参数类型不能为空")
+	@ApiModelProperty(value = "参数类型")
+
+	@Enumerated(EnumType.STRING)
+	private BasicDataType propValueType;
+
+	@NotNull(message = "参数值不能为空")
+	@ApiModelProperty(value = "参数值")
+	private String propValue;
+
+	public String getPropKey() {
+		return propKey;
+	}
+
+	public void setPropKey(String propKey) {
+		this.propKey = propKey;
+	}
+
+	public String getDescription() {
+		return description;
+	}
+
+	public void setDescription(String description) {
+		this.description = description;
+	}
+
+	public BasicDataType getPropValueType() {
+		return propValueType;
+	}
+
+	public void setPropValueType(BasicDataType propValueType) {
+		this.propValueType = propValueType;
+	}
+
+	public String getPropValue() {
+		return propValue;
+	}
+
+	public void setPropValue(String propValue) {
+		this.propValue = propValue;
+	}
+}

+ 58 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/UpdateRolePrivilegeRelationsDomain.java

@@ -0,0 +1,58 @@
+package cn.com.qmth.examcloud.core.basic.api.controller.bean;
+
+import java.util.Set;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 更新角色权限关联 请求
+ *
+ * @author WANGWEI
+ * @date 2018年6月14日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public class UpdateRolePrivilegeRelationsDomain implements JsonSerializable {
+
+	private static final long serialVersionUID = -9173265174568713167L;
+
+	private String rootOrgId;
+
+	private String roleId;
+
+	private String privilegeGroupId;
+
+	private Set<Long> privilegeIdSet;
+
+	public String getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(String rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+	public String getRoleId() {
+		return roleId;
+	}
+
+	public void setRoleId(String roleId) {
+		this.roleId = roleId;
+	}
+
+	public String getPrivilegeGroupId() {
+		return privilegeGroupId;
+	}
+
+	public void setPrivilegeGroupId(String privilegeGroupId) {
+		this.privilegeGroupId = privilegeGroupId;
+	}
+
+	public Set<Long> getPrivilegeIdSet() {
+		return privilegeIdSet;
+	}
+
+	public void setPrivilegeIdSet(Set<Long> privilegeIdSet) {
+		this.privilegeIdSet = privilegeIdSet;
+	}
+
+}

+ 41 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/UpdateRootOrgPrivilegeRelationsDomain.java

@@ -0,0 +1,41 @@
+package cn.com.qmth.examcloud.core.basic.api.controller.bean;
+
+import java.util.Set;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+public class UpdateRootOrgPrivilegeRelationsDomain implements JsonSerializable {
+
+	private static final long serialVersionUID = -8907927108545093675L;
+
+	private String rootOrgId;
+
+	private Long privilegeGroupId;
+
+	private Set<Long> privilegeIdSet;
+
+	public String getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(String rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+	public Long getPrivilegeGroupId() {
+		return privilegeGroupId;
+	}
+
+	public void setPrivilegeGroupId(Long privilegeGroupId) {
+		this.privilegeGroupId = privilegeGroupId;
+	}
+
+	public Set<Long> getPrivilegeIdSet() {
+		return privilegeIdSet;
+	}
+
+	public void setPrivilegeIdSet(Set<Long> privilegeIdSet) {
+		this.privilegeIdSet = privilegeIdSet;
+	}
+
+}

+ 172 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/UserDomain.java

@@ -0,0 +1,172 @@
+package cn.com.qmth.examcloud.core.basic.api.controller.bean;
+
+import java.util.Date;
+import java.util.List;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 全量用户信息
+ *
+ * @author WANGWEI
+ * @date 2018年6月25日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public class UserDomain implements JsonSerializable {
+
+	private static final long serialVersionUID = -1326157272669439949L;
+
+	private Long id;
+
+	private Long rootOrgId;
+
+	private String rootOrgName;
+
+	private Long orgId;
+
+	private String orgCode;
+
+	private String orgName;
+
+	private String name;
+
+	private String loginName;
+
+	/**
+	 * 角色合集
+	 */
+	private String roleNamesStr;
+
+	private Boolean enable;
+
+	private List<Long> roleIds;
+
+	private List<String> roleCodes;
+
+	private Date updateTime;
+
+	private Date creationTime;
+
+	private String phoneNumber;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public List<String> getRoleCodes() {
+		return roleCodes;
+	}
+
+	public void setRoleCodes(List<String> roleCodes) {
+		this.roleCodes = roleCodes;
+	}
+
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+	public String getRootOrgName() {
+		return rootOrgName;
+	}
+
+	public void setRootOrgName(String rootOrgName) {
+		this.rootOrgName = rootOrgName;
+	}
+
+	public Long getOrgId() {
+		return orgId;
+	}
+
+	public void setOrgId(Long orgId) {
+		this.orgId = orgId;
+	}
+
+	public String getOrgName() {
+		return orgName;
+	}
+
+	public void setOrgName(String orgName) {
+		this.orgName = orgName;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getLoginName() {
+		return loginName;
+	}
+
+	public void setLoginName(String loginName) {
+		this.loginName = loginName;
+	}
+
+	public String getRoleNamesStr() {
+		return roleNamesStr;
+	}
+
+	public void setRoleNamesStr(String roleNamesStr) {
+		this.roleNamesStr = roleNamesStr;
+	}
+
+	public Boolean getEnable() {
+		return enable;
+	}
+
+	public void setEnable(Boolean enable) {
+		this.enable = enable;
+	}
+
+	public List<Long> getRoleIds() {
+		return roleIds;
+	}
+
+	public void setRoleIds(List<Long> roleIds) {
+		this.roleIds = roleIds;
+	}
+
+	public Date getUpdateTime() {
+		return updateTime;
+	}
+
+	public void setUpdateTime(Date updateTime) {
+		this.updateTime = updateTime;
+	}
+
+	public Date getCreationTime() {
+		return creationTime;
+	}
+
+	public void setCreationTime(Date creationTime) {
+		this.creationTime = creationTime;
+	}
+
+	public String getOrgCode() {
+		return orgCode;
+	}
+
+	public void setOrgCode(String orgCode) {
+		this.orgCode = orgCode;
+	}
+
+	public String getPhoneNumber() {
+		return phoneNumber;
+	}
+
+	public void setPhoneNumber(String phoneNumber) {
+		this.phoneNumber = phoneNumber;
+	}
+
+}

+ 128 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/UserFormDomain.java

@@ -0,0 +1,128 @@
+package cn.com.qmth.examcloud.core.basic.api.controller.bean;
+
+import java.util.List;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 用户表单
+ *
+ * @author WANGWEI
+ * @date 2018年6月25日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public class UserFormDomain implements JsonSerializable {
+
+	private static final long serialVersionUID = -1326157272669439949L;
+
+	private Long id;
+
+	private Long rootOrgId;
+
+	private String rootOrgName;
+
+	private Long orgId;
+
+	private String orgName;
+
+	private String name;
+
+	private String loginName;
+
+	private String phoneNumber;
+
+	private Boolean enable;
+
+	private String password;
+
+	private List<Long> roleIds;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public String getPassword() {
+		return password;
+	}
+
+	public void setPassword(String password) {
+		this.password = password;
+	}
+
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+	public String getRootOrgName() {
+		return rootOrgName;
+	}
+
+	public void setRootOrgName(String rootOrgName) {
+		this.rootOrgName = rootOrgName;
+	}
+
+	public Long getOrgId() {
+		return orgId;
+	}
+
+	public void setOrgId(Long orgId) {
+		this.orgId = orgId;
+	}
+
+	public String getOrgName() {
+		return orgName;
+	}
+
+	public void setOrgName(String orgName) {
+		this.orgName = orgName;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getLoginName() {
+		return loginName;
+	}
+
+	public void setLoginName(String loginName) {
+		this.loginName = loginName;
+	}
+
+	public Boolean getEnable() {
+		return enable;
+	}
+
+	public void setEnable(Boolean enable) {
+		this.enable = enable;
+	}
+
+	public String getPhoneNumber() {
+		return phoneNumber;
+	}
+
+	public void setPhoneNumber(String phoneNumber) {
+		this.phoneNumber = phoneNumber;
+	}
+
+	public List<Long> getRoleIds() {
+		return roleIds;
+	}
+
+	public void setRoleIds(List<Long> roleIds) {
+		this.roleIds = roleIds;
+	}
+
+}

+ 41 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/provider/AuthCloudServiceProvider.java

@@ -0,0 +1,41 @@
+package cn.com.qmth.examcloud.core.basic.api.provider;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.api.commons.security.bean.UserType;
+import cn.com.qmth.examcloud.core.basic.api.AuthCloudService;
+import cn.com.qmth.examcloud.core.basic.api.request.LoginReq;
+import cn.com.qmth.examcloud.core.basic.api.response.LoginResp;
+import cn.com.qmth.examcloud.core.basic.service.AuthService;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.ApiOperation;
+
+@RestController
+@RequestMapping("${$rmp.cloud.basic}" + "auth")
+public class AuthCloudServiceProvider extends ControllerSupport implements AuthCloudService {
+
+	private static final long serialVersionUID = 4189424508654646499L;
+
+	@Autowired
+	AuthService authService;
+
+	@ApiOperation(value = "登录")
+	@PostMapping("login")
+	@Override
+	public LoginResp login(@RequestBody LoginReq req) {
+		Long userId = req.getUserId();
+		UserType userType = req.getUserType();
+
+		User user = authService.login(userType, userId);
+
+		LoginResp resp = new LoginResp();
+		resp.setUser(user);
+		return resp;
+	}
+
+}

+ 161 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/provider/CourseCloudServiceProvider.java

@@ -0,0 +1,161 @@
+package cn.com.qmth.examcloud.core.basic.api.provider;
+
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.google.common.collect.Lists;
+
+import cn.com.qmth.examcloud.api.commons.enums.CourseLevel;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.basic.api.CourseCloudService;
+import cn.com.qmth.examcloud.core.basic.api.bean.CourseBean;
+import cn.com.qmth.examcloud.core.basic.api.request.GetCourseReq;
+import cn.com.qmth.examcloud.core.basic.api.request.GetCoursesByIdListReq;
+import cn.com.qmth.examcloud.core.basic.api.request.SaveCourseReq;
+import cn.com.qmth.examcloud.core.basic.api.response.GetCourseResp;
+import cn.com.qmth.examcloud.core.basic.api.response.GetCoursesByIdListResp;
+import cn.com.qmth.examcloud.core.basic.api.response.SaveCourseResp;
+import cn.com.qmth.examcloud.core.basic.dao.CourseRepo;
+import cn.com.qmth.examcloud.core.basic.dao.OrgRepo;
+import cn.com.qmth.examcloud.core.basic.dao.entity.CourseEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.OrgEntity;
+import cn.com.qmth.examcloud.core.basic.service.CourseService;
+import cn.com.qmth.examcloud.core.basic.service.bean.CourseInfo;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * {@link StatusException} 状态码范围:011XXX<br>
+ *
+ * @author WANGWEI
+ * @date 2018年7月10日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@RestController
+@RequestMapping("${$rmp.cloud.basic}" + "course")
+public class CourseCloudServiceProvider implements CourseCloudService {
+
+	private static final long serialVersionUID = -2733647860311223743L;
+
+	@Autowired
+	OrgRepo orgRepo;
+
+	@Autowired
+	CourseRepo courseRepo;
+
+	@Autowired
+	CourseService courseService;
+
+	@ApiOperation(value = "保存课程")
+	@PostMapping("saveCourse")
+	@Transactional
+	@Override
+	public SaveCourseResp saveCourse(@RequestBody SaveCourseReq courseReq) {
+
+		CourseInfo info = new CourseInfo();
+		info.setRootOrgId(courseReq.getRootOrgId());
+		info.setCode(courseReq.getCourseCode());
+		info.setEnable(courseReq.getEnable());
+		info.setId(courseReq.getCourseId());
+		info.setName(courseReq.getCourseName());
+		if (null != courseReq.getCourseLevel()) {
+			info.setLevel(CourseLevel.getCourseLevel(courseReq.getCourseLevel()));
+		}
+
+		CourseEntity saved = courseService.saveCourse(info);
+
+		SaveCourseResp resp = new SaveCourseResp();
+		CourseBean courseBean = new CourseBean();
+		courseBean.setId(saved.getId());
+		courseBean.setCode(saved.getCode());
+		courseBean.setLevel(saved.getLevel().name());
+		courseBean.setName(saved.getName());
+		courseBean.setRootOrgId(saved.getRootOrgId());
+		courseBean.setEnable(saved.getEnable());
+
+		resp.setCourseBean(courseBean);
+
+		return resp;
+	}
+
+	@ApiOperation(value = "查询课程")
+	@PostMapping("getCourse")
+	@Override
+	public GetCourseResp getCourse(@RequestBody GetCourseReq req) {
+		GetCourseResp resp = new GetCourseResp();
+		Long rootOrgId = req.getRootOrgId();
+		String code = req.getCode();
+		Long id = req.getId();
+
+		if (null == rootOrgId) {
+			throw new StatusException("160000", "rootOrgId is null");
+		}
+		OrgEntity rootOrg = GlobalHelper.getEntity(orgRepo, rootOrgId, OrgEntity.class);
+		if (null == rootOrg) {
+			throw new StatusException("160001", "机构不存在");
+		}
+		if (null != rootOrg.getParentId()) {
+			throw new StatusException("160002", "机构错误");
+		}
+
+		CourseEntity c = null;
+		if (null != id) {
+			c = GlobalHelper.getEntity(courseRepo, id, CourseEntity.class);
+
+		} else if (StringUtils.isNotBlank(code)) {
+			c = courseRepo.findByRootOrgIdAndCode(rootOrgId, code);
+		} else {
+			throw new StatusException("160002", "id,code can not be all null");
+		}
+
+		if (null == c) {
+			throw new StatusException("160003", "课程不存在");
+		}
+
+		CourseBean courseBean = new CourseBean();
+		courseBean.setId(c.getId());
+		courseBean.setCode(c.getCode());
+		courseBean.setLevel(c.getLevel().name());
+		courseBean.setName(c.getName());
+		courseBean.setRootOrgId(c.getRootOrgId());
+		courseBean.setEnable(c.getEnable());
+		resp.setCourseBean(courseBean);
+
+		return resp;
+	}
+
+	@ApiOperation(value = "按ID查询课程")
+	@PostMapping("getCoursesByIdList")
+	@Override
+	public GetCoursesByIdListResp getCoursesByIdList(@RequestBody GetCoursesByIdListReq req) {
+		List<Long> courseIdList = req.getCourseIdList();
+		final List<CourseBean> list = Lists.newArrayList();
+		for (Long cur : courseIdList) {
+			CourseEntity c = GlobalHelper.getEntity(courseRepo, cur, CourseEntity.class);
+			if (null == c) {
+				throw new StatusException("160001", "no course [id=" + cur + "]");
+			}
+			CourseBean courseBean = new CourseBean();
+			courseBean.setId(c.getId());
+			courseBean.setCode(c.getCode());
+			courseBean.setLevel(c.getLevel().name());
+			courseBean.setName(c.getName());
+			courseBean.setRootOrgId(c.getRootOrgId());
+			courseBean.setEnable(c.getEnable());
+
+			list.add(courseBean);
+		}
+
+		GetCoursesByIdListResp resp = new GetCoursesByIdListResp();
+		resp.setCourseList(list);
+		return resp;
+	}
+
+}

+ 156 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/provider/FaceCloudServiceProvider.java

@@ -0,0 +1,156 @@
+package cn.com.qmth.examcloud.core.basic.api.provider;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.google.common.collect.Lists;
+
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.basic.api.FaceCloudService;
+import cn.com.qmth.examcloud.core.basic.api.bean.FacesetBean;
+import cn.com.qmth.examcloud.core.basic.api.bean.StudentFaceBean;
+import cn.com.qmth.examcloud.core.basic.api.request.GetStudentFaceReq;
+import cn.com.qmth.examcloud.core.basic.api.request.GetUsableFacesetListReq;
+import cn.com.qmth.examcloud.core.basic.api.request.SaveStudentFaceReq;
+import cn.com.qmth.examcloud.core.basic.api.response.GetStudentFaceResp;
+import cn.com.qmth.examcloud.core.basic.api.response.GetUsableFacesetListResp;
+import cn.com.qmth.examcloud.core.basic.api.response.SaveStudentFaceResp;
+import cn.com.qmth.examcloud.core.basic.dao.FacesetRepo;
+import cn.com.qmth.examcloud.core.basic.dao.StudentFaceRepo;
+import cn.com.qmth.examcloud.core.basic.dao.StudentRepo;
+import cn.com.qmth.examcloud.core.basic.dao.entity.FacesetEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.StudentFaceEntity;
+import cn.com.qmth.examcloud.core.basic.service.FaceService;
+import cn.com.qmth.examcloud.core.basic.service.bean.StudentFaceInfo;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * 类注释
+ *
+ * @author WANGWEI
+ * @date 2018年8月14日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@RestController
+@RequestMapping("${$rmp.cloud.basic}" + "studentFace")
+public class FaceCloudServiceProvider extends ControllerSupport implements FaceCloudService {
+
+	private static final long serialVersionUID = 8674317301051207328L;
+
+	@Autowired
+	StudentFaceRepo studentFaceRepo;
+
+	@Autowired
+	FacesetRepo facesetRepo;
+
+	@Autowired
+	StudentRepo studentRepo;
+
+	@Autowired
+	FaceService studentFaceService;
+
+	@ApiOperation(value = "保存学生人脸数据")
+	@PostMapping("saveStudentFace")
+	@Transactional
+	@Override
+	public SaveStudentFaceResp saveStudentFace(@RequestBody SaveStudentFaceReq req) {
+		trim(req);
+		StudentFaceInfo info = new StudentFaceInfo();
+		info.setFacesetToken(req.getFacesetToken());
+		info.setFaceToken(req.getFaceToken());
+		info.setOperator(req.getOperator());
+		info.setPhotoName(req.getPhotoName());
+		info.setRootOrgId(req.getRootOrgId());
+		info.setStudentId(req.getStudentId());
+		info.setFaceCount(req.getFaceCount());
+		info.setPhotoTreatyPath(req.getPhotoTreatyPath());
+
+		StudentFaceEntity saved = studentFaceService.saveStudentFace(info);
+		SaveStudentFaceResp resp = new SaveStudentFaceResp();
+		resp.setId(saved.getStudentId());
+		return resp;
+	}
+
+	@ApiOperation(value = "获取可用的faceset集合")
+	@PostMapping("getUsableFacesetList")
+	@Override
+	public GetUsableFacesetListResp getUsableFacesetList(@RequestBody GetUsableFacesetListReq req) {
+
+		List<FacesetEntity> facesetList = studentFaceService.getUsableFacesetList();
+
+		List<FacesetBean> list = Lists.newArrayList();
+
+		for (FacesetEntity cur : facesetList) {
+			FacesetBean bean = new FacesetBean();
+			list.add(bean);
+
+			bean.setDisplayName(cur.getDisplayName());
+			bean.setFaceCount(cur.getFaceCount());
+			bean.setFacesetToken(cur.getFacesetToken());
+			bean.setId(cur.getId());
+			bean.setOuterId(cur.getOuterId());
+			bean.setTags(cur.getTags());
+		}
+
+		GetUsableFacesetListResp resp = new GetUsableFacesetListResp();
+		resp.setFacesetBeanList(list);
+		return resp;
+	}
+
+	@ApiOperation(value = "获取学生人脸数据")
+	@PostMapping("getStudentFace")
+	@Override
+	public GetStudentFaceResp getStudentFace(@RequestBody GetStudentFaceReq req) {
+		Long studentId = req.getStudentId();
+
+		if (null == studentId) {
+			throw new StatusException("710001", "studentId is null");
+		}
+
+		StudentFaceEntity studentFaceEntity = GlobalHelper.getEntity(studentFaceRepo, studentId,
+				StudentFaceEntity.class);
+
+		if (null == studentFaceEntity) {
+			throw new StatusException("710001", "studentFaceEntity is null");
+		}
+
+		StudentFaceBean studentFaceBean = new StudentFaceBean();
+
+		studentFaceBean.setCreator(studentFaceEntity.getCreator());
+		studentFaceBean.setFacesetId(studentFaceEntity.getFacesetId());
+		studentFaceBean.setFaceToken(studentFaceEntity.getFaceToken());
+		studentFaceBean.setModifiedBy(studentFaceEntity.getModifiedBy());
+		studentFaceBean.setStudentId(studentFaceEntity.getStudentId());
+
+		GetStudentFaceResp resp = new GetStudentFaceResp();
+		resp.setStudentFaceBean(studentFaceBean);
+
+		if (null != studentFaceEntity.getFacesetId()) {
+			FacesetEntity facesetEntity = GlobalHelper.getEntity(facesetRepo,
+					studentFaceEntity.getFacesetId(), FacesetEntity.class);
+			if (null != facesetEntity) {
+				FacesetBean facesetBean = new FacesetBean();
+				facesetBean.setDisplayName(facesetEntity.getDisplayName());
+				facesetBean.setFaceCount(facesetEntity.getFaceCount());
+				facesetBean.setFacesetToken(facesetEntity.getFacesetToken());
+				facesetBean.setId(facesetEntity.getId());
+				facesetBean.setOuterId(facesetEntity.getOuterId());
+				facesetBean.setTags(facesetEntity.getTags());
+
+				resp.setFacesetBean(facesetBean);
+			}
+
+		}
+
+		return resp;
+	}
+
+}

+ 220 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/provider/OrgCloudServiceProvider.java

@@ -0,0 +1,220 @@
+package cn.com.qmth.examcloud.core.basic.api.provider;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.persistence.criteria.Predicate;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.google.common.collect.Lists;
+
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.basic.api.OrgCloudService;
+import cn.com.qmth.examcloud.core.basic.api.bean.OrgBean;
+import cn.com.qmth.examcloud.core.basic.api.request.GetOrgReq;
+import cn.com.qmth.examcloud.core.basic.api.request.GetOrgsByIdListReq;
+import cn.com.qmth.examcloud.core.basic.api.request.GetOrgsReq;
+import cn.com.qmth.examcloud.core.basic.api.request.SaveOrgReq;
+import cn.com.qmth.examcloud.core.basic.api.response.GetOrgResp;
+import cn.com.qmth.examcloud.core.basic.api.response.GetOrgsByIdListResp;
+import cn.com.qmth.examcloud.core.basic.api.response.GetOrgsResp;
+import cn.com.qmth.examcloud.core.basic.api.response.SaveOrgResp;
+import cn.com.qmth.examcloud.core.basic.dao.OrgPropertyRepo;
+import cn.com.qmth.examcloud.core.basic.dao.OrgRepo;
+import cn.com.qmth.examcloud.core.basic.dao.entity.OrgEntity;
+import cn.com.qmth.examcloud.core.basic.service.bean.OrgInfo;
+import cn.com.qmth.examcloud.core.basic.service.impl.OrgServiceImpl;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * {@link StatusException} 状态码范围:010XXX<br>
+ *
+ * @author WANGWEI
+ * @date 2018年8月23日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@RestController
+@RequestMapping("${$rmp.cloud.basic}" + "org")
+public class OrgCloudServiceProvider extends ControllerSupport implements OrgCloudService {
+
+	private static final long serialVersionUID = -7858439296389761341L;
+
+	@Autowired
+	private OrgServiceImpl orgService;
+
+	@Autowired
+	private OrgRepo orgRepo;
+
+	@Autowired
+	OrgPropertyRepo orgPropertyRepo;
+
+	@ApiOperation(value = "保存机构")
+	@PostMapping("saveOrg")
+	@Transactional
+	@Override
+	public SaveOrgResp saveOrg(@RequestBody SaveOrgReq req) {
+		OrgInfo info = new OrgInfo();
+		info.setCode(req.getCode());
+		info.setName(req.getName());
+		info.setContacts(req.getContacts());
+		info.setTelephone(req.getTelephone());
+		info.setDomainName(req.getDomainName());
+		info.setRemark(req.getRemark());
+
+		OrgEntity saved = orgService.saveRootOrg(info);
+
+		SaveOrgResp orgResp = new SaveOrgResp();
+		orgResp.setId(saved.getId());
+		orgResp.setName(saved.getName());
+		return orgResp;
+	}
+
+	@ApiOperation(value = "获取机构")
+	@PostMapping("getOrg")
+	@Override
+	public GetOrgResp getOrg(@RequestBody GetOrgReq req) {
+		Long orgId = req.getOrgId();
+		String orgCode = req.getOrgCode();
+		Long rootOrgId = req.getRootOrgId();
+
+		OrgEntity org = null;
+		if (null != orgId) {
+			org = GlobalHelper.getEntity(orgRepo, orgId, OrgEntity.class);
+		} else if (StringUtils.isNotBlank(orgCode)) {
+			if (null == rootOrgId) {
+				throw new StatusException("150001", "rootOrgId is null");
+			}
+			org = orgRepo.findByRootIdAndCode(rootOrgId, orgCode);
+		} else {
+			throw new StatusException("150001", "orgId,orgCode不能都不为空");
+		}
+
+		if (null == org) {
+			throw new StatusException("150001", "机构不存在");
+		}
+
+		OrgBean orgBean = new OrgBean();
+		orgBean.setId(org.getId());
+		orgBean.setName(org.getName());
+		orgBean.setCode(org.getCode());
+		orgBean.setParentId(org.getParentId());
+		orgBean.setRootId(org.getRootId());
+		orgBean.setEnable(org.getEnable());
+
+		GetOrgResp resp = new GetOrgResp();
+		resp.setOrg(orgBean);
+		return resp;
+	}
+
+	@ApiOperation(value = "按ID集合查询机构")
+	@PostMapping("getOrgsByIdList")
+	@Override
+	public GetOrgsByIdListResp getOrgsByIdList(@RequestBody GetOrgsByIdListReq req) {
+
+		List<Long> orgIdList = req.getOrgIdList();
+
+		final List<OrgBean> orgList = Lists.newArrayList();
+		for (Long cur : orgIdList) {
+			OrgEntity org = GlobalHelper.getEntity(orgRepo, cur, OrgEntity.class);
+			if (null == org) {
+				throw new StatusException("010001", "no org. [id=" + cur + "]");
+			}
+			OrgBean orgBean = new OrgBean();
+			orgBean.setId(org.getId());
+			orgBean.setName(org.getName());
+			orgBean.setCode(org.getCode());
+			orgBean.setParentId(org.getParentId());
+			orgBean.setRootId(org.getRootId());
+			orgBean.setEnable(org.getEnable());
+
+			orgList.add(orgBean);
+		}
+		GetOrgsByIdListResp resp = new GetOrgsByIdListResp();
+		resp.setOrgList(orgList);
+		return resp;
+	}
+
+	@ApiOperation(value = "查询所有机构")
+	@PostMapping("getOrgs")
+	@Override
+	public GetOrgsResp getOrgs(@RequestBody GetOrgsReq req) {
+		Boolean withoutParentId = req.getWithoutParentId();
+		Long rootOrgId = req.getRootOrgId();
+		Boolean enable = req.getEnable();
+
+		final long start = null == req.getStart() ? 1 : req.getStart();
+
+		Pageable pageable = PageRequest.of(0, 100, Sort.Direction.ASC, "id");
+
+		Specification<OrgEntity> specification = (root, query, cb) -> {
+			List<Predicate> predicates = new ArrayList<>();
+
+			if (null != rootOrgId) {
+				predicates.add(cb.equal(root.get("rootId"), rootOrgId));
+			}
+
+			predicates.add(cb.greaterThanOrEqualTo(root.get("id"), start));
+
+			if (null != withoutParentId) {
+				if (withoutParentId) {
+					predicates.add(cb.isNull(root.get("parentId")));
+				} else {
+					predicates.add(cb.isNotNull(root.get("parentId")));
+				}
+			}
+
+			if (null != enable) {
+				predicates.add(cb.equal(root.get("enable"), enable));
+			}
+
+			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+		};
+
+		Page<OrgEntity> page = orgRepo.findAll(specification, pageable);
+
+		Iterator<OrgEntity> iterator = page.iterator();
+
+		List<OrgBean> list = Lists.newArrayList();
+		long next = start;
+		boolean has = false;
+		while (iterator.hasNext()) {
+			OrgEntity org = iterator.next();
+			OrgBean orgBean = new OrgBean();
+			orgBean.setId(org.getId());
+			orgBean.setName(org.getName());
+			orgBean.setCode(org.getCode());
+			orgBean.setParentId(org.getParentId());
+			orgBean.setRootId(org.getRootId());
+			orgBean.setEnable(org.getEnable());
+
+			next = org.getId();
+			list.add(orgBean);
+			has = true;
+		}
+
+		GetOrgsResp resp = new GetOrgsResp();
+		if (has) {
+			next++;
+		}
+		resp.setNext(next);
+		resp.setOrgBeanList(list);
+
+		return resp;
+	}
+
+}

+ 135 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/provider/RolePrivilegeCloudServiceProvider.java

@@ -0,0 +1,135 @@
+package cn.com.qmth.examcloud.core.basic.api.provider;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.google.common.collect.Lists;
+
+import cn.com.qmth.examcloud.core.basic.api.RolePrivilegeCloudService;
+import cn.com.qmth.examcloud.core.basic.api.bean.RoleBean;
+import cn.com.qmth.examcloud.core.basic.api.request.DeleteRoleReq;
+import cn.com.qmth.examcloud.core.basic.api.request.GetPrivilegeListReq;
+import cn.com.qmth.examcloud.core.basic.api.request.SaveRoleReq;
+import cn.com.qmth.examcloud.core.basic.api.response.DeleteRoleResp;
+import cn.com.qmth.examcloud.core.basic.api.response.GetPrivilegeListResp;
+import cn.com.qmth.examcloud.core.basic.api.response.SaveRoleResp;
+import cn.com.qmth.examcloud.core.basic.dao.PrivilegeGroupRepo;
+import cn.com.qmth.examcloud.core.basic.dao.PrivilegeRepo;
+import cn.com.qmth.examcloud.core.basic.dao.RolePrivilegeRelationRepo;
+import cn.com.qmth.examcloud.core.basic.dao.RoleRepo;
+import cn.com.qmth.examcloud.core.basic.dao.entity.PrivilegeEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.RoleEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.RolePrivilegeRelationEntity;
+import cn.com.qmth.examcloud.core.basic.service.RolePrivilegeService;
+import cn.com.qmth.examcloud.core.basic.service.bean.RoleInfo;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * 角色权限服务
+ *
+ * @author WANGWEI
+ * @date 2018年6月8日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@Api("角色权限")
+@RestController
+@RequestMapping("${$rmp.cloud.basic}" + "rolePrivilege")
+public class RolePrivilegeCloudServiceProvider extends ControllerSupport
+		implements
+			RolePrivilegeCloudService {
+
+	private static final long serialVersionUID = -4360164791713797878L;
+
+	@Autowired
+	RoleRepo roleRepo;
+
+	@Autowired
+	RolePrivilegeRelationRepo rolePrivilegeRelationRepo;
+
+	@Autowired
+	PrivilegeRepo privilegeRepo;
+
+	@Autowired
+	PrivilegeGroupRepo privilegeGroupRepo;
+
+	@Autowired
+	RolePrivilegeService rolePrivilegeService;
+
+	@ApiOperation(value = "添加角色")
+	@PostMapping("saveRole")
+	@Transactional
+	@Override
+	public SaveRoleResp saveRole(@RequestBody SaveRoleReq req) {
+		RoleInfo info = new RoleInfo();
+		info.setCode(req.getCode());
+		info.setName(req.getName());
+		info.setRootOrgId(req.getRootOrgId());
+
+		RoleEntity saved = rolePrivilegeService.saveRole(info);
+
+		RoleBean roleBean = new RoleBean();
+		roleBean.setRoleCode(saved.getCode());
+		roleBean.setRoleId(saved.getId());
+		roleBean.setRoleName(saved.getName());
+
+		SaveRoleResp resp = new SaveRoleResp();
+		resp.setRoleBean(roleBean);
+		return resp;
+	}
+
+	@ApiOperation(value = "删除角色")
+	@PostMapping("deleteRole")
+	@Transactional
+	@Override
+	public DeleteRoleResp deleteRole(@RequestBody DeleteRoleReq req) {
+		RoleInfo info = new RoleInfo();
+		info.setId(req.getId());
+		info.setCode(req.getCode());
+		info.setName(req.getName());
+		info.setRootOrgId(req.getRootOrgId());
+
+		RoleEntity saved = rolePrivilegeService.deleteRole(info, req.isCascade());
+
+		RoleBean roleBean = new RoleBean();
+		roleBean.setRoleCode(saved.getCode());
+		roleBean.setRoleId(saved.getId());
+		roleBean.setRoleName(saved.getName());
+
+		DeleteRoleResp resp = new DeleteRoleResp();
+		resp.setRoleBean(roleBean);
+		return resp;
+	}
+
+	@ApiOperation(value = "查询权限集合")
+	@PostMapping("getPrivilegeList")
+	@Override
+	public GetPrivilegeListResp getPrivilegeList(@RequestBody GetPrivilegeListReq req) {
+		Long rootOrgId = req.getRootOrgId();
+		Long roleId = req.getRoleId();
+
+		List<RolePrivilegeRelationEntity> relationList = rolePrivilegeRelationRepo
+				.findAllByRoleIdAndRootOrgId(roleId, rootOrgId);
+
+		List<String> privilegeCodeList = Lists.newArrayList();
+		for (RolePrivilegeRelationEntity cur : relationList) {
+
+			PrivilegeEntity privilegeEntity = GlobalHelper.getEntity(privilegeRepo,
+					cur.getPrivilegeId(), PrivilegeEntity.class);
+			privilegeCodeList.add(privilegeEntity.getCode());
+		}
+
+		GetPrivilegeListResp resp = new GetPrivilegeListResp();
+		resp.setPrivilegeCodeList(privilegeCodeList);
+		return resp;
+	}
+
+}

+ 267 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/provider/StudentCloudServiceProvider.java

@@ -0,0 +1,267 @@
+package cn.com.qmth.examcloud.core.basic.api.provider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.persistence.criteria.Predicate;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.basic.api.StudentCloudService;
+import cn.com.qmth.examcloud.core.basic.api.bean.StudentBean;
+import cn.com.qmth.examcloud.core.basic.api.request.CountStudentReq;
+import cn.com.qmth.examcloud.core.basic.api.request.GetStudentReq;
+import cn.com.qmth.examcloud.core.basic.api.request.SaveStudentReq;
+import cn.com.qmth.examcloud.core.basic.api.request.UnbindStudentCodeReq;
+import cn.com.qmth.examcloud.core.basic.api.request.UpdatePasswordReq;
+import cn.com.qmth.examcloud.core.basic.api.request.UpdateStudentStatusReq;
+import cn.com.qmth.examcloud.core.basic.api.response.CountStudentResp;
+import cn.com.qmth.examcloud.core.basic.api.response.GetStudentResp;
+import cn.com.qmth.examcloud.core.basic.api.response.SaveStudentResp;
+import cn.com.qmth.examcloud.core.basic.api.response.UnbindStudentCodeResp;
+import cn.com.qmth.examcloud.core.basic.api.response.UpdatePasswordResp;
+import cn.com.qmth.examcloud.core.basic.api.response.UpdateStudentStatusResp;
+import cn.com.qmth.examcloud.core.basic.dao.OrgRepo;
+import cn.com.qmth.examcloud.core.basic.dao.StudentRepo;
+import cn.com.qmth.examcloud.core.basic.dao.UserRepo;
+import cn.com.qmth.examcloud.core.basic.dao.entity.OrgEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.StudentEntity;
+import cn.com.qmth.examcloud.core.basic.service.StudentService;
+import cn.com.qmth.examcloud.core.basic.service.bean.StudentInfo;
+import cn.com.qmth.examcloud.core.basic.service.cache.StudentCache;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * {@link StatusException} 状态码范围:056XXX<br>
+ *
+ * @author WANGWEI
+ * @date 2018年8月14日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@RestController
+@RequestMapping("${$rmp.cloud.basic}" + "student")
+public class StudentCloudServiceProvider extends ControllerSupport implements StudentCloudService {
+
+	private static final long serialVersionUID = -571041158847398070L;
+
+	@Autowired
+	OrgRepo orgRepo;
+
+	@Autowired
+	UserRepo userRepo;
+
+	@Autowired
+	StudentService studentService;
+
+	@Autowired
+	StudentRepo studentRepo;
+
+	@Autowired
+	StudentCache studentCache;
+
+	@ApiOperation(value = "保存学生")
+	@PostMapping("saveStudent")
+	@Transactional
+	@Override
+	public SaveStudentResp saveStudent(@RequestBody SaveStudentReq req) {
+
+		trim(req);
+
+		StudentInfo studentInfo = new StudentInfo();
+		studentInfo.setEnable(req.getEnable());
+		studentInfo.setName(req.getName());
+		studentInfo.setIdentityNumber(req.getIdentityNumber());
+		studentInfo.setStudentCodeList(req.getStudentCodeList());
+		studentInfo.setRootOrgId(req.getRootOrgId());
+
+		studentInfo.setOrgId(req.getOrgId());
+		studentInfo.setOrgCode(req.getOrgCode());
+		studentInfo.setOrgName(req.getOrgName());
+
+		studentInfo.setPhoneNumber(req.getPhoneNumber());
+		studentInfo.setPhotoPath(req.getPhotoPath());
+		studentInfo.setRemark(req.getRemark());
+
+		StudentEntity student = studentService.saveStudent(studentInfo);
+
+		SaveStudentResp resp = new SaveStudentResp();
+		resp.setStudentId(student.getId());
+		resp.setRootOrgId(student.getRootOrgId());
+
+		if (null != student.getOrgId()) {
+			OrgEntity org = GlobalHelper.getEntity(orgRepo, student.getOrgId(), OrgEntity.class);
+			resp.setOrgId(org.getId());
+			resp.setOrgName(org.getName());
+		}
+
+		studentCache.remove(student.getId());
+
+		return resp;
+	}
+
+	@ApiOperation(value = "查询学生")
+	@PostMapping("getStudent")
+	@Override
+	public GetStudentResp getStudent(@RequestBody GetStudentReq req) {
+		Long studentId = req.getStudentId();
+		Long rootOrgId = req.getRootOrgId();
+		String identityNumber = req.getIdentityNumber();
+		String studentCode = req.getStudentCode();
+		String securityPhone = req.getSecurityPhone();
+
+		StudentInfo student = studentService.getStudentInfo(rootOrgId, studentId, identityNumber,
+				studentCode, securityPhone);
+
+		StudentBean studentBean = new StudentBean();
+		studentBean.setId(student.getId());
+		studentBean.setName(student.getName());
+		studentBean.setIdentityNumber(student.getIdentityNumber());
+		studentBean.setStudentCodeList(student.getStudentCodeList());
+		studentBean.setRootOrgId(student.getRootOrgId());
+		if (null != student.getOrgId()) {
+			OrgEntity org = GlobalHelper.getEntity(orgRepo, student.getOrgId(), OrgEntity.class);
+			studentBean.setOrgId(org.getId());
+			studentBean.setOrgCode(org.getCode());
+			studentBean.setOrgName(org.getName());
+		}
+		studentBean.setOrgId(student.getOrgId());
+		studentBean.setOrgCode(student.getOrgCode());
+		studentBean.setOrgName(student.getOrgName());
+		studentBean.setPhoneNumber(student.getPhoneNumber());
+		studentBean.setPhotoPath(student.getPhotoPath());
+		studentBean.setRemark(student.getRemark());
+		studentBean.setSecurityPhone(student.getSecurityPhone());
+
+		GetStudentResp resp = new GetStudentResp();
+		resp.setStudentInfo(studentBean);
+		return resp;
+	}
+
+	@ApiOperation(value = "解绑学号")
+	@PostMapping("unbindStudentCode")
+	@Transactional
+	@Override
+	public UnbindStudentCodeResp unbindStudentCode(@RequestBody UnbindStudentCodeReq req) {
+		Long rootOrgId = req.getRootOrgId();
+		String studentCode = req.getStudentCode();
+		String identityNumber = req.getIdentityNumber();
+
+		if (null == rootOrgId) {
+			throw new StatusException("160001", "rootOrgId is null");
+		}
+		if (StringUtils.isBlank(studentCode)) {
+			throw new StatusException("160002", "studentCode is blank");
+		}
+
+		List<Long> studentIdList = studentService.unbindStudentCode(rootOrgId, studentCode,
+				identityNumber);
+
+		UnbindStudentCodeResp resp = new UnbindStudentCodeResp();
+		resp.setStudentIdList(studentIdList);
+
+		for (Long cur : studentIdList) {
+			studentCache.remove(cur);
+		}
+
+		return resp;
+	}
+
+	@ApiOperation(value = "修改密码")
+	@PostMapping("updatePassword")
+	@Transactional
+	@Override
+	public UpdatePasswordResp updatePassword(@RequestBody UpdatePasswordReq req) {
+		Long rootOrgId = req.getRootOrgId();
+		String identityNumber = req.getIdentityNumber();
+		Long studentId = req.getStudentId();
+		String password = req.getPassword();
+
+		StudentEntity entity = null;
+		if (null != studentId) {
+			entity = GlobalHelper.getEntity(studentRepo, studentId, StudentEntity.class);
+		} else if (StringUtils.isNotBlank(identityNumber)) {
+			if (null == rootOrgId) {
+				throw new StatusException("056001", "rootOrgId is needed");
+			}
+			entity = studentRepo.findByIdentityNumberAndRootOrgId(identityNumber, rootOrgId);
+		} else {
+			throw new StatusException("056002", "params is missing");
+		}
+
+		if (null == entity) {
+			throw new StatusException("056003", "no student");
+		}
+
+		entity.setPassword(password);
+		studentRepo.save(entity);
+
+		UpdatePasswordResp resp = new UpdatePasswordResp();
+		return resp;
+	}
+
+	@ApiOperation(value = "更新学生状态")
+	@PostMapping("updateStudentStatus")
+	@Transactional
+	@Override
+	public UpdateStudentStatusResp updateStudentStatus(@RequestBody UpdateStudentStatusReq req) {
+		Long rootOrgId = req.getRootOrgId();
+		String identityNumber = req.getIdentityNumber();
+		Long studentId = req.getStudentId();
+		Boolean enable = req.getEnable();
+
+		StudentEntity entity = null;
+		if (null != studentId) {
+			entity = GlobalHelper.getEntity(studentRepo, studentId, StudentEntity.class);
+		} else if (StringUtils.isNotBlank(identityNumber)) {
+			if (null == rootOrgId) {
+				throw new StatusException("056004", "rootOrgId is needed");
+			}
+			entity = studentRepo.findByIdentityNumberAndRootOrgId(identityNumber, rootOrgId);
+		} else {
+			throw new StatusException("056005", "params is missing");
+		}
+
+		if (null == entity) {
+			throw new StatusException("056006", "no student");
+		}
+
+		entity.setEnable(enable);
+		StudentEntity saved = studentRepo.save(entity);
+
+		studentCache.remove(saved.getId());
+
+		UpdateStudentStatusResp resp = new UpdateStudentStatusResp();
+		return resp;
+	}
+
+	@ApiOperation(value = "统计学生")
+	@PostMapping("countStudent")
+	@Override
+	public CountStudentResp countStudent(@RequestBody CountStudentReq req) {
+		Long rootOrgId = req.getRootOrgId();
+
+		Specification<StudentEntity> specification = (root, query, cb) -> {
+			List<Predicate> predicates = new ArrayList<>();
+			predicates.add(cb.equal(root.get("rootOrgId"), rootOrgId));
+
+			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+		};
+
+		long count = studentRepo.count(specification);
+
+		CountStudentResp resp = new CountStudentResp();
+		resp.setCount(count);
+		return resp;
+	}
+
+}

+ 78 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/provider/SystemPropertyCloudServiceProvider.java

@@ -0,0 +1,78 @@
+package cn.com.qmth.examcloud.core.basic.api.provider;
+
+import java.util.Date;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.commons.util.DateUtil;
+import cn.com.qmth.examcloud.commons.util.DateUtil.DatePatterns;
+import cn.com.qmth.examcloud.core.basic.api.SystemPropertyCloudService;
+import cn.com.qmth.examcloud.core.basic.api.request.GetSystemPropertyReq;
+import cn.com.qmth.examcloud.core.basic.api.request.SetSystemPropertyReq;
+import cn.com.qmth.examcloud.core.basic.api.response.GetSystemPropertyResp;
+import cn.com.qmth.examcloud.core.basic.api.response.SetSystemPropertyResp;
+import cn.com.qmth.examcloud.core.basic.service.SystemPropertyService;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * 系统配置
+ *
+ * @author WANGWEI
+ * @date 2018年12月3日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@RestController
+@RequestMapping("${$rmp.cloud.basic}" + "systemProperty")
+public class SystemPropertyCloudServiceProvider extends ControllerSupport
+		implements
+			SystemPropertyCloudService {
+
+	private static final long serialVersionUID = 2299432976949405946L;
+
+	@Autowired
+	SystemPropertyService systemPropertyService;
+
+	@ApiOperation(value = "设置配置")
+	@PostMapping("setSystemProperty")
+	@Transactional
+	@Override
+	public SetSystemPropertyResp setSystemProperty(@RequestBody SetSystemPropertyReq req) {
+		String key = req.getKey();
+		String value = req.getValue();
+		systemPropertyService.set(key, value);
+
+		SetSystemPropertyResp resp = new SetSystemPropertyResp();
+		resp.setKey(key);
+		resp.setValue(value);
+		return resp;
+	}
+
+	@ApiOperation(value = "获取配置")
+	@PostMapping("getSystemProperty")
+	@Override
+	public GetSystemPropertyResp getSystemProperty(@RequestBody GetSystemPropertyReq req) {
+		String key = req.getKey();
+		Object value = systemPropertyService.get(key);
+
+		String strValue = null;
+		if (null == value) {
+			strValue = null;
+		} else if (value instanceof Date) {
+			strValue = DateUtil.format((Date) value, DatePatterns.CHINA_DEFAULT);
+		} else {
+			strValue = String.valueOf(value);
+		}
+
+		GetSystemPropertyResp resp = new GetSystemPropertyResp();
+		resp.setKey(key);
+		resp.setValue(strValue);
+		return resp;
+	}
+
+}

+ 333 - 0
examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/provider/UserCloudServiceProvider.java

@@ -0,0 +1,333 @@
+package cn.com.qmth.examcloud.core.basic.api.provider;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+import javax.persistence.criteria.Subquery;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.google.common.collect.Lists;
+
+import cn.com.qmth.examcloud.api.commons.security.enums.RoleMeta;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.basic.api.UserCloudService;
+import cn.com.qmth.examcloud.core.basic.api.bean.RoleBean;
+import cn.com.qmth.examcloud.core.basic.api.bean.UserBean;
+import cn.com.qmth.examcloud.core.basic.api.request.AddUserReq;
+import cn.com.qmth.examcloud.core.basic.api.request.GetAllUsersByRoleReq;
+import cn.com.qmth.examcloud.core.basic.api.request.GetUserListByIdsReq;
+import cn.com.qmth.examcloud.core.basic.api.request.GetUserReq;
+import cn.com.qmth.examcloud.core.basic.api.response.AddUserResp;
+import cn.com.qmth.examcloud.core.basic.api.response.GetAllUsersByRoleResp;
+import cn.com.qmth.examcloud.core.basic.api.response.GetUserListByIdsResp;
+import cn.com.qmth.examcloud.core.basic.api.response.GetUserResp;
+import cn.com.qmth.examcloud.core.basic.base.constants.BasicConsts;
+import cn.com.qmth.examcloud.core.basic.dao.OrgRepo;
+import cn.com.qmth.examcloud.core.basic.dao.RoleRepo;
+import cn.com.qmth.examcloud.core.basic.dao.UserRepo;
+import cn.com.qmth.examcloud.core.basic.dao.UserRoleRelationRepo;
+import cn.com.qmth.examcloud.core.basic.dao.entity.OrgEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.RoleEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.UserEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.UserRoleRelationEntity;
+import cn.com.qmth.examcloud.core.basic.service.UserService;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * {@link StatusException} 状态码范围:008XXX<br>
+ *
+ * @author WANGWEI
+ * @date 2018年7月23日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@RestController
+@RequestMapping("${$rmp.cloud.basic}" + "user")
+public class UserCloudServiceProvider extends ControllerSupport implements UserCloudService {
+
+	private static final long serialVersionUID = -7881522922704971610L;
+
+	@Autowired
+	UserRepo userRepo;
+
+	@Autowired
+	OrgRepo orgRepo;
+
+	@Autowired
+	RoleRepo roleRepo;
+
+	@Autowired
+	UserService userService;
+
+	@Autowired
+	UserRoleRelationRepo userRoleRelationRepo;
+
+	@ApiOperation(value = "保存用户")
+	@PostMapping("addUser")
+	@Transactional
+	@Override
+	public AddUserResp addUser(@RequestBody AddUserReq req) {
+		trim(req);
+		Long rootOrgId = req.getRootOrgId();
+		String loginName = req.getLoginName();
+		String name = req.getName();
+
+		UserEntity userEntity = userRepo.findByRootOrgIdAndLoginName(rootOrgId, loginName);
+
+		if (null != userEntity) {
+			throw new StatusException("650001", "用户名已经存在");
+		}
+
+		userEntity = new UserEntity();
+		userEntity.setEnable(true);
+		userEntity.setLoginName(loginName);
+		userEntity.setRootOrgId(rootOrgId);
+		userEntity.setName(name);
+		if (StringUtils.isBlank(req.getPassword())) {
+			userEntity.setPassword(BasicConsts.DEFAULT_PASSWORD);
+		} else {
+			userEntity.setPassword(req.getPassword());
+		}
+
+		UserEntity saved = userService.save(userEntity);
+
+		List<String> roleCodeList = req.getRoleCodeList();
+
+		if (CollectionUtils.isEmpty(roleCodeList)) {
+			throw new StatusException("650002", "角色不能为空");
+		}
+
+		List<UserRoleRelationEntity> userRoles = Lists.newArrayList();
+		for (String roleCode : roleCodeList) {
+			RoleEntity roleEntity = roleRepo.findByCodeAndRootOrgIdIsNull(roleCode);
+			if (null == roleEntity) {
+				roleEntity = roleRepo.findByCodeAndRootOrgId(roleCode, rootOrgId);
+			}
+			if (null == roleEntity) {
+				throw new StatusException("002002", "role code is wrong. roleCode=" + roleCode);
+			}
+			if (roleEntity.getCode().equals(RoleMeta.SUPER_ADMIN.name())) {
+				throw new StatusException("150007", "不允许新增或修改超级管理员");
+			}
+			UserRoleRelationEntity relation = new UserRoleRelationEntity(saved.getId(),
+					roleEntity.getId());
+			userRoles.add(relation);
+		}
+
+		userRoleRelationRepo.deleteByUserId(saved.getId());
+		userRoleRelationRepo.saveAll(userRoles);
+		AddUserResp resp = new AddUserResp();
+		resp.setUserId(saved.getId());
+
+		return resp;
+	}
+
+	@ApiOperation(value = "获取用户")
+	@PostMapping("getUser")
+	@Override
+	public GetUserResp getUser(@RequestBody GetUserReq req) {
+		Long rootOrgId = req.getRootOrgId();
+		String loginName = req.getLoginName();
+		Long userId = req.getUserId();
+
+		UserEntity userEntity = null;
+		if (null != userId) {
+			userEntity = GlobalHelper.getEntity(userRepo, userId, UserEntity.class);
+		} else if (StringUtils.isNotBlank(loginName)) {
+			userEntity = userRepo.findByRootOrgIdAndLoginName(rootOrgId, loginName);
+		} else {
+			throw new StatusException("230001", "userId and  loginName can not be all null");
+		}
+
+		GetUserResp resp = new GetUserResp();
+		if (null == userEntity) {
+			resp.setExisting(false);
+			return resp;
+		}
+
+		resp.setExisting(true);
+
+		UserBean userBean = new UserBean();
+		userBean.setUserId(userEntity.getId());
+		userBean.setName(userEntity.getName());
+		userBean.setLoginName(userEntity.getLoginName());
+		userBean.setDisplayName(userEntity.getLoginName() + " (" + userEntity.getName() + ")");
+		userBean.setOrgId(userEntity.getOrgId());
+		userBean.setRootOrgId(userEntity.getRootOrgId());
+		OrgEntity rootOrg = GlobalHelper.getEntity(orgRepo, userBean.getRootOrgId(),
+				OrgEntity.class);
+		userBean.setRootOrgName(rootOrg.getName());
+
+		if (null != userBean.getOrgId()) {
+			OrgEntity org = GlobalHelper.getEntity(orgRepo, userBean.getOrgId(), OrgEntity.class);
+			userBean.setOrgName(org.getName());
+		}
+
+		List<RoleBean> roleList = getUserRoles(userEntity.getId());
+		userBean.setRoleList(roleList);
+
+		resp.setUserBean(userBean);
+		return resp;
+	}
+
+	/**
+	 * 获取角色集合
+	 *
+	 * @author WANGWEI
+	 * @param userId
+	 * @return
+	 */
+	private List<RoleBean> getUserRoles(Long userId) {
+		List<UserRoleRelationEntity> relationList = userRoleRelationRepo.findAllByUserId(userId);
+		List<RoleBean> roleList = Lists.newArrayList();
+		if (CollectionUtils.isNotEmpty(relationList)) {
+			for (UserRoleRelationEntity cur : relationList) {
+				Long roleId = cur.getRoleId();
+				RoleEntity roleEntity = GlobalHelper.getEntity(roleRepo, roleId, RoleEntity.class);
+				if (null == roleEntity) {
+					throw new StatusException("002002", "roleId wrong. roleId=" + roleId);
+				}
+				RoleBean role = new RoleBean(roleEntity.getRootOrgId(), roleEntity.getId(),
+						roleEntity.getCode(), roleEntity.getName());
+				roleList.add(role);
+			}
+		}
+		return roleList;
+	}
+
+	@ApiOperation(value = "根据角色获取用户集合")
+	@PostMapping("getAllUsersByRole")
+	@Override
+	public GetAllUsersByRoleResp getAllUsersByRole(@RequestBody GetAllUsersByRoleReq req) {
+		Long rootOrgId = req.getRootOrgId();
+		Long roleId = req.getRoleId();
+		String roleCode = req.getRoleCode();
+
+		if (null != roleId) {
+			RoleEntity roleEntity = GlobalHelper.getEntity(roleRepo, roleId, RoleEntity.class);
+			if (null == roleEntity) {
+				throw new StatusException("150001", "角色不存在");
+			}
+		} else if (StringUtils.isNotBlank(roleCode)) {
+			RoleEntity roleEntity = roleRepo.findByCodeAndRootOrgIdIsNull(roleCode.trim());
+			if (null == roleEntity) {
+				roleEntity = roleRepo.findByCodeAndRootOrgId(roleCode.trim(), rootOrgId);
+			}
+			if (null == roleEntity) {
+				throw new StatusException("150002", "角色不存在");
+			}
+			roleId = roleEntity.getId();
+		} else {
+			throw new StatusException("150003", "roleId,roleCode不能都为空");
+		}
+		final Long finalRoleId = roleId;
+
+		final long start = null == req.getStart() ? 1 : req.getStart();
+
+		Pageable pageable = PageRequest.of(0, 100, Sort.Direction.ASC, "id");
+
+		Specification<UserEntity> specification = (root, query, cb) -> {
+			List<Predicate> predicates = new ArrayList<>();
+			predicates.add(cb.equal(root.get("rootOrgId"), rootOrgId));
+
+			predicates.add(cb.greaterThanOrEqualTo(root.get("id"), start));
+
+			Subquery<UserRoleRelationEntity> subquery = query
+					.subquery(UserRoleRelationEntity.class);
+			Root<UserRoleRelationEntity> subRoot = subquery.from(UserRoleRelationEntity.class);
+			subquery.select(subRoot.get("userId"));
+			Predicate p1 = cb.equal(subRoot.get("roleId"), finalRoleId);
+			Predicate p2 = cb.equal(subRoot.get("userId"), root.get("id"));
+			subquery.where(cb.and(p1, p2));
+			predicates.add(cb.exists(subquery));
+
+			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+		};
+
+		Page<UserEntity> page = userRepo.findAll(specification, pageable);
+
+		Iterator<UserEntity> iterator = page.iterator();
+
+		List<UserBean> list = Lists.newArrayList();
+		long next = start;
+		boolean has = false;
+		while (iterator.hasNext()) {
+			UserEntity userEntity = iterator.next();
+			UserBean userBean = new UserBean();
+			userBean.setUserId(userEntity.getId());
+			userBean.setName(userEntity.getName());
+			userBean.setLoginName(userEntity.getLoginName());
+			userBean.setDisplayName(userEntity.getLoginName() + " (" + userEntity.getName() + ")");
+			userBean.setOrgId(userEntity.getOrgId());
+			userBean.setRootOrgId(userEntity.getRootOrgId());
+
+			next = userEntity.getId();
+			list.add(userBean);
+			has = true;
+		}
+
+		GetAllUsersByRoleResp resp = new GetAllUsersByRoleResp();
+		if (has) {
+			next++;
+		}
+		resp.setNext(next);
+		resp.setUserBeanList(list);
+
+		return resp;
+	}
+
+	@ApiOperation(value = "根据ID获取用户集合")
+	@PostMapping("getUserListByIds")
+	@Override
+	public GetUserListByIdsResp getUserListByIds(@RequestBody GetUserListByIdsReq req) {
+		Long rootOrgId = req.getRootOrgId();
+		List<Long> userIdList = req.getUserIdList();
+		if (CollectionUtils.isEmpty(userIdList)) {
+			throw new StatusException("008001", "userIdList is empty");
+		}
+		if (100 < userIdList.size()) {
+			throw new StatusException("008002", "size of userIdList is too large");
+		}
+
+		List<UserEntity> userEntityList = userRepo.findByIdIn(userIdList);
+
+		List<UserBean> list = Lists.newArrayList();
+		for (UserEntity userEntity : userEntityList) {
+			UserBean userBean = new UserBean();
+			userBean.setUserId(userEntity.getId());
+			userBean.setName(userEntity.getName());
+			userBean.setLoginName(userEntity.getLoginName());
+			userBean.setDisplayName(userEntity.getLoginName() + " (" + userEntity.getName() + ")");
+			userBean.setOrgId(userEntity.getOrgId());
+			userBean.setRootOrgId(userEntity.getRootOrgId());
+
+			if (!userBean.getRootOrgId().equals(rootOrgId)) {
+				throw new StatusException("008003", "params error");
+			}
+			list.add(userBean);
+		}
+
+		GetUserListByIdsResp resp = new GetUserListByIdsResp();
+		resp.setUserBeanList(list);
+
+		return resp;
+	}
+
+}

+ 34 - 0
examcloud-core-basic-base/pom.xml

@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>cn.com.qmth.examcloud.core.basic</groupId>
+		<artifactId>examcloud-core-basic</artifactId>
+		<version>2019-SNAPSHOT</version>
+	</parent>
+	<artifactId>examcloud-core-basic-base</artifactId>
+
+	<dependencies>
+		<dependency>
+			<groupId>cn.com.qmth.examcloud</groupId>
+			<artifactId>examcloud-web</artifactId>
+			<version>${examcloud.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>cn.com.qmth.examcloud</groupId>
+			<artifactId>examcloud-support</artifactId>
+			<version>${examcloud.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>cn.com.qmth.examcloud.reports</groupId>
+			<artifactId>examcloud-reports-commons</artifactId>
+			<version>${examcloud.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>com.github.penggle</groupId>
+			<artifactId>kaptcha</artifactId>
+			<version>2.3.2</version>
+		</dependency>
+	</dependencies>
+
+</project>

+ 32 - 0
examcloud-core-basic-base/src/main/java/cn/com/qmth/examcloud/core/basic/base/constants/BasicConsts.java

@@ -0,0 +1,32 @@
+package cn.com.qmth.examcloud.core.basic.base.constants;
+
+/**
+ * 常量
+ *
+ * @author WANGWEI
+ * @date 2018年6月20日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public interface BasicConsts {
+
+	/**
+	 * 根虚拟权限ID
+	 */
+	Long ROOT_PRIVILEGE_ID = -1L;
+
+	/**
+	 * 默认密码
+	 */
+	String DEFAULT_PASSWORD = "123456";
+
+	/**
+	 * 默认机构代码
+	 */
+	String DEFAULT_ORG_COEE = "QMTH-DEFAULT";
+
+	/**
+	 * 默认机构名称
+	 */
+	String DEFAULT_ORG_NAME = "默认学习中心";
+
+}

+ 17 - 0
examcloud-core-basic-base/src/main/java/cn/com/qmth/examcloud/core/basic/base/constants/PropKeys.java

@@ -0,0 +1,17 @@
+package cn.com.qmth.examcloud.core.basic.base.constants;
+
+/**
+ * 自定义属性
+ *
+ * @author WANGWEI
+ * @date 2018年6月21日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public interface PropKeys {
+
+	/**
+	 * session过期时长
+	 */
+	String SESSION_TIMEOUT = "$core.basic.sessionTimeout";
+
+}

+ 13 - 0
examcloud-core-basic-base/src/main/java/cn/com/qmth/examcloud/core/basic/base/constants/SystemProps.java

@@ -0,0 +1,13 @@
+package cn.com.qmth.examcloud.core.basic.base.constants;
+
+/**
+ * 系统属性
+ *
+ * @author WANGWEI
+ * @date 2019年7月29日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public class SystemProps {
+
+	public static final String ORG_PROPERTY_GROUP_PREFIX = "orgPropertyGroup.";
+}

+ 62 - 0
examcloud-core-basic-base/src/main/java/cn/com/qmth/examcloud/core/basic/base/enums/AccountType.java

@@ -0,0 +1,62 @@
+package cn.com.qmth.examcloud.core.basic.base.enums;
+
+/**
+ * 账户类型
+ *
+ * @author WANGWEI
+ * @date 2018年5月25日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public enum AccountType {
+
+	/**
+	 * 学生学号
+	 */
+	STUDENT_CODE("SC", "学生学号"),
+
+	/**
+	 * 学生身份证号
+	 */
+	STUDENT_IDENTITY_NUMBER("SIN", "学生身份证号"),
+
+	/**
+	 * 学生手机号登录
+	 */
+	STUDENT_PHONE("SP", "学生手机号"),
+
+	/**
+	 * 常规登录名(非学生登录)
+	 */
+	COMMON_LOGIN_NAME("C", "常规登录名");
+
+	// ===========================================================================
+
+	/**
+	 * 码
+	 */
+	private String code;
+
+	/**
+	 * 描述
+	 */
+	private String desc;
+
+	/**
+	 * 构造函数
+	 *
+	 * @param desc
+	 */
+	private AccountType(String code, String desc) {
+		this.code = code;
+		this.desc = desc;
+	}
+
+	public String getDesc() {
+		return desc;
+	}
+
+	public String getCode() {
+		return code;
+	}
+
+}

+ 28 - 0
examcloud-core-basic-base/src/main/java/cn/com/qmth/examcloud/core/basic/base/processor/HttpMethodProcessorImpl.java

@@ -0,0 +1,28 @@
+package cn.com.qmth.examcloud.core.basic.base.processor;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.stereotype.Component;
+
+import cn.com.qmth.examcloud.reports.commons.util.ReportsUtil;
+import cn.com.qmth.examcloud.web.support.HttpMethodProcessor;
+
+@Component
+public class HttpMethodProcessorImpl implements HttpMethodProcessor {
+
+	@Override
+	public void beforeMethod(HttpServletRequest request, Object[] args) {
+
+	}
+
+	@Override
+	public void onSuccess(HttpServletRequest request, Object[] args, Object ret) {
+		ReportsUtil.sendReport(false);
+	}
+
+	@Override
+	public void onException(HttpServletRequest request, Object[] args, Throwable e) {
+		ReportsUtil.sendReport(true);
+	}
+
+}

+ 5 - 0
examcloud-core-basic-base/src/main/java/cn/com/qmth/examcloud/core/basic/base/util/BaseUtil.java

@@ -0,0 +1,5 @@
+package cn.com.qmth.examcloud.core.basic.base.util;
+
+public class BaseUtil {
+
+}

+ 19 - 0
examcloud-core-basic-dao/pom.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>cn.com.qmth.examcloud.core.basic</groupId>
+		<artifactId>examcloud-core-basic</artifactId>
+		<version>2019-SNAPSHOT</version>
+	</parent>
+	<artifactId>examcloud-core-basic-dao</artifactId>
+
+	<dependencies>
+		<dependency>
+			<groupId>cn.com.qmth.examcloud.core.basic</groupId>
+			<artifactId>examcloud-core-basic-base</artifactId>
+			<version>${examcloud.version}</version>
+		</dependency>
+	</dependencies>
+
+</project>

+ 23 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/AppRepo.java

@@ -0,0 +1,23 @@
+package cn.com.qmth.examcloud.core.basic.dao;
+
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.data.repository.query.QueryByExampleExecutor;
+
+import cn.com.qmth.examcloud.core.basic.dao.entity.AppEntity;
+
+/**
+ * 类注释
+ *
+ * @author WANGWEI
+ * @date 2018年6月11日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public interface AppRepo extends JpaRepository<AppEntity, Long>, QueryByExampleExecutor<AppEntity> {
+
+	@Query(value = "select id,code from AppEntity a where id >= :beginId and id < :endId order by id ASC")
+	List<Object[]> findIdList(@Param("beginId") Long beginId, @Param("endId") Long endId);
+}

+ 25 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/CourseRepo.java

@@ -0,0 +1,25 @@
+package cn.com.qmth.examcloud.core.basic.dao;
+
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.repository.query.QueryByExampleExecutor;
+
+import cn.com.qmth.examcloud.core.basic.dao.entity.CourseEntity;
+
+public interface CourseRepo
+		extends
+			JpaRepository<CourseEntity, Long>,
+			QueryByExampleExecutor<CourseEntity>,
+			JpaSpecificationExecutor<CourseEntity> {
+
+	List<CourseEntity> findByRootOrgId(Long rootOrgId);
+
+	CourseEntity findByRootOrgIdAndCode(Long rootOrgId, String code);
+
+	List<CourseEntity> findByIdNotIn(List<Long> ids);
+
+	int countByRootOrgIdAndNameLike(Long rootOrgId, String name);
+
+}

+ 23 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/CourseSpeciatlyRelationRepo.java

@@ -0,0 +1,23 @@
+package cn.com.qmth.examcloud.core.basic.dao;
+
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.repository.query.QueryByExampleExecutor;
+
+import cn.com.qmth.examcloud.core.basic.dao.entity.CourseSpeciatlyRelationEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.CourseSpeciatlyRelationPK;
+
+public interface CourseSpeciatlyRelationRepo
+		extends
+			JpaRepository<CourseSpeciatlyRelationEntity, CourseSpeciatlyRelationPK>,
+			QueryByExampleExecutor<CourseSpeciatlyRelationEntity>,
+			JpaSpecificationExecutor<CourseSpeciatlyRelationEntity> {
+
+	List<CourseSpeciatlyRelationEntity> findBySpecialtyId(Long specialtyId);
+
+	List<CourseSpeciatlyRelationEntity> findByCourseId(Long courseId);
+
+	void deleteBySpecialtyId(Long specialtyId);
+}

+ 17 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/FacesetRepo.java

@@ -0,0 +1,17 @@
+package cn.com.qmth.examcloud.core.basic.dao;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.repository.query.QueryByExampleExecutor;
+
+import cn.com.qmth.examcloud.core.basic.dao.entity.FacesetEntity;
+
+public interface FacesetRepo
+		extends
+			JpaRepository<FacesetEntity, Long>,
+			QueryByExampleExecutor<FacesetEntity>,
+			JpaSpecificationExecutor<FacesetEntity> {
+
+	FacesetEntity findByFacesetToken(String facesetToken);
+
+}

+ 28 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/OrgPropertyRepo.java

@@ -0,0 +1,28 @@
+package cn.com.qmth.examcloud.core.basic.dao;
+
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.repository.query.QueryByExampleExecutor;
+
+import cn.com.qmth.examcloud.core.basic.dao.entity.OrgPropertyEntity;
+
+/**
+ * 类注释
+ *
+ * @author WANGWEI
+ * @date 2018年8月23日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public interface OrgPropertyRepo
+		extends
+			JpaRepository<OrgPropertyEntity, Long>,
+			QueryByExampleExecutor<OrgPropertyEntity>,
+			JpaSpecificationExecutor<OrgPropertyEntity> {
+
+	OrgPropertyEntity findByOrgIdAndKeyId(Long orgId, Long keyId);
+
+	List<OrgPropertyEntity> findByOrgId(Long orgId);
+
+}

+ 36 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/OrgRepo.java

@@ -0,0 +1,36 @@
+package cn.com.qmth.examcloud.core.basic.dao;
+
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.repository.query.QueryByExampleExecutor;
+
+import cn.com.qmth.examcloud.core.basic.dao.entity.OrgEntity;
+
+/**
+ * Created by songyue on 17/1/13.
+ */
+public interface OrgRepo
+		extends
+			JpaRepository<OrgEntity, Long>,
+			QueryByExampleExecutor<OrgEntity>,
+			JpaSpecificationExecutor<OrgEntity> {
+
+	List<OrgEntity> findByRootId(Long rootId);
+
+	OrgEntity findByParentIdIsNullAndDomainName(String domainName);
+
+	OrgEntity findByRootIdAndCode(Long rootId, String code);
+
+	List<OrgEntity> findByRootIdAndNameLike(Long rootId, String name);
+
+	OrgEntity findByParentIdIsNullAndCode(String code);
+
+	OrgEntity findByParentIdIsNullAndId(Long id);
+
+	int countByRootIdAndNameLike(Long rootId, String name);
+
+	int countByRootIdAndParentId(Long rootId, long parentId);
+
+}

+ 26 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/PrivilegeGroupRepo.java

@@ -0,0 +1,26 @@
+package cn.com.qmth.examcloud.core.basic.dao;
+
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.repository.query.QueryByExampleExecutor;
+
+import cn.com.qmth.examcloud.api.commons.enums.PrivilegeGroupType;
+import cn.com.qmth.examcloud.core.basic.dao.entity.PrivilegeGroupEntity;
+
+public interface PrivilegeGroupRepo
+		extends
+			JpaRepository<PrivilegeGroupEntity, Long>,
+			QueryByExampleExecutor<PrivilegeGroupEntity>,
+			JpaSpecificationExecutor<PrivilegeGroupEntity> {
+
+	PrivilegeGroupEntity findByCode(String code);
+
+	List<PrivilegeGroupEntity> findAllByRootOrgIdIsNullOrderById();
+
+	PrivilegeGroupEntity findByRootOrgIdAndType(Long rootOrgId, PrivilegeGroupType type);
+
+	List<PrivilegeGroupEntity> findAllByType(PrivilegeGroupType type);
+
+}

+ 36 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/PrivilegeRepo.java

@@ -0,0 +1,36 @@
+package cn.com.qmth.examcloud.core.basic.dao;
+
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.repository.query.QueryByExampleExecutor;
+
+import cn.com.qmth.examcloud.core.basic.dao.entity.PrivilegeEntity;
+
+/**
+ * 类注释
+ *
+ * @author WANGWEI
+ * @date 2018年6月13日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public interface PrivilegeRepo
+		extends
+			JpaRepository<PrivilegeEntity, Long>,
+			QueryByExampleExecutor<PrivilegeEntity>,
+			JpaSpecificationExecutor<PrivilegeEntity> {
+
+	List<PrivilegeEntity> findAllByGroupIdOrderByWeightDesc(Long groupId);
+
+	List<PrivilegeEntity> findAllByGroupId(Long groupId);
+
+	List<PrivilegeEntity> findAllByParentId(Long parentId);
+
+	List<PrivilegeEntity> findAllByIdIn(List<Long> idList);
+
+	List<PrivilegeEntity> findAllByCodeIn(List<String> codeList);
+
+	PrivilegeEntity findByCode(String code);
+
+}

+ 19 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/ResourceRepo.java

@@ -0,0 +1,19 @@
+package cn.com.qmth.examcloud.core.basic.dao;
+
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.repository.query.QueryByExampleExecutor;
+
+import cn.com.qmth.examcloud.core.basic.dao.entity.ResourceEntity;
+
+public interface ResourceRepo
+		extends
+			JpaRepository<ResourceEntity, Long>,
+			QueryByExampleExecutor<ResourceEntity>,
+			JpaSpecificationExecutor<ResourceEntity> {
+	ResourceEntity findByRootOrgIdAndFilePath(Long rootOrgId, String filePath);
+	List<ResourceEntity> findByRootOrgIdAndParentId(Long rootOrgId, Long parentId);
+	int countByRootOrgIdAndParentId(Long rootOrgId, Long parentId);
+}

+ 35 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/RolePrivilegeRelationRepo.java

@@ -0,0 +1,35 @@
+package cn.com.qmth.examcloud.core.basic.dao;
+
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.repository.query.QueryByExampleExecutor;
+
+import cn.com.qmth.examcloud.core.basic.dao.entity.RolePrivilegeRelationEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.RolePrivilegeRelationPK;
+
+public interface RolePrivilegeRelationRepo
+		extends
+			JpaRepository<RolePrivilegeRelationEntity, RolePrivilegeRelationPK>,
+			QueryByExampleExecutor<RolePrivilegeRelationEntity>,
+			JpaSpecificationExecutor<RolePrivilegeRelationEntity> {
+
+	List<RolePrivilegeRelationEntity> findAllByRoleIdAndRootOrgId(Long roleId, Long rootOrgId);
+
+	List<RolePrivilegeRelationEntity> findAllByRoleIdInAndRootOrgId(List<Long> roleIdList,
+			Long rootOrgId);
+
+	List<RolePrivilegeRelationEntity> findAllByRoleIdInAndRootOrgIdAndPrivilegeId(
+			List<Long> roleIdList, Long rootOrgId, Long privilegeId);
+
+	int countByRoleIdAndRootOrgId(Long roleId, Long rootOrgId);
+
+	void deleteByRoleIdAndRootOrgId(Long roleId, Long rootOrgId);
+
+	void deleteByPrivilegeId(Long privilegeId);
+
+	List<RolePrivilegeRelationEntity> findAllByPrivilegeIdAndRootOrgId(Long privilegeId,
+			Long rootOrgId);
+
+}

+ 29 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/RoleRepo.java

@@ -0,0 +1,29 @@
+package cn.com.qmth.examcloud.core.basic.dao;
+
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.repository.query.QueryByExampleExecutor;
+
+import cn.com.qmth.examcloud.core.basic.dao.entity.RoleEntity;
+
+public interface RoleRepo
+		extends
+			JpaRepository<RoleEntity, Long>,
+			QueryByExampleExecutor<RoleEntity>,
+			JpaSpecificationExecutor<RoleEntity> {
+
+	RoleEntity findByCodeAndRootOrgIdIsNull(String code);
+
+	RoleEntity findByCode(String code);
+
+	RoleEntity findByNameAndRootOrgId(String name, Long rootOrgId);
+
+	RoleEntity findByCodeAndRootOrgId(String code, Long rootOrgId);
+
+	List<RoleEntity> findByRootOrgIdIsNull();
+
+	List<RoleEntity> findByRootOrgId(Long rootOrgId);
+
+}

+ 26 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/RootOrgPrivilegeRelationRepo.java

@@ -0,0 +1,26 @@
+package cn.com.qmth.examcloud.core.basic.dao;
+
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.repository.query.QueryByExampleExecutor;
+
+import cn.com.qmth.examcloud.core.basic.dao.entity.RootOrgPrivilegeRelationEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.RootOrgPrivilegeRelationPK;
+
+public interface RootOrgPrivilegeRelationRepo
+		extends
+			JpaRepository<RootOrgPrivilegeRelationEntity, RootOrgPrivilegeRelationPK>,
+			QueryByExampleExecutor<RootOrgPrivilegeRelationEntity>,
+			JpaSpecificationExecutor<RootOrgPrivilegeRelationEntity> {
+
+	void deleteByRootOrgIdAndGroupId(Long rootOrgId, Long groupId);
+
+	void deleteByPrivilegeId(Long privilegeId);
+
+	List<RootOrgPrivilegeRelationEntity> findAllByRootOrgId(Long rootOrgId);
+
+	List<RootOrgPrivilegeRelationEntity> findAllByRootOrgIdAndGroupId(Long rootOrgId, Long groupId);
+
+}

+ 13 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/SmsAssemblyRepo.java

@@ -0,0 +1,13 @@
+package cn.com.qmth.examcloud.core.basic.dao;
+
+import cn.com.qmth.examcloud.core.basic.dao.entity.SmsAssemblyEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+public interface SmsAssemblyRepo
+		extends
+			JpaRepository<SmsAssemblyEntity, Long>,
+			JpaSpecificationExecutor<SmsAssemblyEntity> {
+
+	SmsAssemblyEntity findByCode(String code);
+}

+ 22 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/SpecialtyRepo.java

@@ -0,0 +1,22 @@
+package cn.com.qmth.examcloud.core.basic.dao;
+
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.repository.query.QueryByExampleExecutor;
+
+import cn.com.qmth.examcloud.core.basic.dao.entity.SpecialtyEntity;
+
+public interface SpecialtyRepo
+		extends
+			JpaRepository<SpecialtyEntity, Long>,
+			QueryByExampleExecutor<SpecialtyEntity>,
+			JpaSpecificationExecutor<SpecialtyEntity> {
+
+	SpecialtyEntity findByRootOrgIdAndCode(Long rootOrgId, String code);
+
+	List<SpecialtyEntity> findByRootOrgId(Long rootOrgId);
+
+	int countByRootOrgIdAndNameLike(Long rootOrgId,String name);
+}

+ 24 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/StudentCodeRepo.java

@@ -0,0 +1,24 @@
+package cn.com.qmth.examcloud.core.basic.dao;
+
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.repository.query.QueryByExampleExecutor;
+
+import cn.com.qmth.examcloud.core.basic.dao.entity.StudentCodeEntity;
+
+/**
+ * Created by songyue on 17/1/13.
+ */
+public interface StudentCodeRepo
+		extends
+			JpaRepository<StudentCodeEntity, Long>,
+			QueryByExampleExecutor<StudentCodeEntity>,
+			JpaSpecificationExecutor<StudentCodeEntity> {
+
+	StudentCodeEntity findByStudentCodeAndRootOrgId(String studentCode, Long rootOrgId);
+
+	List<StudentCodeEntity> findByStudentId(Long studentId);
+
+}

+ 13 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/StudentFaceRepo.java

@@ -0,0 +1,13 @@
+package cn.com.qmth.examcloud.core.basic.dao;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.repository.query.QueryByExampleExecutor;
+
+import cn.com.qmth.examcloud.core.basic.dao.entity.StudentFaceEntity;
+
+public interface StudentFaceRepo
+		extends
+			JpaRepository<StudentFaceEntity, Long>,
+			QueryByExampleExecutor<StudentFaceEntity> {
+
+}

+ 30 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/StudentRepo.java

@@ -0,0 +1,30 @@
+package cn.com.qmth.examcloud.core.basic.dao;
+
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.repository.query.QueryByExampleExecutor;
+
+import cn.com.qmth.examcloud.core.basic.dao.entity.StudentEntity;
+
+/**
+ * Created by songyue on 17/1/13.
+ */
+public interface StudentRepo
+		extends
+			JpaRepository<StudentEntity, Long>,
+			QueryByExampleExecutor<StudentEntity>,
+			JpaSpecificationExecutor<StudentEntity> {
+
+	StudentEntity findByIdentityNumber(String identityNumber);
+
+	StudentEntity findByIdentityNumberAndRootOrgId(String identityNumber, Long rootOrgId);
+
+	StudentEntity findBySecurityPhone(String securityPhone);
+
+	int countByRootOrgId(Long rootOrgId);
+
+	List<StudentEntity> findByOrgId(Long orgId);
+
+}

+ 22 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/SystemPropertyRepo.java

@@ -0,0 +1,22 @@
+package cn.com.qmth.examcloud.core.basic.dao;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.repository.query.QueryByExampleExecutor;
+
+import cn.com.qmth.examcloud.core.basic.dao.entity.SystemPropertyEntity;
+
+/**
+ * 类注释
+ *
+ * @author WANGWEI
+ * @date 2018年12月3日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public interface SystemPropertyRepo
+		extends
+			JpaRepository<SystemPropertyEntity, String>,
+			QueryByExampleExecutor<SystemPropertyEntity>,
+			JpaSpecificationExecutor<SystemPropertyEntity> {
+
+}

+ 12 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/ThirdPartyAccessRepo.java

@@ -0,0 +1,12 @@
+package cn.com.qmth.examcloud.core.basic.dao;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.repository.query.QueryByExampleExecutor;
+
+import cn.com.qmth.examcloud.core.basic.dao.entity.ThirdPartyAccessEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.ThirdPartyAccessPK;
+
+public interface ThirdPartyAccessRepo
+		extends JpaRepository<ThirdPartyAccessEntity, ThirdPartyAccessPK>, QueryByExampleExecutor<ThirdPartyAccessEntity> {
+
+}

+ 29 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/UniqueRuleHolder.java

@@ -0,0 +1,29 @@
+package cn.com.qmth.examcloud.core.basic.dao;
+
+import java.util.List;
+
+import com.google.common.collect.Lists;
+
+import cn.com.qmth.examcloud.web.jpa.UniqueRule;
+
+/**
+ * 状态码范围: 111XXX
+ *
+ * @author WANGWEI
+ * @date 2019年3月8日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public class UniqueRuleHolder {
+
+	private static final List<UniqueRule> LIST = Lists.newArrayList();
+
+	public static List<UniqueRule> getUniqueRuleList() {
+		return LIST;
+	}
+
+	static {
+		// ResourceEntity
+		LIST.add(new UniqueRule("IDX_B_RESOURCE_000002", "111001", "路径已存在"));
+	}
+
+}

+ 43 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/UserRepo.java

@@ -0,0 +1,43 @@
+package cn.com.qmth.examcloud.core.basic.dao;
+
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.data.repository.query.QueryByExampleExecutor;
+import org.springframework.transaction.annotation.Transactional;
+
+import cn.com.qmth.examcloud.core.basic.dao.entity.UserEntity;
+
+/**
+ * Created by songyue on 17/1/13.
+ */
+public interface UserRepo
+		extends
+			JpaRepository<UserEntity, Long>,
+			QueryByExampleExecutor<UserEntity>,
+			JpaSpecificationExecutor<UserEntity> {
+
+	UserEntity findByRootOrgIdAndLoginName(long rootOrgId, String loginName);
+
+	List<UserEntity> findByLoginName(String loginName);
+
+	@Transactional
+	@Modifying
+	@Query("update UserEntity set password = :password where id = :id")
+	void updatePasswordById(@Param("id") long id, @Param("password") String password);
+
+	int countByLoginName(String loginName);
+
+	List<UserEntity> findByOrgId(Long orgId);
+
+	List<UserEntity> findByRootOrgId(Long rootOrgId);
+
+	int countByRootOrgId(Long rootOrgId);
+
+	List<UserEntity> findByIdIn(List<Long> userIdList);
+	
+}

+ 33 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/UserRoleRelationRepo.java

@@ -0,0 +1,33 @@
+package cn.com.qmth.examcloud.core.basic.dao;
+
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.repository.query.QueryByExampleExecutor;
+
+import cn.com.qmth.examcloud.core.basic.dao.entity.UserRoleRelationEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.UserRoleRelationPK;
+
+/**
+ * 类注释
+ *
+ * @author WANGWEI
+ * @date 2018年7月11日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public interface UserRoleRelationRepo
+		extends
+			JpaRepository<UserRoleRelationEntity, UserRoleRelationPK>,
+			QueryByExampleExecutor<UserRoleRelationEntity>,
+			JpaSpecificationExecutor<UserRoleRelationEntity> {
+
+	List<UserRoleRelationEntity> findAllByUserId(Long userId);
+
+	void deleteByUserId(Long userId);
+
+	int countByRoleId(Long roleId);
+
+	void deleteByRoleId(Long roleId);
+
+}

+ 114 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/AppEntity.java

@@ -0,0 +1,114 @@
+package cn.com.qmth.examcloud.core.basic.dao.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Index;
+import javax.persistence.Table;
+
+import cn.com.qmth.examcloud.web.jpa.JpaEntity;
+
+/**
+ * 应用
+ *
+ * @author WANGWEI
+ * @date 2018年5月23日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@Entity
+@Table(name = "EC_B_APP", indexes = {
+		@Index(name = "IDX_B_APP_001001", columnList = "name", unique = true),
+		@Index(name = "IDX_B_APP_001002", columnList = "code", unique = true),
+		@Index(name = "IDX_B_APP_001002", columnList = "simpleName", unique = true)})
+public class AppEntity extends JpaEntity {
+
+	private static final long serialVersionUID = -6288949236298877031L;
+
+	/**
+	 * 应用ID
+	 */
+	@Id
+	@GeneratedValue(strategy = GenerationType.IDENTITY)
+	private Long id;
+
+	/**
+	 * 应用名称
+	 */
+	@Column(unique = true, nullable = false)
+	private String name;
+
+	/**
+	 * 应用名称简称
+	 */
+	@Column(unique = true, nullable = false)
+	private String simpleName;
+
+	/**
+	 * 应用编码
+	 */
+	@Column(unique = true, nullable = false)
+	private String code;
+
+	/**
+	 * 密钥
+	 */
+	@Column(nullable = false, length = 100)
+	private String secretKey;
+
+	/**
+	 * 接口鉴权时差范围
+	 */
+	@Column(nullable = false, length = 100)
+	private Long timeRange;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getSimpleName() {
+		return simpleName;
+	}
+
+	public void setSimpleName(String simpleName) {
+		this.simpleName = simpleName;
+	}
+
+	public String getCode() {
+		return code;
+	}
+
+	public void setCode(String code) {
+		this.code = code;
+	}
+
+	public String getSecretKey() {
+		return secretKey;
+	}
+
+	public void setSecretKey(String secretKey) {
+		this.secretKey = secretKey;
+	}
+
+	public Long getTimeRange() {
+		return timeRange;
+	}
+
+	public void setTimeRange(Long timeRange) {
+		this.timeRange = timeRange;
+	}
+
+}

+ 98 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/CourseEntity.java

@@ -0,0 +1,98 @@
+package cn.com.qmth.examcloud.core.basic.dao.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Index;
+import javax.persistence.Table;
+
+import cn.com.qmth.examcloud.api.commons.enums.CourseLevel;
+import cn.com.qmth.examcloud.web.jpa.JpaEntity;
+
+/**
+ * 
+ * @Description: 课程
+ * @author ting.yin
+ * @date 2017年1月5日
+ */
+@Entity
+@Table(name = "EC_B_COURSE", indexes = {
+		@Index(name = "IDX_B_COURSE_002001", columnList = "rootOrgId,code", unique = true),
+		@Index(name = "IDX_B_COURSE_002002", columnList = "rootOrgId,name", unique = false)})
+public class CourseEntity extends JpaEntity {
+
+	private static final long serialVersionUID = -6261302618070108336L;
+
+	@Id
+	@GeneratedValue(strategy = GenerationType.IDENTITY)
+	private Long id;
+
+	@Column(nullable = false)
+	private Long rootOrgId;
+
+	@Column(nullable = false)
+	private String code;
+
+	@Column(nullable = false)
+	private String name;
+
+	@Column(nullable = false)
+	@Enumerated(EnumType.STRING)
+	private CourseLevel level;
+
+	@Column(nullable = false)
+	private Boolean enable;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+	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 CourseLevel getLevel() {
+		return level;
+	}
+
+	public void setLevel(CourseLevel level) {
+		this.level = level;
+	}
+
+	public Boolean getEnable() {
+		return enable;
+	}
+
+	public void setEnable(Boolean enable) {
+		this.enable = enable;
+	}
+
+}

+ 56 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/CourseSpeciatlyRelationEntity.java

@@ -0,0 +1,56 @@
+package cn.com.qmth.examcloud.core.basic.dao.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.Table;
+
+import cn.com.qmth.examcloud.web.jpa.JpaEntity;
+
+/**
+ * @Description 专业课程关联
+ * @author Administrator
+ * @date 2017年8月8号
+ */
+@Entity
+@Table(name = "EC_B_CS_RELATION")
+@IdClass(CourseSpeciatlyRelationPK.class)
+public class CourseSpeciatlyRelationEntity extends JpaEntity {
+
+	private static final long serialVersionUID = -4747240416438623301L;
+
+	@Id
+	private Long courseId;
+
+	@Id
+	private Long specialtyId;
+
+	@Column(nullable = true)
+	private Long creator;
+
+	public Long getCourseId() {
+		return courseId;
+	}
+
+	public void setCourseId(Long courseId) {
+		this.courseId = courseId;
+	}
+
+	public Long getSpecialtyId() {
+		return specialtyId;
+	}
+
+	public void setSpecialtyId(Long specialtyId) {
+		this.specialtyId = specialtyId;
+	}
+
+	public Long getCreator() {
+		return creator;
+	}
+
+	public void setCreator(Long creator) {
+		this.creator = creator;
+	}
+
+}

+ 36 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/CourseSpeciatlyRelationPK.java

@@ -0,0 +1,36 @@
+package cn.com.qmth.examcloud.core.basic.dao.entity;
+
+import java.io.Serializable;
+
+/**
+ * 类注释
+ *
+ * @author WANGWEI
+ * @date 2018年8月27日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public class CourseSpeciatlyRelationPK implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+
+	private Long courseId;
+
+	private Long specialtyId;
+
+	public Long getCourseId() {
+		return courseId;
+	}
+
+	public void setCourseId(Long courseId) {
+		this.courseId = courseId;
+	}
+
+	public Long getSpecialtyId() {
+		return specialtyId;
+	}
+
+	public void setSpecialtyId(Long specialtyId) {
+		this.specialtyId = specialtyId;
+	}
+
+}

+ 91 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/FacesetEntity.java

@@ -0,0 +1,91 @@
+package cn.com.qmth.examcloud.core.basic.dao.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+import cn.com.qmth.examcloud.web.jpa.JpaEntity;
+
+/**
+ * face++ faceset
+ *
+ * @author WANGWEI
+ * @date 2018年9月3日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@Entity
+@Table(name = "EC_B_FACESET")
+public class FacesetEntity extends JpaEntity {
+
+	private static final long serialVersionUID = -1831392800541099736L;
+
+	@Id
+	@GeneratedValue(strategy = GenerationType.IDENTITY)
+	private Long id;
+
+	@Column(nullable = false, unique = true)
+	private String facesetToken;
+
+	private Long faceCount;
+
+	private String outerId;
+
+	private String displayName;
+
+	private String tags;
+
+	@Column(nullable = false)
+	private Boolean enable;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public String getFacesetToken() {
+		return facesetToken;
+	}
+
+	public void setFacesetToken(String facesetToken) {
+		this.facesetToken = facesetToken;
+	}
+
+	public Long getFaceCount() {
+		return faceCount;
+	}
+
+	public void setFaceCount(Long faceCount) {
+		this.faceCount = faceCount;
+	}
+
+	public String getOuterId() {
+		return outerId;
+	}
+
+	public void setOuterId(String outerId) {
+		this.outerId = outerId;
+	}
+
+	public String getDisplayName() {
+		return displayName;
+	}
+
+	public void setDisplayName(String displayName) {
+		this.displayName = displayName;
+	}
+
+	public String getTags() {
+		return tags;
+	}
+
+	public void setTags(String tags) {
+		this.tags = tags;
+	}
+
+}

+ 138 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/OrgEntity.java

@@ -0,0 +1,138 @@
+package cn.com.qmth.examcloud.core.basic.dao.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Index;
+import javax.persistence.Table;
+
+import cn.com.qmth.examcloud.web.jpa.JpaEntity;
+
+/**
+ * 顶级机构:parentId is null; id=rootId<br>
+ *
+ * @author WANGWEI
+ * @date 2018年8月23日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@Entity
+@Table(name = "EC_B_ORG", indexes = {
+		@Index(name = "IDX_B_ORG_003001", columnList = "rootId,code", unique = true)})
+public class OrgEntity extends JpaEntity {
+
+	private static final long serialVersionUID = -592353272256492483L;
+
+	@Id
+	@GeneratedValue(strategy = GenerationType.IDENTITY)
+	private Long id;
+
+	@Column(nullable = false)
+	private Long rootId;
+
+	@Column(nullable = true)
+	private Long parentId;
+
+	@Column(nullable = false)
+	private String code;
+
+	@Column(nullable = false)
+	private String name;
+
+	@Column(nullable = false)
+	private Boolean enable;
+
+	@Column(nullable = true)
+	private String telephone;
+
+	@Column(nullable = true)
+	private String contacts;
+
+	@Column(nullable = true)
+	private String remark;
+
+	@Column(nullable = true, unique = true)
+	private String domainName;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public Long getRootId() {
+		return rootId;
+	}
+
+	public void setRootId(Long rootId) {
+		this.rootId = rootId;
+	}
+
+	public Long getParentId() {
+		return parentId;
+	}
+
+	public void setParentId(Long parentId) {
+		this.parentId = parentId;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getCode() {
+		return code;
+	}
+
+	public void setCode(String code) {
+		this.code = code;
+	}
+
+	public Boolean getEnable() {
+		return enable;
+	}
+
+	public void setEnable(Boolean enable) {
+		this.enable = enable;
+	}
+
+	public String getTelephone() {
+		return telephone;
+	}
+
+	public void setTelephone(String telephone) {
+		this.telephone = telephone;
+	}
+
+	public String getContacts() {
+		return contacts;
+	}
+
+	public void setContacts(String contacts) {
+		this.contacts = contacts;
+	}
+
+	public String getRemark() {
+		return remark;
+	}
+
+	public void setRemark(String remark) {
+		this.remark = remark;
+	}
+
+	public String getDomainName() {
+		return domainName;
+	}
+
+	public void setDomainName(String domainName) {
+		this.domainName = domainName;
+	}
+
+}

+ 73 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/OrgPropertyEntity.java

@@ -0,0 +1,73 @@
+package cn.com.qmth.examcloud.core.basic.dao.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Index;
+import javax.persistence.Lob;
+import javax.persistence.Table;
+
+import cn.com.qmth.examcloud.web.jpa.JpaEntity;
+
+/**
+ * 机构属性配置
+ *
+ * @author WANGWEI
+ * @date 2018年8月6日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@Entity
+@Table(name = "EC_B_ORG_PROP", indexes = {
+		@Index(name = "IDX_B_ORG_P_002001", columnList = "orgId,keyId", unique = true)})
+public class OrgPropertyEntity extends JpaEntity {
+
+	private static final long serialVersionUID = 4009839764353162256L;
+
+	@Id
+	@GeneratedValue(strategy = GenerationType.IDENTITY)
+	private Long id;
+
+	@Column(nullable = false)
+	private Long orgId;
+
+	@Column(nullable = false)
+	private Long keyId;
+
+	@Lob
+	private String value;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public Long getOrgId() {
+		return orgId;
+	}
+
+	public void setOrgId(Long orgId) {
+		this.orgId = orgId;
+	}
+
+	public Long getKeyId() {
+		return keyId;
+	}
+
+	public void setKeyId(Long keyId) {
+		this.keyId = keyId;
+	}
+
+	public String getValue() {
+		return value;
+	}
+
+	public void setValue(String value) {
+		this.value = value;
+	}
+
+}

+ 249 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/PrivilegeEntity.java

@@ -0,0 +1,249 @@
+package cn.com.qmth.examcloud.core.basic.dao.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+import cn.com.qmth.examcloud.web.helpers.tree.TreeNode;
+import cn.com.qmth.examcloud.web.jpa.JpaEntity;
+
+/**
+ * 权限
+ *
+ * @author WANGWEI
+ * @date 2018年5月23日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@Entity
+@Table(name = "EC_B_PRIVILEGE")
+public class PrivilegeEntity extends JpaEntity implements TreeNode {
+
+	private static final long serialVersionUID = -6288949236298877031L;
+
+	/**
+	 * 权限ID
+	 */
+	@Id
+	@GeneratedValue(strategy = GenerationType.IDENTITY)
+	private Long id;
+
+	/**
+	 * 权限码
+	 */
+	@Column(unique = true, nullable = false)
+	private String code;
+
+	/**
+	 * 权限名称
+	 */
+	@Column(unique = false, nullable = false)
+	private String name;
+
+	/**
+	 * 父权限ID
+	 */
+	private Long parentId;
+
+	/**
+	 * 组(权限分组,同一组的权限构成权限树)
+	 */
+	@Column(unique = false, nullable = false)
+	private Long groupId;
+
+	/**
+	 * 描述
+	 */
+	@Column(unique = false, nullable = true)
+	private String description;
+
+	/**
+	 * 权重(越大越靠前)
+	 */
+	@Column(unique = false, nullable = false)
+	private Long weight = 0L;
+
+	/**
+	 * 扩展属性
+	 */
+	@Column(unique = false, nullable = true)
+	private String ext1;
+
+	/**
+	 * 扩展属性
+	 */
+	@Column(unique = false, nullable = true)
+	private String ext2;
+
+	/**
+	 * 扩展属性
+	 */
+	@Column(unique = false, nullable = true)
+	private String ext3;
+
+	/**
+	 * 扩展属性
+	 */
+	@Column(unique = false, nullable = true)
+	private String ext4;
+
+	/**
+	 * 扩展属性
+	 */
+	@Column(unique = false, nullable = true)
+	private String ext5;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	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 Long getParentId() {
+		return parentId;
+	}
+
+	public void setParentId(Long parentId) {
+		this.parentId = parentId;
+	}
+
+	public Long getGroupId() {
+		return groupId;
+	}
+
+	public void setGroupId(Long groupId) {
+		this.groupId = groupId;
+	}
+
+	public String getDescription() {
+		return description;
+	}
+
+	public void setDescription(String description) {
+		this.description = description;
+	}
+
+	public Long getWeight() {
+		return weight;
+	}
+
+	public void setWeight(Long weight) {
+		this.weight = weight;
+	}
+
+	public String getExt1() {
+		return ext1;
+	}
+
+	public void setExt1(String ext1) {
+		this.ext1 = ext1;
+	}
+
+	public String getExt2() {
+		return ext2;
+	}
+
+	public void setExt2(String ext2) {
+		this.ext2 = ext2;
+	}
+
+	public String getExt3() {
+		return ext3;
+	}
+
+	public void setExt3(String ext3) {
+		this.ext3 = ext3;
+	}
+
+	public String getExt4() {
+		return ext4;
+	}
+
+	public void setExt4(String ext4) {
+		this.ext4 = ext4;
+	}
+
+	public String getExt5() {
+		return ext5;
+	}
+
+	public void setExt5(String ext5) {
+		this.ext5 = ext5;
+	}
+
+	@Override
+	public String getNodeId() {
+		if (null == this.id) {
+			return null;
+		}
+		return String.valueOf(this.id);
+	}
+
+	@Override
+	public void setNodeId(String nodeId) {
+		if (null == nodeId) {
+			this.id = null;
+		}
+		this.id = Long.parseLong(nodeId);
+	}
+
+	@Override
+	public String getNodeName() {
+		if (null == this.name) {
+			return null;
+		}
+		return String.valueOf(this.name);
+	}
+
+	@Override
+	public void setNodeName(String nodeName) {
+		this.name = nodeName;
+	}
+
+	@Override
+	public String getParentNodeId() {
+		if (null == this.parentId) {
+			return null;
+		}
+		return String.valueOf(this.parentId);
+	}
+
+	@Override
+	public void setParentNodeId(String parentNodeId) {
+		if (null == parentNodeId) {
+			this.parentId = null;
+		}
+		this.parentId = Long.parseLong(parentNodeId);
+	}
+
+	@Override
+	public String getNodeCode() {
+		return this.code;
+	}
+
+	@Override
+	public void setNodeCode(String code) {
+		this.code = code;
+	}
+
+}

+ 172 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/PrivilegeGroupEntity.java

@@ -0,0 +1,172 @@
+package cn.com.qmth.examcloud.core.basic.dao.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Index;
+import javax.persistence.Table;
+
+import cn.com.qmth.examcloud.api.commons.enums.PrivilegeGroupType;
+import cn.com.qmth.examcloud.web.jpa.JpaEntity;
+
+/**
+ * 权限组
+ *
+ * @author WANGWEI
+ * @date 2018年5月23日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@Entity
+@Table(name = "EC_B_PRIVILEGE_GROUP", indexes = {
+		@Index(name = "IDX_B_PRI_G_002001", columnList = "code", unique = true),
+		@Index(name = "IDX_B_PRI_G_002002", columnList = "rootOrgId,type", unique = true)})
+public class PrivilegeGroupEntity extends JpaEntity {
+
+	private static final long serialVersionUID = -3654724059677675683L;
+
+	/**
+	 * 权限组ID
+	 */
+	@Id
+	@GeneratedValue(strategy = GenerationType.IDENTITY)
+	private Long id;
+
+	/**
+	 * 权限组名称
+	 */
+	@Column(nullable = false)
+	private String name;
+
+	/**
+	 * 权限组编码
+	 */
+	@Column(nullable = false)
+	private String code;
+
+	/**
+	 * 权限组类型
+	 */
+	@Enumerated(EnumType.STRING)
+	private PrivilegeGroupType type;
+
+	/**
+	 * 顶级机构ID
+	 */
+	@Column(nullable = true)
+	private Long rootOrgId;
+
+	/**
+	 * 扩展属性
+	 */
+	@Column(unique = false, nullable = true)
+	private String ext1;
+
+	/**
+	 * 扩展属性
+	 */
+	@Column(unique = false, nullable = true)
+	private String ext2;
+
+	/**
+	 * 扩展属性
+	 */
+	@Column(unique = false, nullable = true)
+	private String ext3;
+
+	/**
+	 * 扩展属性
+	 */
+	@Column(unique = false, nullable = true)
+	private String ext4;
+
+	/**
+	 * 扩展属性
+	 */
+	@Column(unique = false, nullable = true)
+	private String ext5;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getCode() {
+		return code;
+	}
+
+	public void setCode(String code) {
+		this.code = code;
+	}
+
+	public PrivilegeGroupType getType() {
+		return type;
+	}
+
+	public void setType(PrivilegeGroupType type) {
+		this.type = type;
+	}
+
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+	public String getExt1() {
+		return ext1;
+	}
+
+	public void setExt1(String ext1) {
+		this.ext1 = ext1;
+	}
+
+	public String getExt2() {
+		return ext2;
+	}
+
+	public void setExt2(String ext2) {
+		this.ext2 = ext2;
+	}
+
+	public String getExt3() {
+		return ext3;
+	}
+
+	public void setExt3(String ext3) {
+		this.ext3 = ext3;
+	}
+
+	public String getExt4() {
+		return ext4;
+	}
+
+	public void setExt4(String ext4) {
+		this.ext4 = ext4;
+	}
+
+	public String getExt5() {
+		return ext5;
+	}
+
+	public void setExt5(String ext5) {
+		this.ext5 = ext5;
+	}
+
+}

+ 110 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/ResourceEntity.java

@@ -0,0 +1,110 @@
+package cn.com.qmth.examcloud.core.basic.dao.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Index;
+import javax.persistence.Table;
+
+import cn.com.qmth.examcloud.web.jpa.JpaEntity;
+
+@Entity
+@Table(name = "EC_B_RESOURCE", indexes = {
+        @Index(name = "IDX_B_RESOURCE_000001", columnList = "rootOrgId,parentId", unique = false),
+        @Index(name = "IDX_B_RESOURCE_000002", columnList = "rootOrgId,filePath", unique = true) })
+public class ResourceEntity extends JpaEntity {
+
+    private static final long serialVersionUID = 4326176782077577894L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    @Column(nullable = false)
+    private Long rootOrgId;
+
+    @Column(nullable = false)
+    private String name;
+
+    @Column(nullable = false)
+    private Boolean isFile;
+
+    @Column(nullable = true)
+    private String suffix;
+
+    @Column(nullable = false)
+    private String filePath;
+
+    @Column(nullable = true)
+    private Long parentId;
+
+    @Column(nullable = true)
+    private String fileTreatyPath;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getRootOrgId() {
+        return rootOrgId;
+    }
+
+    public void setRootOrgId(Long rootOrgId) {
+        this.rootOrgId = rootOrgId;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Boolean getIsFile() {
+        return isFile;
+    }
+
+    public void setIsFile(Boolean isFile) {
+        this.isFile = isFile;
+    }
+
+    public String getSuffix() {
+        return suffix;
+    }
+
+    public void setSuffix(String suffix) {
+        this.suffix = suffix;
+    }
+
+    public String getFilePath() {
+        return filePath;
+    }
+
+    public void setFilePath(String filePath) {
+        this.filePath = filePath;
+    }
+
+    public Long getParentId() {
+        return parentId;
+    }
+
+    public void setParentId(Long parentId) {
+        this.parentId = parentId;
+    }
+    
+    public String getFileTreatyPath() {
+        return fileTreatyPath;
+    }
+    
+    public void setFileTreatyPath(String fileTreatyPath) {
+        this.fileTreatyPath = fileTreatyPath;
+    }
+
+}

+ 85 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/RoleEntity.java

@@ -0,0 +1,85 @@
+package cn.com.qmth.examcloud.core.basic.dao.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Index;
+import javax.persistence.Table;
+
+import cn.com.qmth.examcloud.web.jpa.JpaEntity;
+
+/**
+ * 角色
+ *
+ * @author WANGWEI
+ * @date 2018年5月23日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@Entity
+@Table(name = "EC_B_ROLE", indexes = {
+		@Index(name = "IDX_B_R_010001", columnList = "rootOrgId,code", unique = true),
+		@Index(name = "IDX_B_R_010002", columnList = "rootOrgId,name", unique = true)})
+public class RoleEntity extends JpaEntity {
+
+	private static final long serialVersionUID = -2167420238674588632L;
+
+	/**
+	 * 角色ID
+	 */
+	@Id
+	@GeneratedValue(strategy = GenerationType.IDENTITY)
+	private Long id;
+
+	/**
+	 * 角色码(全局唯一)
+	 */
+	@Column(unique = true, nullable = false)
+	private String code;
+
+	/**
+	 * 角色名称
+	 */
+	@Column(unique = false, nullable = false)
+	private String name;
+
+	/**
+	 * 顶级机构.为null时,为全局角色
+	 */
+	@Column(nullable = true)
+	private Long rootOrgId;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	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 Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+}

+ 57 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/RolePrivilegeRelationEntity.java

@@ -0,0 +1,57 @@
+package cn.com.qmth.examcloud.core.basic.dao.entity;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.Table;
+
+import cn.com.qmth.examcloud.web.jpa.JpaEntity;
+
+/**
+ * 角色权限关联
+ *
+ * @author WANGWEI
+ * @date 2018年5月23日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@Entity
+@Table(name = "EC_B_ROLE_PRIV_RELATION")
+@IdClass(RolePrivilegeRelationPK.class)
+public class RolePrivilegeRelationEntity extends JpaEntity {
+
+	private static final long serialVersionUID = 8849214483955278647L;
+
+	@Id
+	private Long roleId;
+
+	@Id
+	private Long privilegeId;
+
+	@Id
+	private Long rootOrgId;
+
+	public Long getRoleId() {
+		return roleId;
+	}
+
+	public void setRoleId(Long roleId) {
+		this.roleId = roleId;
+	}
+
+	public Long getPrivilegeId() {
+		return privilegeId;
+	}
+
+	public void setPrivilegeId(Long privilegeId) {
+		this.privilegeId = privilegeId;
+	}
+
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+}

+ 57 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/RolePrivilegeRelationPK.java

@@ -0,0 +1,57 @@
+package cn.com.qmth.examcloud.core.basic.dao.entity;
+
+import java.io.Serializable;
+
+/**
+ * 角色权限关联
+ *
+ * @author WANGWEI
+ * @date 2018年5月23日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public class RolePrivilegeRelationPK implements Serializable {
+
+	private static final long serialVersionUID = 7674517327265591905L;
+
+	private Long roleId;
+
+	private Long privilegeId;
+
+	private Long rootOrgId;
+
+	public RolePrivilegeRelationPK() {
+		super();
+	}
+
+	public RolePrivilegeRelationPK(Long roleId, Long privilegeId, Long rootOrgId) {
+		super();
+		this.roleId = roleId;
+		this.privilegeId = privilegeId;
+		this.rootOrgId = rootOrgId;
+	}
+
+	public Long getRoleId() {
+		return roleId;
+	}
+
+	public void setRoleId(Long roleId) {
+		this.roleId = roleId;
+	}
+
+	public Long getPrivilegeId() {
+		return privilegeId;
+	}
+
+	public void setPrivilegeId(Long privilegeId) {
+		this.privilegeId = privilegeId;
+	}
+
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+}

+ 61 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/RootOrgPrivilegeRelationEntity.java

@@ -0,0 +1,61 @@
+package cn.com.qmth.examcloud.core.basic.dao.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.Table;
+
+import cn.com.qmth.examcloud.web.jpa.JpaEntity;
+
+/**
+ * 顶级机构权限关联
+ *
+ * @author WANGWEI
+ * @date 2018年5月23日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@Entity
+@Table(name = "EC_B_ROOT_ORG_PRIV_RELATION")
+@IdClass(RootOrgPrivilegeRelationPK.class)
+public class RootOrgPrivilegeRelationEntity extends JpaEntity {
+
+	private static final long serialVersionUID = 8849214483955278647L;
+
+	@Id
+	private Long rootOrgId;
+
+	@Id
+	private Long privilegeId;
+
+	/**
+	 * 组(权限分组,同一组的权限构成权限树)
+	 */
+	@Column(unique = false, nullable = false)
+	private Long groupId;
+
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+	public Long getPrivilegeId() {
+		return privilegeId;
+	}
+
+	public void setPrivilegeId(Long privilegeId) {
+		this.privilegeId = privilegeId;
+	}
+
+	public Long getGroupId() {
+		return groupId;
+	}
+
+	public void setGroupId(Long groupId) {
+		this.groupId = groupId;
+	}
+
+}

+ 46 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/RootOrgPrivilegeRelationPK.java

@@ -0,0 +1,46 @@
+package cn.com.qmth.examcloud.core.basic.dao.entity;
+
+import java.io.Serializable;
+
+/**
+ * 角色权限关联
+ *
+ * @author WANGWEI
+ * @date 2018年5月23日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public class RootOrgPrivilegeRelationPK implements Serializable {
+
+	private static final long serialVersionUID = 7674517327265591905L;
+
+	private Long privilegeId;
+
+	private Long rootOrgId;
+
+	public RootOrgPrivilegeRelationPK() {
+		super();
+	}
+
+	public RootOrgPrivilegeRelationPK(Long privilegeId, Long rootOrgId) {
+		super();
+		this.privilegeId = privilegeId;
+		this.rootOrgId = rootOrgId;
+	}
+
+	public Long getPrivilegeId() {
+		return privilegeId;
+	}
+
+	public void setPrivilegeId(Long privilegeId) {
+		this.privilegeId = privilegeId;
+	}
+
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+}

+ 221 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/SmsAssemblyEntity.java

@@ -0,0 +1,221 @@
+package cn.com.qmth.examcloud.core.basic.dao.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Index;
+import javax.persistence.Table;
+
+import cn.com.qmth.examcloud.web.jpa.WithIdAndStatusJpaEntity;
+
+/**
+ * 短信装配
+ *
+ * @author Lideyin
+ * @date 2019年3月27日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@Entity
+@Table(name = "EC_B_SMS_ASSEMBLY", indexes = {
+		@Index(name = "IDX_B_SMS_021000", columnList = "code", unique = true),
+		@Index(name = "IDX_B_SMS_021001", columnList = "name", unique = true)})
+public class SmsAssemblyEntity extends WithIdAndStatusJpaEntity {
+
+	private static final long serialVersionUID = 3155706159257187078L;
+
+	/**
+	 * 短信装配编码
+	 */
+	@Column(nullable = false, length = 30)
+	private String code;
+
+	/**
+	 * 短信装配名称
+	 */
+	@Column(nullable = false, length = 30)
+	private String name;
+
+	/**
+	 * 短信样例
+	 */
+	@Column(nullable = false, length = 200)
+	private String example;
+
+	/**
+	 * 短信模板
+	 */
+	@Column(nullable = false, length = 200)
+	private String template;
+
+	/**
+	 * 扩展属性1
+	 */
+	@Column(nullable = true, length = 200)
+	private String ext1;
+
+	/**
+	 * 扩展属性2
+	 */
+	@Column(nullable = true, length = 200)
+	private String ext2;
+
+	/**
+	 * 扩展属性3
+	 */
+	@Column(nullable = true, length = 200)
+	private String ext3;
+
+	/**
+	 * 扩展属性4
+	 */
+	@Column(nullable = true, length = 200)
+	private String ext4;
+
+	/**
+	 * 扩展属性5
+	 */
+	@Column(nullable = true, length = 200)
+	private String ext5;
+
+	/**
+	 * 扩展属性6
+	 */
+	@Column(nullable = true, length = 200)
+	private String ext6;
+
+	/**
+	 * 扩展属性7
+	 */
+	@Column(nullable = true, length = 200)
+	private String ext7;
+
+	/**
+	 * 扩展属性8
+	 */
+	@Column(nullable = true, length = 200)
+	private String ext8;
+
+	/**
+	 * 扩展属性9
+	 */
+	@Column(nullable = true, length = 200)
+	private String ext9;
+
+	/**
+	 * 扩展属性10
+	 */
+	@Column(nullable = true, length = 200)
+	private String ext10;
+
+	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 getExample() {
+		return example;
+	}
+
+	public void setExample(String example) {
+		this.example = example;
+	}
+
+	public String getTemplate() {
+		return template;
+	}
+
+	public void setTemplate(String template) {
+		this.template = template;
+	}
+
+	public String getExt1() {
+		return ext1;
+	}
+
+	public void setExt1(String ext1) {
+		this.ext1 = ext1;
+	}
+
+	public String getExt2() {
+		return ext2;
+	}
+
+	public void setExt2(String ext2) {
+		this.ext2 = ext2;
+	}
+
+	public String getExt3() {
+		return ext3;
+	}
+
+	public void setExt3(String ext3) {
+		this.ext3 = ext3;
+	}
+
+	public String getExt4() {
+		return ext4;
+	}
+
+	public void setExt4(String ext4) {
+		this.ext4 = ext4;
+	}
+
+	public String getExt5() {
+		return ext5;
+	}
+
+	public void setExt5(String ext5) {
+		this.ext5 = ext5;
+	}
+
+	public String getExt6() {
+		return ext6;
+	}
+
+	public void setExt6(String ext6) {
+		this.ext6 = ext6;
+	}
+
+	public String getExt7() {
+		return ext7;
+	}
+
+	public void setExt7(String ext7) {
+		this.ext7 = ext7;
+	}
+
+	public String getExt8() {
+		return ext8;
+	}
+
+	public void setExt8(String ext8) {
+		this.ext8 = ext8;
+	}
+
+	public String getExt9() {
+		return ext9;
+	}
+
+	public void setExt9(String ext9) {
+		this.ext9 = ext9;
+	}
+
+	public String getExt10() {
+		return ext10;
+	}
+
+	public void setExt10(String ext10) {
+		this.ext10 = ext10;
+	}
+
+}

+ 82 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/SpecialtyEntity.java

@@ -0,0 +1,82 @@
+package cn.com.qmth.examcloud.core.basic.dao.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Index;
+import javax.persistence.Table;
+
+import cn.com.qmth.examcloud.web.jpa.JpaEntity;
+
+/**
+ * @Description: 专业
+ * @author weiwenhai
+ * @date 2017年8月7号
+ */
+@Entity
+@Table(name = "EC_B_SPECIALTY", indexes = {
+		@Index(name = "IDX_B_SPE_002001", columnList = "rootOrgId,code", unique = true),
+		@Index(name = "IDX_B_SPE_002002", columnList = "rootOrgId,name", unique = false)})
+public class SpecialtyEntity extends JpaEntity {
+
+	private static final long serialVersionUID = 239263456816448160L;
+
+	@Id
+	@GeneratedValue(strategy = GenerationType.IDENTITY)
+	private Long id;
+
+	@Column(nullable = false)
+	private String code;
+
+	@Column(nullable = false)
+	private String name;
+
+	@Column(nullable = false)
+	private Long rootOrgId;
+
+	@Column(nullable = false)
+	private Boolean enable;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	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 Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+	public Boolean getEnable() {
+		return enable;
+	}
+
+	public void setEnable(Boolean enable) {
+		this.enable = enable;
+	}
+
+}

+ 86 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/StudentCodeEntity.java

@@ -0,0 +1,86 @@
+package cn.com.qmth.examcloud.core.basic.dao.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Index;
+import javax.persistence.Table;
+
+import cn.com.qmth.examcloud.web.jpa.JpaEntity;
+
+/**
+ * 学生学号
+ *
+ * @author WANGWEI
+ * @date 2018年8月22日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@Entity
+@Table(name = "EC_B_STUDENT_CODE", indexes = {
+		@Index(name = "IDX_B_SC_005001", columnList = "rootOrgId,studentCode", unique = true),
+		@Index(name = "IDX_B_SC_005002", columnList = "studentId", unique = false),
+		@Index(name = "IDX_B_SC_005003", columnList = "studentCode", unique = false),
+		@Index(name = "IDX_B_SC_005004", columnList = "identityNumber", unique = false)})
+public class StudentCodeEntity extends JpaEntity {
+
+	private static final long serialVersionUID = 757531976286006550L;
+
+	@Id
+	@GeneratedValue(strategy = GenerationType.IDENTITY)
+	private Long id;
+
+	@Column(nullable = false)
+	private Long rootOrgId;
+
+	@Column(nullable = false)
+	private Long studentId;
+
+	@Column(nullable = false)
+	private String identityNumber;
+
+	@Column(nullable = false)
+	private String studentCode;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+	public Long getStudentId() {
+		return studentId;
+	}
+
+	public void setStudentId(Long studentId) {
+		this.studentId = studentId;
+	}
+
+	public String getIdentityNumber() {
+		return identityNumber;
+	}
+
+	public void setIdentityNumber(String identityNumber) {
+		this.identityNumber = identityNumber;
+	}
+
+	public String getStudentCode() {
+		return studentCode;
+	}
+
+	public void setStudentCode(String studentCode) {
+		this.studentCode = studentCode;
+	}
+
+}

+ 158 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/StudentEntity.java

@@ -0,0 +1,158 @@
+package cn.com.qmth.examcloud.core.basic.dao.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Index;
+import javax.persistence.Table;
+
+import cn.com.qmth.examcloud.web.jpa.JpaEntity;
+
+/**
+ * 类注释
+ *
+ * @author WANGWEI
+ * @date 2018年8月22日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@Entity
+@Table(name = "EC_B_STUDENT", indexes = {
+		@Index(name = "IDX_B_S_002001", columnList = "rootOrgId,identityNumber", unique = true)})
+public class StudentEntity extends JpaEntity {
+
+	private static final long serialVersionUID = 757531976286006550L;
+
+	@Id
+	@GeneratedValue(strategy = GenerationType.IDENTITY)
+	private Long id;
+
+	@Column(nullable = false)
+	private String name;
+
+	@Column(nullable = false)
+	private String password;
+
+	@Column(nullable = false)
+	private String identityNumber;
+
+	private String photoPath;
+
+	private String remark;
+
+	@Column(nullable = false)
+	private Boolean enable;
+
+	private Long orgId;
+
+	@Column(nullable = false)
+	private Long rootOrgId;
+
+	/**
+	 * 手机号码
+	 */
+	private String phoneNumber;
+
+	/**
+	 * 安全手机号码(用于登录)
+	 */
+	@Column(unique = true, nullable = true)
+	private String securityPhone;
+
+	public StudentEntity() {
+	}
+
+	public StudentEntity(Long studentId) {
+		this.id = studentId;
+	}
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getPassword() {
+		return password;
+	}
+
+	public void setPassword(String password) {
+		this.password = password;
+	}
+
+	public String getIdentityNumber() {
+		return identityNumber;
+	}
+
+	public void setIdentityNumber(String identityNumber) {
+		this.identityNumber = identityNumber;
+	}
+
+	public String getPhotoPath() {
+		return photoPath;
+	}
+
+	public void setPhotoPath(String photoPath) {
+		this.photoPath = photoPath;
+	}
+
+	public String getRemark() {
+		return remark;
+	}
+
+	public void setRemark(String remark) {
+		this.remark = remark;
+	}
+
+	public Boolean getEnable() {
+		return enable;
+	}
+
+	public void setEnable(Boolean enable) {
+		this.enable = enable;
+	}
+
+	public Long getOrgId() {
+		return orgId;
+	}
+
+	public void setOrgId(Long orgId) {
+		this.orgId = orgId;
+	}
+
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+	public String getPhoneNumber() {
+		return phoneNumber;
+	}
+
+	public void setPhoneNumber(String phoneNumber) {
+		this.phoneNumber = phoneNumber;
+	}
+
+	public String getSecurityPhone() {
+		return securityPhone;
+	}
+
+	public void setSecurityPhone(String securityPhone) {
+		this.securityPhone = securityPhone;
+	}
+
+}

+ 82 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/StudentFaceEntity.java

@@ -0,0 +1,82 @@
+package cn.com.qmth.examcloud.core.basic.dao.entity;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+import cn.com.qmth.examcloud.web.jpa.JpaEntity;
+
+/**
+ * 学生人脸识别信息
+ *
+ * @author WANGWEI
+ * @date 2018年8月22日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@Entity
+@Table(name = "EC_B_STUDENT_FACE")
+public class StudentFaceEntity extends JpaEntity {
+
+	private static final long serialVersionUID = -3722400898212427026L;
+
+	@Id
+	private Long studentId;
+
+	private Long facesetId;
+
+	/**
+	 * face++参数
+	 */
+	private String faceToken;
+
+	/**
+	 * 创建人
+	 */
+	private String creator;
+
+	/**
+	 * 更新人
+	 */
+	private String modifiedBy;
+
+	public Long getStudentId() {
+		return studentId;
+	}
+
+	public void setStudentId(Long studentId) {
+		this.studentId = studentId;
+	}
+
+	public Long getFacesetId() {
+		return facesetId;
+	}
+
+	public void setFacesetId(Long facesetId) {
+		this.facesetId = facesetId;
+	}
+
+	public String getFaceToken() {
+		return faceToken;
+	}
+
+	public void setFaceToken(String faceToken) {
+		this.faceToken = faceToken;
+	}
+
+	public String getCreator() {
+		return creator;
+	}
+
+	public void setCreator(String creator) {
+		this.creator = creator;
+	}
+
+	public String getModifiedBy() {
+		return modifiedBy;
+	}
+
+	public void setModifiedBy(String modifiedBy) {
+		this.modifiedBy = modifiedBy;
+	}
+
+}

+ 72 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/SystemPropertyEntity.java

@@ -0,0 +1,72 @@
+package cn.com.qmth.examcloud.core.basic.dao.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+import cn.com.qmth.examcloud.api.commons.enums.BasicDataType;
+import cn.com.qmth.examcloud.web.jpa.JpaEntity;
+
+/**
+ * 系统配置
+ *
+ * @author WANGWEI
+ * @date 2018年12月3日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@Entity
+@Table(name = "EC_B_SYS_PROP")
+public class SystemPropertyEntity extends JpaEntity {
+
+	private static final long serialVersionUID = -3688247462533274177L;
+
+	@Id
+	@Column(nullable = false, length = 200)
+	private String propKey;
+
+	@Column(nullable = false, length = 200)
+	private String description;
+
+	@Column(nullable = false)
+	@Enumerated(EnumType.STRING)
+	private BasicDataType propValueType;
+
+	@Column(nullable = true, length = 1000)
+	private String propValue;
+
+	public String getPropKey() {
+		return propKey;
+	}
+
+	public void setPropKey(String propKey) {
+		this.propKey = propKey;
+	}
+
+	public String getDescription() {
+		return description;
+	}
+
+	public void setDescription(String description) {
+		this.description = description;
+	}
+
+	public BasicDataType getPropValueType() {
+		return propValueType;
+	}
+
+	public void setPropValueType(BasicDataType propValueType) {
+		this.propValueType = propValueType;
+	}
+
+	public String getPropValue() {
+		return propValue;
+	}
+
+	public void setPropValue(String propValue) {
+		this.propValue = propValue;
+	}
+
+}

+ 71 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/ThirdPartyAccessEntity.java

@@ -0,0 +1,71 @@
+package cn.com.qmth.examcloud.core.basic.dao.entity;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.Table;
+
+import cn.com.qmth.examcloud.web.jpa.JpaEntity;
+
+/**
+ * 第三方接入信息
+ * 
+ * @author WANGWEI
+ *
+ */
+@Entity
+@IdClass(ThirdPartyAccessPK.class)
+@Table(name = "EC_B_THIRD_PARTY_ACCESS")
+public class ThirdPartyAccessEntity extends JpaEntity {
+
+	private static final long serialVersionUID = -4967242922242580681L;
+
+	@Id
+	private Long rootOrgId;
+
+	@Id
+	private String appId;
+
+	/**
+	 * 密钥
+	 */
+	private String secretKey;
+
+	/**
+	 * 时间差范围
+	 */
+	private Long timeRange;
+
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+	public String getAppId() {
+		return appId;
+	}
+
+	public void setAppId(String appId) {
+		this.appId = appId;
+	}
+
+	public String getSecretKey() {
+		return secretKey;
+	}
+
+	public void setSecretKey(String secretKey) {
+		this.secretKey = secretKey;
+	}
+
+	public Long getTimeRange() {
+		return timeRange;
+	}
+
+	public void setTimeRange(Long timeRange) {
+		this.timeRange = timeRange;
+	}
+
+}

+ 45 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/ThirdPartyAccessPK.java

@@ -0,0 +1,45 @@
+package cn.com.qmth.examcloud.core.basic.dao.entity;
+
+import java.io.Serializable;
+
+/**
+ * 第三方接入信息
+ * 
+ * @author WANGWEI
+ *
+ */
+public class ThirdPartyAccessPK implements Serializable {
+
+	private static final long serialVersionUID = -2564683239937634130L;
+
+	private Long rootOrgId;
+
+	private String appId;
+
+	public ThirdPartyAccessPK() {
+		super();
+	}
+
+	public ThirdPartyAccessPK(Long rootOrgId, String appId) {
+		super();
+		this.rootOrgId = rootOrgId;
+		this.appId = appId;
+	}
+
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+	public String getAppId() {
+		return appId;
+	}
+
+	public void setAppId(String appId) {
+		this.appId = appId;
+	}
+
+}

+ 137 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/UserEntity.java

@@ -0,0 +1,137 @@
+package cn.com.qmth.examcloud.core.basic.dao.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Index;
+import javax.persistence.Table;
+
+import cn.com.qmth.examcloud.api.commons.enums.Gender;
+import cn.com.qmth.examcloud.web.jpa.JpaEntity;
+
+/**
+ * Created by songyue on 17/1/13.
+ */
+@Entity
+@Table(name = "EC_B_USER", indexes = {
+		@Index(name = "IDX_B_U_002001", columnList = "rootOrgId,loginName", unique = true)})
+public class UserEntity extends JpaEntity {
+
+	private static final long serialVersionUID = 6770398649449396459L;
+
+	@Id
+	@GeneratedValue(strategy = GenerationType.IDENTITY)
+	private Long id;
+
+	@Column(nullable = false)
+	private Long rootOrgId;
+
+	private Long orgId;
+
+	@Column(nullable = false)
+	private String name;
+
+	@Column(nullable = false)
+	private String loginName;
+
+	@Column(nullable = false)
+	private String password;
+
+	private String phoneNumber;
+
+	@Enumerated(EnumType.STRING)
+	private Gender gender;
+
+	/**
+	 * 头像
+	 */
+	private String avatar;
+
+	@Column(nullable = false)
+	private Boolean enable;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+	public Long getOrgId() {
+		return orgId;
+	}
+
+	public void setOrgId(Long orgId) {
+		this.orgId = orgId;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getLoginName() {
+		return loginName;
+	}
+
+	public void setLoginName(String loginName) {
+		this.loginName = loginName;
+	}
+
+	public String getPassword() {
+		return password;
+	}
+
+	public void setPassword(String password) {
+		this.password = password;
+	}
+
+	public String getPhoneNumber() {
+		return phoneNumber;
+	}
+
+	public void setPhoneNumber(String phoneNumber) {
+		this.phoneNumber = phoneNumber;
+	}
+
+	public Gender getGender() {
+		return gender;
+	}
+
+	public void setGender(Gender gender) {
+		this.gender = gender;
+	}
+
+	public String getAvatar() {
+		return avatar;
+	}
+
+	public void setAvatar(String avatar) {
+		this.avatar = avatar;
+	}
+
+	public Boolean getEnable() {
+		return enable;
+	}
+
+	public void setEnable(Boolean enable) {
+		this.enable = enable;
+	}
+
+}

+ 57 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/UserPrivilegeRelationEntity.java

@@ -0,0 +1,57 @@
+package cn.com.qmth.examcloud.core.basic.dao.entity;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.Table;
+
+import cn.com.qmth.examcloud.web.jpa.JpaEntity;
+
+/**
+ * 权限
+ *
+ * @author WANGWEI
+ * @date 2018年5月23日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@Entity
+@Table(name = "EC_B_USER_PRIV_RELATION")
+@IdClass(UserPrivilegeRelationPK.class)
+public class UserPrivilegeRelationEntity extends JpaEntity {
+
+	private static final long serialVersionUID = -2673489395106849624L;
+
+	@Id
+	private String userType;
+
+	@Id
+	private Long userId;
+
+	@Id
+	private Long privilegeId;
+
+	public String getUserType() {
+		return userType;
+	}
+
+	public void setUserType(String userType) {
+		this.userType = userType;
+	}
+
+	public Long getUserId() {
+		return userId;
+	}
+
+	public void setUserId(Long userId) {
+		this.userId = userId;
+	}
+
+	public Long getPrivilegeId() {
+		return privilegeId;
+	}
+
+	public void setPrivilegeId(Long privilegeId) {
+		this.privilegeId = privilegeId;
+	}
+
+}

+ 46 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/UserPrivilegeRelationPK.java

@@ -0,0 +1,46 @@
+package cn.com.qmth.examcloud.core.basic.dao.entity;
+
+import java.io.Serializable;
+
+/**
+ * 权限
+ *
+ * @author WANGWEI
+ * @date 2018年5月23日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public class UserPrivilegeRelationPK implements Serializable {
+
+	private static final long serialVersionUID = 3451129659871377605L;
+
+	private String userType;
+
+	private Long userId;
+
+	private Long privilegeId;
+
+	public String getUserType() {
+		return userType;
+	}
+
+	public void setUserType(String userType) {
+		this.userType = userType;
+	}
+
+	public Long getUserId() {
+		return userId;
+	}
+
+	public void setUserId(Long userId) {
+		this.userId = userId;
+	}
+
+	public Long getPrivilegeId() {
+		return privilegeId;
+	}
+
+	public void setPrivilegeId(Long privilegeId) {
+		this.privilegeId = privilegeId;
+	}
+
+}

Some files were not shown because too many files changed in this diff