Преглед на файлове

merge from release_v4.0.2

deason преди 4 години
родител
ревизия
76a969a367
променени са 55 файла, в които са добавени 2888 реда и са изтрити 486 реда
  1. 39 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/AdminOperateController.java
  2. 55 15
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/CourseController.java
  3. 131 51
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/OrgController.java
  4. 38 19
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/RolePrivilegeController.java
  5. 96 50
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/StudentController.java
  6. 144 17
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/UserController.java
  7. 91 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/UserDataRuleController.java
  8. 14 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/controller/bean/UserFormDomain.java
  9. 1 1
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/provider/AuthCloudServiceProvider.java
  10. 1 1
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/provider/CourseCloudServiceProvider.java
  11. 1 1
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/provider/FaceCloudServiceProvider.java
  12. 1 1
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/provider/OrgCloudServiceProvider.java
  13. 1 1
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/provider/RolePrivilegeCloudServiceProvider.java
  14. 77 16
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/provider/StudentCloudServiceProvider.java
  15. 1 1
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/provider/SystemPropertyCloudServiceProvider.java
  16. 16 4
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/provider/UserCloudServiceProvider.java
  17. 68 0
      examcloud-core-basic-api-provider/src/main/java/cn/com/qmth/examcloud/core/basic/api/provider/UserDataRuleCloudServiceProvider.java
  18. 12 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/AdminOperateRepo.java
  19. 12 12
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/CourseRepo.java
  20. 5 1
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/OrgRepo.java
  21. 2 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/StudentRepo.java
  22. 34 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/UserDataRuleRepo.java
  23. 2 2
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/UserRepo.java
  24. 90 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/AdminOperateEntity.java
  25. 15 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/StudentEntity.java
  26. 103 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/UserDataRuleEntity.java
  27. 15 0
      examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/entity/UserEntity.java
  28. 14 0
      examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/AdminOperateService.java
  29. 1 1
      examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/OrgService.java
  30. 70 0
      examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/UserDataRuleService.java
  31. 6 3
      examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/UserService.java
  32. 100 0
      examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/bean/AdminOperateBean.java
  33. 42 0
      examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/bean/AdminOperateQuery.java
  34. 34 0
      examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/bean/datarule/UserDataRuleCopyReq.java
  35. 67 0
      examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/bean/datarule/UserDataRuleForCourseInfo.java
  36. 67 0
      examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/bean/datarule/UserDataRuleForExamInfo.java
  37. 67 0
      examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/bean/datarule/UserDataRuleForOrgInfo.java
  38. 56 0
      examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/bean/datarule/UserDataRuleForm.java
  39. 79 0
      examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/bean/datarule/UserDataRuleInfo.java
  40. 74 0
      examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/bean/datarule/UserDataRuleQuery.java
  41. 0 27
      examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/cache/LoginRuleCacheBean.java
  42. 39 0
      examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/cache/UserDataRuleCacheBean.java
  43. 130 0
      examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/impl/AdminOperateServiceImpl.java
  44. 8 3
      examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/impl/AuthServiceImpl.java
  45. 2 4
      examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/impl/LoginRuleServiceImpl.java
  46. 33 7
      examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/impl/OrgServiceImpl.java
  47. 13 6
      examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/impl/StudentServiceImpl.java
  48. 455 0
      examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/impl/UserDataRuleServiceImpl.java
  49. 61 39
      examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/impl/UserServiceImpl.java
  50. 68 0
      examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/report/ExamProcessRecordReport.java
  51. 72 0
      examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/report/RocketMqConsumerListener.java
  52. 65 63
      examcloud-core-basic-starter/src/main/java/cn/com/qmth/examcloud/core/basic/starter/CoreBasicApp.java
  53. 126 111
      examcloud-core-basic-starter/src/main/java/cn/com/qmth/examcloud/core/basic/starter/config/ExamCloudResourceManager.java
  54. 30 29
      examcloud-core-basic-starter/src/main/java/cn/com/qmth/examcloud/core/basic/starter/config/ExamCloudWebMvcConfigurer.java
  55. 44 0
      examcloud-core-basic-starter/src/main/java/cn/com/qmth/examcloud/core/basic/starter/config/MqConsumerListenerStartup.java

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

@@ -0,0 +1,39 @@
+package cn.com.qmth.examcloud.core.basic.api.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.api.commons.exchange.PageInfo;
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.core.basic.service.AdminOperateService;
+import cn.com.qmth.examcloud.core.basic.service.bean.AdminOperateBean;
+import cn.com.qmth.examcloud.core.basic.service.bean.AdminOperateQuery;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+
+@RestController
+@Api(tags = "管理端操作日志")
+@RequestMapping("${$rmp.ctr.basic}/" + "adminOperate")
+public class AdminOperateController extends ControllerSupport {
+	@Autowired
+	private AdminOperateService adminOperateService;
+
+	@GetMapping("page/{pageNo}/{pageSize}")
+	@ApiOperation(value = "日志分页查询")
+	public PageInfo<AdminOperateBean> queryPage(@ModelAttribute AdminOperateQuery req,
+			@PathVariable @ApiParam(value = "pageNo = 1,2,3...") Integer pageNo, @PathVariable Integer pageSize) {
+		User user = getAccessUser();
+		if(isSuperAdmin()) {
+			return adminOperateService.queryPage(req, pageNo, pageSize, null);
+		}else {
+			return adminOperateService.queryPage(req, pageNo, pageSize, user.getRootOrgId());
+		}
+	}
+	
+}

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

@@ -1,7 +1,9 @@
 package cn.com.qmth.examcloud.core.basic.api.controller;
 
 import cn.com.qmth.examcloud.api.commons.enums.CourseLevel;
+import cn.com.qmth.examcloud.api.commons.enums.DataRuleType;
 import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.api.commons.security.bean.UserDataRule;
 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;
@@ -11,11 +13,13 @@ 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.UserDataRuleService;
 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.security.DataRule;
 import cn.com.qmth.examcloud.web.support.ControllerSupport;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
@@ -27,6 +31,7 @@ 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.PageImpl;
 import org.springframework.data.domain.PageRequest;
 import org.springframework.data.domain.Sort;
 import org.springframework.data.domain.Sort.Direction;
@@ -70,6 +75,9 @@ public class CourseController extends ControllerSupport {
     @Autowired
     DataSyncCloudService dataSyncCloudService;
 
+    @Autowired
+    UserDataRuleService userDataRuleService;
+
     private static final String[] EXCEL_HEADER = new String[]{"课程名称", "课程代码", "层次(ZSB,GQZ,ALL)"};
 
     /**
@@ -85,20 +93,32 @@ public class CourseController extends ControllerSupport {
      * @return
      * @author WANGWEI
      */
+    @DataRule(type = DataRuleType.COURSE)
     @ApiOperation(value = "分页查询课程")
     @GetMapping("coursePage/{curPage}/{pageSize}")
     public Page<CourseEntity> getCoursePage(@PathVariable Integer curPage,
-                                            @PathVariable Integer pageSize, @RequestParam(required = false) String name,
+                                            @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();
 
+        PageRequest pageable = PageRequest.of(curPage, pageSize, new Sort(Direction.DESC, "updateTime", "id"));
+
+        UserDataRule userDataRule = super.getUserDataRule(DataRuleType.COURSE);
+        if (userDataRule.assertEmptyQueryResult()) {
+            return new PageImpl<>(new ArrayList<>(), pageable, 0L);
+        }
+
         Specification<CourseEntity> specification = (root, query, cb) -> {
             List<Predicate> predicates = new ArrayList<>();
 
+            if (userDataRule.assertNeedQueryRefIds()) {
+                predicates.add(root.get("id").in(userDataRule.getRefIds()));
+            }
+
             predicates.add(cb.equal(root.get("rootOrgId"), accessUser.getRootOrgId()));
 
             if (StringUtils.isNotBlank(name)) {
@@ -129,10 +149,7 @@ public class CourseController extends ControllerSupport {
             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);
+        Page<CourseEntity> page = courseRepo.findAll(specification, pageable);
         return page;
     }
 
@@ -146,6 +163,7 @@ public class CourseController extends ControllerSupport {
      * @return
      * @author WANGWEI
      */
+    @DataRule(type = DataRuleType.COURSE)
     @ApiOperation(value = "查询课程")
     @GetMapping("query")
     public List<CourseEntity> query(@RequestParam(required = false) Long rootOrgId,
@@ -153,36 +171,40 @@ public class CourseController extends ControllerSupport {
                                     @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;
 
+        UserDataRule userDataRule = super.getUserDataRule(DataRuleType.COURSE);
+        if (userDataRule.assertEmptyQueryResult()) {
+            return new ArrayList<>();
+        }
+
         Specification<CourseEntity> specification = (root, query, cb) -> {
             List<Predicate> predicates = new ArrayList<>();
 
-            predicates.add(cb.equal(root.get("rootOrgId"), finalRootOrgId));
+            if (userDataRule.assertNeedQueryRefIds()) {
+                predicates.add(root.get("id").in(userDataRule.getRefIds()));
+            }
 
+            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<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"));
@@ -194,7 +216,6 @@ public class CourseController extends ControllerSupport {
         };
 
         PageRequest pageRequest = PageRequest.of(0, 50, new Sort(Direction.DESC, "updateTime"));
-
         Page<CourseEntity> page = courseRepo.findAll(specification, pageRequest);
 
         Iterator<CourseEntity> iterator = page.iterator();
@@ -299,6 +320,10 @@ public class CourseController extends ControllerSupport {
         info.setName(domain.getName());
 
         CourseEntity saved = courseService.saveCourse(info);
+
+        // 用户与课程的数据权限
+        userDataRuleService.addUserDataRule(accessUser.getUserId(), DataRuleType.COURSE, saved.getId());
+
         return saved.getId();
     }
 
@@ -379,6 +404,7 @@ public class CourseController extends ControllerSupport {
         return map;
     }
 
+    @DataRule(type = DataRuleType.COURSE)
     @ApiOperation(value = "导出课程", notes = "导出")
     @GetMapping("export")
     public void export(@RequestParam(required = false) String name,
@@ -389,9 +415,19 @@ public class CourseController extends ControllerSupport {
 
         User accessUser = getAccessUser();
 
+        UserDataRule userDataRule = super.getUserDataRule(DataRuleType.COURSE);
+        if (userDataRule.assertEmptyQueryResult()) {
+            this.doExport(new ArrayList<>());
+            return;
+        }
+
         Specification<CourseEntity> specification = (root, query, cb) -> {
             List<Predicate> predicates = new ArrayList<>();
 
+            if (userDataRule.assertNeedQueryRefIds()) {
+                predicates.add(root.get("id").in(userDataRule.getRefIds()));
+            }
+
             predicates.add(cb.equal(root.get("rootOrgId"), accessUser.getRootOrgId()));
 
             if (StringUtils.isNotBlank(name)) {
@@ -428,7 +464,10 @@ public class CourseController extends ControllerSupport {
         }
 
         List<CourseEntity> list = courseRepo.findAll(specification);
+        this.doExport(list);
+    }
 
+    private void doExport(List<CourseEntity> list) {
         List<Object[]> datas = Lists.newArrayList();
 
         for (CourseEntity cur : list) {
@@ -520,4 +559,5 @@ public class CourseController extends ControllerSupport {
 
         return courseIds;
     }
+
 }

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

@@ -1,7 +1,9 @@
 package cn.com.qmth.examcloud.core.basic.api.controller;
 
+import cn.com.qmth.examcloud.api.commons.enums.DataRuleType;
 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.bean.UserDataRule;
 import cn.com.qmth.examcloud.commons.exception.ExamCloudRuntimeException;
 import cn.com.qmth.examcloud.commons.exception.StatusException;
 import cn.com.qmth.examcloud.commons.helpers.DynamicEnum;
@@ -18,11 +20,14 @@ 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.UserDataRuleService;
 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.reports.commons.bean.AdminOperateReport;
+import cn.com.qmth.examcloud.reports.commons.util.ReportsUtil;
 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;
@@ -35,6 +40,7 @@ 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.security.DataRule;
 import cn.com.qmth.examcloud.web.support.ApiId;
 import cn.com.qmth.examcloud.web.support.ControllerSupport;
 import cn.com.qmth.examcloud.web.support.Naked;
@@ -50,10 +56,7 @@ 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.*;
 import org.springframework.data.domain.Sort.Direction;
 import org.springframework.data.jpa.domain.Specification;
 import org.springframework.transaction.annotation.Transactional;
@@ -108,15 +111,15 @@ public class OrgController extends ControllerSupport {
     @Autowired
     DataSyncCloudService dataSyncCloudService;
 
-//	@Autowired
-//	UpyunCloudService upyunCloudService;
-
     @Autowired
     SystemPropertyCache systemPropertyCache;
 
     @Autowired
     SystemPropertyService systemPropertyService;
 
+    @Autowired
+    UserDataRuleService userDataRuleService;
+
     @ApiOperation(value = "分页查询所有机构")
     @GetMapping("fullOrgPage/{curPage}/{pageSize}")
     public PageInfo<OrgDomain> getFullOrgPage(@PathVariable Integer curPage,
@@ -361,6 +364,7 @@ public class OrgController extends ControllerSupport {
      * @return
      * @author WANGWEI
      */
+    @DataRule(type = DataRuleType.ORG)
     @ApiOperation(value = "分页查询子机构")
     @GetMapping("subOrgPage/{curPage}/{pageSize}")
     public PageInfo<OrgDomain> getSubOrgPage(@PathVariable Integer curPage,
@@ -369,7 +373,6 @@ public class OrgController extends ControllerSupport {
                                              @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");
         }
@@ -382,8 +385,18 @@ public class OrgController extends ControllerSupport {
 
         Pageable pageable = PageRequest.of(curPage, pageSize, Sort.Direction.DESC, "updateTime");
 
+        UserDataRule userDataRule = super.getUserDataRule(DataRuleType.ORG);
+        if (userDataRule.assertEmptyQueryResult()) {
+            return new PageInfo(new PageImpl<>(new ArrayList<>(), pageable, 0L));
+        }
+
         Specification<OrgEntity> specification = (root, query, cb) -> {
             List<Predicate> predicates = new ArrayList<>();
+
+            if (userDataRule.assertNeedQueryRefIds()) {
+                predicates.add(root.get("id").in(userDataRule.getRefIds()));
+            }
+
             predicates.add(cb.equal(root.get("parentId"), parentId));
 
             if (StringUtils.isNotEmpty(name)) {
@@ -466,30 +479,37 @@ public class OrgController extends ControllerSupport {
      * @return
      * @author WANGWEI
      */
+    @DataRule(type = DataRuleType.ORG)
     @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;
+        if (null != returnEmptyWhenNameNull && returnEmptyWhenNameNull && StringUtils.isBlank(name)) {
+            return new ArrayList<>();
+        }
+
+        UserDataRule userDataRule = super.getUserDataRule(DataRuleType.ORG);
+        if (userDataRule.assertEmptyQueryResult()) {
+            return new ArrayList<>();
         }
 
         Specification<OrgEntity> specification = (root, query, cb) -> {
             List<Predicate> predicates = new ArrayList<>();
+
+            if (userDataRule.assertNeedQueryRefIds()) {
+                predicates.add(root.get("id").in(userDataRule.getRefIds()));
+            }
+
             predicates.add(cb.equal(root.get("rootId"), finalRootOrgId));
             predicates.add(cb.isNotNull(root.get("parentId")));
             if (StringUtils.isNotBlank(name)) {
@@ -671,27 +691,27 @@ public class OrgController extends ControllerSupport {
         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());
+            //			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;
@@ -935,6 +955,10 @@ public class OrgController extends ControllerSupport {
         info.setProperties(properties);
 
         OrgEntity saved = orgService.saveSubOrg(info);
+
+        // 用户与学习中心的数据权限
+        userDataRuleService.addUserDataRule(accessUser.getUserId(), DataRuleType.ORG, saved.getId());
+
         return saved;
     }
 
@@ -1085,6 +1109,7 @@ public class OrgController extends ControllerSupport {
 
         validateRootOrgIsolation(orgEntity.getRootId());
 
+        String changeInfo = getChangeInfo(domain);
         orgService.saveOrgProperties(orgEntity.getId(), properties);
 
         if (CollectionUtils.isNotEmpty(relatedPropertyGroupIdList)) {
@@ -1098,6 +1123,57 @@ public class OrgController extends ControllerSupport {
             }
         }
 
+        User accessUser = getAccessUser();
+        if (changeInfo != null) {
+            ReportsUtil.report(new AdminOperateReport(accessUser.getRootOrgId(), accessUser.getUserId(), "考生端配置-保存考生端配置", changeInfo));
+        }
+    }
+
+    private String getChangeInfo(SaveOrgPropertiesDomain domain) {
+        Map<String, String> old = getAllOrgProperties(domain.getOrgId());
+        Map<String, String> prop = domain.getProperties();
+        if (old == null || old.size() == 0) {
+            return "新增配置";
+        }
+        StringBuilder sb = new StringBuilder();
+        if (!StringUtils.equals(prop.get("OE_STUDENT_SYS_NAME"), old.get("OE_STUDENT_SYS_NAME"))) {
+            sb.append("系统名称,");
+        }
+        if (!StringUtils.equals(prop.get("LOGO_FILE_URL"), old.get("LOGO_FILE_URL"))) {
+            sb.append("学校logo,");
+        }
+        if (!StringUtils.equals(prop.get("LOGIN_TYPE"), old.get("LOGIN_TYPE"))) {
+            sb.append("登录帐号选择,");
+        }
+        if (!StringUtils.equals(prop.get("STUDENT_CODE_LOGIN_ALIAS"), old.get("STUDENT_CODE_LOGIN_ALIAS"))) {
+            sb.append("学号登录别名,");
+        }
+        if (!StringUtils.equals(prop.get("IDENTITY_NUMBER_LOGIN_ALIAS"), old.get("IDENTITY_NUMBER_LOGIN_ALIAS"))) {
+            sb.append("身份证号登录别名,");
+        }
+        if (!StringUtils.equals(prop.get("LOGIN_SUPPORT"), old.get("LOGIN_SUPPORT"))) {
+            sb.append("考生端登录方式,");
+        }
+        if (!StringUtils.equals(prop.get("STUDENT_CLIENT_CONSOLE_CONFIG"), old.get("STUDENT_CLIENT_CONSOLE_CONFIG"))) {
+            sb.append("控制台配置,");
+        }
+        if (!StringUtils.equals(prop.get("PREVENT_CHEATING_CONFIG"), old.get("PREVENT_CHEATING_CONFIG"))) {
+            sb.append("防作弊配置,");
+        }
+        if (!StringUtils.equals(prop.get("STUDENT_CLIENT_VERSION"), old.get("STUDENT_CLIENT_VERSION"))) {
+            sb.append("学生端版本,");
+        }
+        if (!StringUtils.equals(prop.get("IS_CUSTOM_MENU_LOGO"), old.get("IS_CUSTOM_MENU_LOGO"))) {
+            sb.append("自定义菜单logo,");
+        }
+        if (!StringUtils.equals(prop.get("CUS_MENU_LOGO_FILE_URL"), old.get("CUS_MENU_LOGO_FILE_URL"))) {
+            sb.append("菜单logo,");
+        }
+        if (sb.length() != 0) {
+            sb.deleteCharAt(sb.length() - 1);
+            return "修改内容:" + sb.toString();
+        }
+        return null;
     }
 
     /**
@@ -1199,7 +1275,6 @@ public class OrgController extends ControllerSupport {
         File storeLocation = fileItem.getStoreLocation();
         String name = file.getOriginalFilename();
 
-        System.out.println(storeLocation.length());
         if (999999 < storeLocation.length()) {
             throw new StatusException("140082", "文件过大");
         }
@@ -1255,17 +1330,17 @@ public class OrgController extends ControllerSupport {
 
         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();
+        //		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();
@@ -1283,7 +1358,8 @@ public class OrgController extends ControllerSupport {
         }
         logoFileUrlEntity.setValue(url);
         orgPropertyRepo.save(logoFileUrlEntity);
-
+        User accessUser = getAccessUser();
+        ReportsUtil.report(new AdminOperateReport(accessUser.getRootOrgId(), accessUser.getUserId(), "考生端配置-上传学校logo", null));
         return url;
     }
 
@@ -1301,8 +1377,10 @@ public class OrgController extends ControllerSupport {
     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);
+
+        User accessUser = getAccessUser();
+        List<Map<String, Object>> failRecords = orgService.importSubOrg(accessUser.getRootOrgId(), accessUser.getUserId(), storeLocation);
+
         Map<String, Object> map = Maps.newHashMap();
         map.put("hasError", CollectionUtils.isNotEmpty(failRecords));
         map.put("failRecords", failRecords);
@@ -1334,7 +1412,6 @@ public class OrgController extends ControllerSupport {
         File storeLocation = fileItem.getStoreLocation();
         String name = file.getOriginalFilename();
 
-        System.out.println(storeLocation.length());
         if (999999 < storeLocation.length()) {
             throw new StatusException("140082", "文件过大");
         }
@@ -1407,6 +1484,9 @@ public class OrgController extends ControllerSupport {
         logoFileUrlEntity.setValue(url);
         orgPropertyRepo.save(logoFileUrlEntity);
 
+        User accessUser = getAccessUser();
+        ReportsUtil.report(new AdminOperateReport(accessUser.getRootOrgId(), accessUser.getUserId(), "考生端配置-上传菜单logo", null));
         return url;
     }
+
 }

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

@@ -12,6 +12,8 @@ import cn.com.qmth.examcloud.core.basic.dao.*;
 import cn.com.qmth.examcloud.core.basic.dao.entity.*;
 import cn.com.qmth.examcloud.core.basic.service.RolePrivilegeService;
 import cn.com.qmth.examcloud.core.basic.service.bean.RoleInfo;
+import cn.com.qmth.examcloud.reports.commons.bean.AdminOperateReport;
+import cn.com.qmth.examcloud.reports.commons.util.ReportsUtil;
 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;
@@ -44,7 +46,7 @@ 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","online_num"});
+                    "school_config", "index_school_privilege_settings", "system", "online_num"});
 
     @Autowired
     PrivilegeGroupRepo privilegeGroupRepo;
@@ -103,7 +105,7 @@ public class RolePrivilegeController extends ControllerSupport {
     @ApiOperation(value = "查询用户的权限树", notes = "")
     @PostMapping("getUserPrivileges")
     public List<PrivilegeDomain> getPrivileges(@RequestParam String groupCode,
-                                               @RequestParam boolean full) {
+                                               @RequestParam(required = false) boolean full) {
 
         User accessUser = getAccessUser();
         Long rootOrgId = accessUser.getRootOrgId();
@@ -137,7 +139,7 @@ public class RolePrivilegeController extends ControllerSupport {
             } else if (disabledPrivilegeCodeList.contains(cur.getCode())) {
                 hasPrivilege = false;
             }
-            if ((!full) && (!hasPrivilege)) {
+            if (!hasPrivilege) {
                 continue;
             }
 
@@ -473,38 +475,55 @@ public class RolePrivilegeController extends ControllerSupport {
     @PostMapping("updateRolePrivilegeRelations")
     @Transactional
     public void updateRolePrivilegeRelations(@RequestBody UpdateRolePrivilegeRelationsDomain req) {
+        this.validateRoles();
+
         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);
+        rolePrivilegeService.updateRolePrivilegeRelations(rootOrgId, roleId, privilegeGroupId, privilegeIdSet);
     }
 
     @ApiOperation(value = "更新顶级机构权限关联")
     @PostMapping("updateRootOrgPrivilegeRelations")
     @Transactional
-    public void updateRootOrgPrivilegeRelations(
-            @RequestBody UpdateRootOrgPrivilegeRelationsDomain req) {
+    public void updateRootOrgPrivilegeRelations(@RequestBody UpdateRootOrgPrivilegeRelationsDomain req) {
+        // this.validateRoles();
+
+    	User accessUser = getAccessUser();
         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", "非法请求");
+        boolean hasChange=hasChange(rootOrgId, privilegeGroupId, privilegeIdSet);
+        rolePrivilegeService.updateRootOrgPrivilegeRelations(rootOrgId, privilegeGroupId, privilegeIdSet);
+        if(hasChange) {
+        	ReportsUtil.report(new AdminOperateReport(accessUser.getRootOrgId(), accessUser.getUserId(), "考生端配置-保存考生端配置","修改自定义菜单"));
         }
+    }
+    
+    private boolean hasChange(Long rootOrgId,Long privilegeGroupId,Set<Long> privilegeIdSet) {
+    	List<RootOrgPrivilegeRelationEntity> list=rootOrgPrivilegeRelationRepo.findAllByRootOrgIdAndGroupId(rootOrgId, privilegeGroupId);
+    	if(list==null||list.size()==0) {
+    		return true;
+    	}
+    	if(list.size()!=privilegeIdSet.size()) {
+    		return true;
+    	}
+    	for(RootOrgPrivilegeRelationEntity ob:list) {
+    		if(!privilegeIdSet.contains(ob.getPrivilegeId())) {
+    			return true;
+    		}
+    	}
+    	return false;
+    }
 
-        rolePrivilegeService.updateRootOrgPrivilegeRelations(rootOrgId, privilegeGroupId,
-                privilegeIdSet);
+    private void validateRoles() {
+        User accessUser = getAccessUser();
+        if (!hasAnyRoles(accessUser, RoleMeta.SUPER_ADMIN, RoleMeta.ORG_ADMIN)) {
+            throw new StatusException("500403", "没有数据操作权限!");
+        }
     }
 
     @ApiOperation(value = "增加权限")

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

@@ -1,12 +1,15 @@
 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.enums.DataRuleType;
 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.bean.UserDataRule;
 import cn.com.qmth.examcloud.api.commons.security.bean.UserType;
-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.commons.util.ByteUtil;
+import cn.com.qmth.examcloud.commons.util.SHA256;
 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;
@@ -16,7 +19,6 @@ 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;
@@ -29,12 +31,10 @@ 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.security.DataRule;
 import cn.com.qmth.examcloud.web.support.ControllerSupport;
 import com.google.common.collect.Lists;
 import io.swagger.annotations.Api;
@@ -43,6 +43,7 @@ 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.PageImpl;
 import org.springframework.data.domain.PageRequest;
 import org.springframework.data.domain.Sort;
 import org.springframework.data.domain.Sort.Direction;
@@ -107,6 +108,7 @@ public class StudentController extends ControllerSupport {
      * @return
      * @author WANGWEI
      */
+    @DataRule(type = DataRuleType.ORG)
     @ApiOperation(value = "查询学生分页数据", notes = "分页")
     @GetMapping("studentPage/{curPage}/{pageSize}")
     public PageInfo<StudentDomain> getStudentPage(
@@ -124,9 +126,16 @@ public class StudentController extends ControllerSupport {
         }
 
         validateRootOrgIsolation(rootOrgId);
-
         final Long finalRootOrgId = rootOrgId;
 
+        PageRequest pageable = PageRequest.of(curPage, pageSize,
+                new Sort(Direction.DESC, "updateTime", "id"));
+
+        UserDataRule userDataRule = super.getUserDataRule(DataRuleType.ORG);
+        if (userDataRule.assertEmptyQueryResult()) {
+            return new PageInfo(new PageImpl<>(new ArrayList<>(), pageable, 0L));
+        }
+
         Specification<StudentEntity> specification = (root, query, cb) -> {
             List<Predicate> predicates = new ArrayList<>();
             predicates.add(cb.equal(root.get("rootOrgId"), finalRootOrgId));
@@ -134,6 +143,7 @@ public class StudentController extends ControllerSupport {
             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)));
             }
@@ -166,11 +176,20 @@ public class StudentController extends ControllerSupport {
                 predicates.add(cb.exists(subquery));
             }
 
-            Boolean orgStudentData = PrivilegeManager.judge(finalRootOrgId,
+
+            if (orgId == null) {
+                if (userDataRule.assertNeedQueryRefIds()) {
+                    predicates.add(root.get("orgId").in(userDataRule.getRefIds()));
+                }
+            } else {
+                predicates.add(cb.equal(root.get("orgId"), orgId));
+            }
+
+
+            /*Boolean orgStudentData = PrivilegeManager.judge(finalRootOrgId,
                     accessUser.getRoleList(), OrgStudentData.CODE);
             Boolean allStudentData = PrivilegeManager.judge(finalRootOrgId,
                     accessUser.getRoleList(), AllStudentData.CODE);
-
             // 学习中心过滤
             if (isSuperAdmin()) {
                 if (null != orgId) {
@@ -196,24 +215,19 @@ public class StudentController extends ControllerSupport {
                 } 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);
-
+        Page<StudentEntity> studentList = studentRepo.findAll(specification, pageable);
         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");
-//		}
+        //		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();
@@ -236,8 +250,8 @@ public class StudentController extends ControllerSupport {
             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(
+                //						UrlUtil.joinUrl(upyunDomain, "student_base_photo", next.getPhotoPath()));
                 //通用存储
                 bean.setPhotoPath(FileStorageUtil.realPath(FileStorageUtil.getIntactPath("student_base_photo",
                         next.getPhotoPath())));
@@ -265,6 +279,7 @@ public class StudentController extends ControllerSupport {
         return ret;
     }
 
+    @DataRule(type = DataRuleType.ORG)
     @ApiOperation(value = "导出学生", notes = "")
     @GetMapping("export")
     public void exportStudents(@RequestParam String name,
@@ -280,9 +295,14 @@ public class StudentController extends ControllerSupport {
         }
 
         validateRootOrgIsolation(rootOrgId);
-
         final Long finalRootOrgId = rootOrgId;
 
+        UserDataRule userDataRule = super.getUserDataRule(DataRuleType.ORG);
+        if (userDataRule.assertEmptyQueryResult()) {
+            this.doExport(new ArrayList<>());
+            return;
+        }
+
         Specification<StudentEntity> specification = (root, query, cb) -> {
             List<Predicate> predicates = new ArrayList<>();
             predicates.add(cb.equal(root.get("rootOrgId"), finalRootOrgId));
@@ -290,6 +310,7 @@ public class StudentController extends ControllerSupport {
             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)));
             }
@@ -315,11 +336,20 @@ public class StudentController extends ControllerSupport {
                 predicates.add(cb.exists(subquery));
             }
 
-            Boolean orgStudentData = PrivilegeManager.judge(finalRootOrgId,
+
+            if (orgId == null) {
+                if (userDataRule.assertNeedQueryRefIds()) {
+                    predicates.add(root.get("orgId").in(userDataRule.getRefIds()));
+                }
+            } else {
+                predicates.add(cb.equal(root.get("orgId"), orgId));
+            }
+
+
+            /*Boolean orgStudentData = PrivilegeManager.judge(finalRootOrgId,
                     accessUser.getRoleList(), OrgStudentData.CODE);
             Boolean allStudentData = PrivilegeManager.judge(finalRootOrgId,
                     accessUser.getRoleList(), AllStudentData.CODE);
-
             // 学习中心过滤
             if (isSuperAdmin()) {
                 if (null != orgId) {
@@ -345,7 +375,7 @@ public class StudentController extends ControllerSupport {
                 } else {
                     predicates.add(cb.equal(root.get("orgId"), -1));
                 }
-            }
+            }*/
 
             return cb.and(predicates.toArray(new Predicate[predicates.size()]));
         };
@@ -357,7 +387,10 @@ public class StudentController extends ControllerSupport {
 
         List<StudentEntity> studentList = studentRepo.findAll(specification,
                 new Sort(Direction.DESC, "updateTime"));
+        this.doExport(studentList);
+    }
 
+    private void doExport(List<StudentEntity> studentList) {
         List<Object[]> datas = Lists.newArrayList();
 
         for (StudentEntity cur : studentList) {
@@ -415,8 +448,8 @@ public class StudentController extends ControllerSupport {
             s.setEnable(true);
             studentRepo.save(s);
             //操作日志
-            ReportsUtil.report(new OperateReport(s.getRootOrgId(), getAccessUser().getUserId(),s.getId(), null, UserType.COMMON,
-            		OperateContent.STUDENT_ENABLE.getDesc()));
+            ReportsUtil.report(new OperateReport(s.getRootOrgId(), getAccessUser().getUserId(), s.getId(), null, UserType.COMMON,
+                    OperateContent.STUDENT_ENABLE.getDesc()));
             ret.add(s.getId() + ":" + s.getName());
         }
         for (Long cur : studentIds) {
@@ -444,8 +477,8 @@ public class StudentController extends ControllerSupport {
             s.setEnable(false);
             studentRepo.save(s);
             //操作日志
-            ReportsUtil.report(new OperateReport(s.getRootOrgId(), getAccessUser().getUserId(), s.getId(),null, UserType.COMMON,
-            		OperateContent.STUDENT_DISABLE.getDesc()));
+            ReportsUtil.report(new OperateReport(s.getRootOrgId(), getAccessUser().getUserId(), s.getId(), null, UserType.COMMON,
+                    OperateContent.STUDENT_DISABLE.getDesc()));
             ret.add(s.getId() + ":" + s.getName());
         }
         for (Long cur : studentIds) {
@@ -466,17 +499,21 @@ public class StudentController extends ControllerSupport {
                 throw new StatusException("450110", "学生不存在");
             }
             String identityNumber = s.getIdentityNumber();
+            String passwd = null;
             if (StringUtils.isNotEmpty(identityNumber)
                     && identityNumber.matches("[0-9a-zA-Z]{6,}")) {
-                s.setPassword(StringUtils.substring(identityNumber, -6, identityNumber.length()));
+                passwd = StringUtils.substring(identityNumber, -6, identityNumber.length());
             } else {
-                s.setPassword(BasicConsts.DEFAULT_PASSWORD);
+                passwd = BasicConsts.DEFAULT_PASSWORD;
             }
+            byte[] bytes = SHA256.encode(identityNumber + passwd);
+            String encodePassword = ByteUtil.toHexAscii(bytes);
+            s.setPassword(encodePassword);
             s.setUpdateTime(new Date());
             studentRepo.save(s);
             //操作日志
-            ReportsUtil.report(new OperateReport(s.getRootOrgId(), getAccessUser().getUserId(),s.getId(), null, UserType.COMMON,
-            		OperateContent.STUDENT_PASSWORD_RESET.getDesc()));
+            ReportsUtil.report(new OperateReport(s.getRootOrgId(), getAccessUser().getUserId(), s.getId(), null, UserType.COMMON,
+                    OperateContent.STUDENT_PASSWORD_RESET.getDesc()));
         }
     }
 
@@ -491,17 +528,21 @@ public class StudentController extends ControllerSupport {
         List<StudentEntity> stuentList = studentRepo.findByOrgId(org.getId());
         for (StudentEntity s : stuentList) {
             String identityNumber = s.getIdentityNumber();
+            String passwd = null;
             if (StringUtils.isNotEmpty(identityNumber)
                     && identityNumber.matches("[0-9a-zA-Z]{6,}")) {
-                s.setPassword(StringUtils.substring(identityNumber, -6, identityNumber.length()));
+                passwd = StringUtils.substring(identityNumber, -6, identityNumber.length());
             } else {
-                s.setPassword(BasicConsts.DEFAULT_PASSWORD);
+                passwd = BasicConsts.DEFAULT_PASSWORD;
             }
+            byte[] bytes = SHA256.encode(identityNumber + passwd);
+            String encodePassword = ByteUtil.toHexAscii(bytes);
+            s.setPassword(encodePassword);
             s.setUpdateTime(new Date());
             studentRepo.save(s);
             //操作日志
-            ReportsUtil.report(new OperateReport(s.getRootOrgId(), getAccessUser().getUserId(),s.getId(), null, UserType.COMMON,
-            		OperateContent.STUDENT_PASSWORD_RESET.getDesc()));
+            ReportsUtil.report(new OperateReport(s.getRootOrgId(), getAccessUser().getUserId(), s.getId(), null, UserType.COMMON,
+                    OperateContent.STUDENT_PASSWORD_RESET.getDesc()));
         }
     }
 
@@ -527,11 +568,13 @@ public class StudentController extends ControllerSupport {
 
         StudentEntity s = GlobalHelper.getEntity(studentRepo, accessUser.getUserId(),
                 StudentEntity.class);
-        s.setPassword(newPassword);
+        byte[] bytes = SHA256.encode(s.getIdentityNumber() + newPassword);
+        String encodePassword = ByteUtil.toHexAscii(bytes);
+        s.setPassword(encodePassword);
         studentRepo.save(s);
         //操作日志
-        ReportsUtil.report(new OperateReport(s.getRootOrgId(), getAccessUser().getUserId(), s.getId(),null, UserType.STUDENT,
-        		OperateContent.STUDENT_PASSWORD_UPDATE.getDesc()));
+        ReportsUtil.report(new OperateReport(s.getRootOrgId(), getAccessUser().getUserId(), s.getId(), null, UserType.STUDENT,
+                OperateContent.STUDENT_PASSWORD_UPDATE.getDesc()));
         return s.getId();
     }
 
@@ -562,14 +605,17 @@ public class StudentController extends ControllerSupport {
         StudentEntity s = GlobalHelper.getEntity(studentRepo, accessUser.getUserId(),
                 StudentEntity.class);
 
-        if (StringUtils.isNotBlank(s.getPassword()) && !s.getPassword().equals(password)) {
+        String encodeOldPassword = ByteUtil.toHexAscii(SHA256.encode(s.getIdentityNumber() + password));
+        if (StringUtils.isNotBlank(s.getPassword()) && !s.getPassword().equals(encodeOldPassword)) {
             throw new StatusException("450111", "当前密码错误");
         }
-        s.setPassword(newPassword);
+        byte[] bytes = SHA256.encode(s.getIdentityNumber() + newPassword);
+        String encodePassword = ByteUtil.toHexAscii(bytes);
+        s.setPassword(encodePassword);
         studentRepo.save(s);
         //操作日志
-        ReportsUtil.report(new OperateReport(s.getRootOrgId(), getAccessUser().getUserId(), s.getId(),null, UserType.STUDENT,
-        		OperateContent.STUDENT_PASSWORD_UPDATE.getDesc()));
+        ReportsUtil.report(new OperateReport(s.getRootOrgId(), getAccessUser().getUserId(), s.getId(), null, UserType.STUDENT,
+                OperateContent.STUDENT_PASSWORD_UPDATE.getDesc()));
         return s.getId();
     }
 
@@ -657,8 +703,8 @@ public class StudentController extends ControllerSupport {
         for (Long cur : studentIdList) {
             studentCache.remove(cur);
             //操作日志
-            ReportsUtil.report(new OperateReport(rootOrgId, getAccessUser().getUserId(),cur, null, UserType.COMMON,
-            		OperateContent.STUDENT_UNBIND_STUDENTCODE.getDesc()));
+            ReportsUtil.report(new OperateReport(rootOrgId, getAccessUser().getUserId(), cur, null, UserType.COMMON,
+                    OperateContent.STUDENT_UNBIND_STUDENTCODE.getDesc()));
         }
 
         return studentIdList;
@@ -682,8 +728,8 @@ public class StudentController extends ControllerSupport {
             studentService.unbindSecurityPhone(cur);
             StudentEntity s = GlobalHelper.getEntity(studentRepo, cur, StudentEntity.class);
             //操作日志
-            ReportsUtil.report(new OperateReport(s.getRootOrgId(), getAccessUser().getUserId(),s.getId(), null, UserType.COMMON,
-            		OperateContent.STUDENT_UNBIND_PHONE.getDesc()));
+            ReportsUtil.report(new OperateReport(s.getRootOrgId(), getAccessUser().getUserId(), s.getId(), null, UserType.COMMON,
+                    OperateContent.STUDENT_UNBIND_PHONE.getDesc()));
         }
 
         for (Long cur : studentIds) {
@@ -691,11 +737,11 @@ public class StudentController extends ControllerSupport {
         }
 
     }
-    
+
     @ApiOperation(value = "学生在线信号", notes = "学生在线信号")
     @GetMapping("online_signal/{studentId}")
     public void onlineSignal(@PathVariable Long studentId) {
-    	User accessUser = getAccessUser();
+        User accessUser = getAccessUser();
 
         //在线学生
         ReportsUtil.report(new OnlineStudentReport(accessUser.getRootOrgId(), studentId));

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

@@ -1,9 +1,12 @@
 package cn.com.qmth.examcloud.core.basic.api.controller;
 
+import cn.com.qmth.examcloud.api.commons.enums.DataRuleType;
 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.util.ByteUtil;
+import cn.com.qmth.examcloud.commons.util.SHA256;
 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;
@@ -15,7 +18,10 @@ 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.UserDataRuleService;
 import cn.com.qmth.examcloud.core.basic.service.UserService;
+import cn.com.qmth.examcloud.reports.commons.bean.AdminOperateReport;
+import cn.com.qmth.examcloud.reports.commons.util.ReportsUtil;
 import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
 import cn.com.qmth.examcloud.web.support.ControllerSupport;
 import com.google.common.collect.Lists;
@@ -65,6 +71,9 @@ public class UserController extends ControllerSupport {
     @Autowired
     UserRoleRelationRepo userRoleRelationRepo;
 
+    @Autowired
+    UserDataRuleService userDataRuleService;
+
     /**
      * 方法注释
      *
@@ -84,13 +93,18 @@ public class UserController extends ControllerSupport {
     public PageInfo<UserDomain> getUserPage(@PathVariable Integer curPage,
                                             @PathVariable Integer pageSize,
                                             @RequestParam(required = false) Long rootOrgId,
-                                            @RequestParam String loginName, @RequestParam String name,
+                                            @RequestParam(required = false) String name,
+                                            @RequestParam(required = false) String loginName,
                                             @RequestParam(required = false) Boolean enable,
-                                            @RequestParam(required = false) Long roleId, @RequestParam(required =
-            false) Long orgId,
-                                            @RequestParam(required = false) String roleCode) {
+                                            @RequestParam(required = false) Long roleId,
+                                            @RequestParam(required = false) Long orgId,
+                                            @RequestParam(required = false) String roleCode,
+                                            @RequestParam(required = false) String ignoreRoleCodes) {
 
         User accessUser = getAccessUser();
+        if (!hasAnyRoles(accessUser, RoleMeta.SUPER_ADMIN, RoleMeta.ORG_ADMIN, RoleMeta.DATA_ADMIN)) {
+            throw new StatusException("500403", "没有数据访问权限!");
+        }
 
         if (null == rootOrgId) {
             rootOrgId = accessUser.getRootOrgId();
@@ -127,6 +141,8 @@ public class UserController extends ControllerSupport {
 
         final Long finalRoleId = roleId;
 
+        final List<Long> ignoreRoleIds = this.queryRoleIdByCodes(rootOrgId, ignoreRoleCodes);
+
         Specification<UserEntity> specification = (root, query, cb) -> {
             List<Predicate> predicates = new ArrayList<>();
             predicates.add(cb.equal(root.get("rootOrgId"), finalRootOrgId));
@@ -153,6 +169,17 @@ public class UserController extends ControllerSupport {
                 subquery.where(cb.and(p1, p2));
                 predicates.add(cb.exists(subquery));
             }
+            if (CollectionUtils.isNotEmpty(ignoreRoleIds)) {
+                Subquery<UserRoleRelationEntity> subquery = query
+                        .subquery(UserRoleRelationEntity.class);
+                Root<UserRoleRelationEntity> subRoot = subquery.from(UserRoleRelationEntity.class);
+                subquery.select(subRoot.get("userId"));
+                Predicate p1 = subRoot.get("roleId").in(ignoreRoleIds);
+                Predicate p2 = cb.equal(subRoot.get("userId"), root.get("id"));
+                subquery.where(cb.and(p1, p2));
+                predicates.add(cb.not(cb.exists(subquery)));
+            }
+
             return cb.and(predicates.toArray(new Predicate[predicates.size()]));
         };
         Pageable pageable = PageRequest.of(curPage - 1, pageSize, Sort.Direction.DESC, "updateTime",
@@ -222,7 +249,8 @@ public class UserController extends ControllerSupport {
                                   @RequestParam(required = false) Boolean enable,
                                   @RequestParam(required = false) Long roleId,
                                   @RequestParam(required = false) Long orgId,
-                                  @RequestParam(required = false) String roleCode) {
+                                  @RequestParam(required = false) String roleCode,
+                                  @RequestParam(required = false) String ignoreRoleCodes) {
 
         OrgEntity rootOrg = null;
 
@@ -262,9 +290,10 @@ public class UserController extends ControllerSupport {
             }
             roleId = roleEntity.getId();
         }
-
         final Long finalRoleId = roleId;
 
+        final List<Long> ignoreRoleIds = this.queryRoleIdByCodes(rootOrgId, ignoreRoleCodes);
+
         Specification<UserEntity> specification = (root, query, cb) -> {
             List<Predicate> predicates = new ArrayList<>();
 
@@ -294,6 +323,18 @@ public class UserController extends ControllerSupport {
                 subquery.where(cb.and(p1, p2));
                 predicates.add(cb.exists(subquery));
             }
+
+            if (CollectionUtils.isNotEmpty(ignoreRoleIds)) {
+                Subquery<UserRoleRelationEntity> subquery = query
+                        .subquery(UserRoleRelationEntity.class);
+                Root<UserRoleRelationEntity> subRoot = subquery.from(UserRoleRelationEntity.class);
+                subquery.select(subRoot.get("userId"));
+                Predicate p1 = subRoot.get("roleId").in(ignoreRoleIds);
+                Predicate p2 = cb.equal(subRoot.get("userId"), root.get("id"));
+                subquery.where(cb.and(p1, p2));
+                predicates.add(cb.not(cb.exists(subquery)));
+            }
+
             return cb.and(predicates.toArray(new Predicate[predicates.size()]));
         };
 
@@ -346,14 +387,45 @@ public class UserController extends ControllerSupport {
 
             fullUserInfoList.add(bean);
         }
-
         return fullUserInfoList;
     }
 
+    private List<Long> queryRoleIdByCodes(Long rootOrgId, String roleCodes) {
+        if (StringUtils.isBlank(roleCodes)) {
+            return new ArrayList<>();
+        }
+
+        String[] codes = roleCodes.split(",");
+        if (codes.length == 0) {
+            return new ArrayList<>();
+        }
+
+        List<Long> roleIds = new ArrayList<>();
+
+        for (String roleCode : codes) {
+            if (StringUtils.isBlank(roleCode)) {
+                continue;
+            }
+
+            RoleEntity roleEntity = roleRepo.findByCodeAndRootOrgIdIsNull(roleCode.trim());
+            if (roleEntity == null) {
+                roleEntity = roleRepo.findByCodeAndRootOrgId(roleCode.trim(), rootOrgId);
+            }
+
+            if (roleEntity == null) {
+                log.warn("Role not exist, code is " + roleCode);
+                throw new StatusException("500404", "角色不存在");
+            }
+
+            roleIds.add(roleEntity.getId());
+        }
+
+        return roleIds;
+    }
+
     /**
      * 方法注释
      *
-     * @param id
      * @return
      * @author WANGWEI
      */
@@ -455,9 +527,17 @@ public class UserController extends ControllerSupport {
     @PostMapping
     @Transactional
     public Map<String, Object> addUser(@RequestBody UserFormDomain userForm) {
+        User accessUser = getAccessUser();
+        if (!hasAnyRoles(accessUser, RoleMeta.SUPER_ADMIN, RoleMeta.ORG_ADMIN, RoleMeta.DATA_ADMIN)) {
+            throw new StatusException("500403", "没有数据访问权限!");
+        }
+
         trim(userForm, true);
         userForm.setId(null);
-        return saveUser(userForm);
+        Map<String, Object> ret = saveUser(userForm);
+
+        ReportsUtil.report(new AdminOperateReport(accessUser.getRootOrgId(), accessUser.getUserId(), "普通用户管理-新增用户", "用户ID:" + ret.get("userId")));
+        return ret;
     }
 
     /**
@@ -471,6 +551,11 @@ public class UserController extends ControllerSupport {
     @PutMapping
     @Transactional
     public Map<String, Object> updateUser(@RequestBody UserFormDomain userForm) {
+        User accessUser = getAccessUser();
+        if (!hasAnyRoles(accessUser, RoleMeta.SUPER_ADMIN, RoleMeta.ORG_ADMIN, RoleMeta.DATA_ADMIN)) {
+            throw new StatusException("500403", "没有数据访问权限!");
+        }
+
         trim(userForm, true);
         if (null == userForm.getId()) {
             throw new StatusException("150009", "user ID is null");
@@ -487,8 +572,12 @@ public class UserController extends ControllerSupport {
         }
 
         userForm.setPassword(userEntity.getPassword());
+        userForm.setPasswordWeak(userEntity.getPasswordWeak());
+
+        Map<String, Object> ret = saveUser(userForm);
 
-        return saveUser(userForm);
+        ReportsUtil.report(new AdminOperateReport(accessUser.getRootOrgId(), accessUser.getUserId(), "普通用户管理-修改用户", "用户ID:" + ret.get("userId")));
+        return ret;
     }
 
     /**
@@ -539,6 +628,8 @@ public class UserController extends ControllerSupport {
             }
         }
 
+        boolean isCreateUser = false, isOrgAdmin = false;
+
         UserEntity userEntity = new UserEntity();
         userEntity.setId(userForm.getId());
         userEntity.setEnable(userForm.getEnable());
@@ -547,7 +638,13 @@ public class UserController extends ControllerSupport {
         userEntity.setName(userForm.getName());
         userEntity.setOrgId(orgId);
         userEntity.setRootOrgId(rootOrgId);
-        userEntity.setPassword(userForm.getPassword());
+        if (userForm.getId() == null) {
+            userEntity.setPassword(ByteUtil.toHexAscii(SHA256.encode(userForm.getLoginName() + userForm.getPassword())));
+            isCreateUser = true;
+        } else {
+            userEntity.setPassword(userForm.getPassword());
+        }
+        userEntity.setPasswordWeak(userForm.getPasswordWeak());
         userEntity.setUpdateTime(new Date());
 
         UserEntity saved = userService.save(userEntity);
@@ -575,14 +672,28 @@ public class UserController extends ControllerSupport {
                 }
             }
 
-            UserRoleRelationEntity relation = new UserRoleRelationEntity(saved.getId(),
-                    curRoleEntity.getId());
+            if (curRoleEntity.getCode().equals(RoleMeta.ORG_ADMIN.name())) {
+                isOrgAdmin = true;
+            }
+
+            UserRoleRelationEntity relation = new UserRoleRelationEntity(saved.getId(), curRoleEntity.getId());
             userRoles.add(relation);
         }
 
         userRoleRelationRepo.deleteByUserId(saved.getId());
         List<UserRoleRelationEntity> savedRelationList = userRoleRelationRepo.saveAll(userRoles);
 
+        if (saved.getOrgId() != null) {
+            // 用户与学习中心的数据权限
+            userDataRuleService.addUserDataRule(saved.getId(), DataRuleType.ORG, saved.getOrgId());
+        }
+
+        if (isCreateUser && isOrgAdmin) {
+            userDataRuleService.updateUserDataRuleForDefaultStatus(saved.getId(), DataRuleType.ORG, true);
+            userDataRuleService.updateUserDataRuleForDefaultStatus(saved.getId(), DataRuleType.EXAM, true);
+            userDataRuleService.updateUserDataRuleForDefaultStatus(saved.getId(), DataRuleType.COURSE, true);
+        }
+
         Map<String, Object> ret = Maps.newHashMap();
         ret.put("userId", saved.getId());
         ret.put("loginName", saved.getLoginName());
@@ -624,9 +735,11 @@ public class UserController extends ControllerSupport {
                 throw new StatusException("150410", "超级管理员账号不允许修改");
             }
             UserEntity user = GlobalHelper.getEntity(userRepo, userId, UserEntity.class);
-            user.setPassword(BasicConsts.DEFAULT_PASSWORD);
+            user.setPassword(ByteUtil.toHexAscii(SHA256.encode(user.getLoginName() + BasicConsts.DEFAULT_PASSWORD)));
             userRepo.save(user);
         }
+        User accessUser = getAccessUser();
+        ReportsUtil.report(new AdminOperateReport(accessUser.getRootOrgId(), accessUser.getUserId(), "普通用户管理-重置用户密码", "用户ID:" + id));
     }
 
     @ApiOperation(value = "启用用户", notes = "启用用户")
@@ -645,6 +758,8 @@ public class UserController extends ControllerSupport {
             userRepo.save(user);
             ret.add(user.getId() + ":" + user.getName());
         }
+        User accessUser = getAccessUser();
+        ReportsUtil.report(new AdminOperateReport(accessUser.getRootOrgId(), accessUser.getUserId(), "普通用户管理-启用用户", "用户ID:" + ids));
         return ret;
     }
 
@@ -664,22 +779,34 @@ public class UserController extends ControllerSupport {
             userRepo.save(user);
             ret.add(user.getId() + ":" + user.getName());
         }
+        User accessUser = getAccessUser();
+        ReportsUtil.report(new AdminOperateReport(accessUser.getRootOrgId(), accessUser.getUserId(), "普通用户管理-禁用用户", "用户ID:" + ids));
         return ret;
     }
 
     /**
      * 方法注释
      *
-     * @param userId
      * @param password
      * @author WANGWEI
      */
     @ApiOperation(value = "修改用户密码", notes = "修改密码")
     @PutMapping("/password")
     @Transactional
-    public void updatePass(@RequestParam long userId, @RequestParam String password) {
+    public void updatePass(@RequestParam String password) {
+        User accessUser = getAccessUser();
+        Long userId = accessUser.getUserId();
+        if (password.length() < 6) {
+            throw new StatusException("150411", "密码长度至少6位");
+        }
+        UserEntity user = GlobalHelper.getEntity(userRepo, userId, UserEntity.class);
         String realPassword = StringEscapeUtils.unescapeJavaScript(password);
-        userRepo.updatePasswordById(userId, realPassword);
+        byte[] bytes = SHA256.encode(user.getLoginName() + realPassword);
+        String encodePassword = ByteUtil.toHexAscii(bytes);
+        int ret = userRepo.updatePasswordById(userId, encodePassword);
+        if (ret == 0) {
+            throw new StatusException("150412", "新密码不可与原密码一致,请重新修改密码");
+        }
     }
 
     /**

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

@@ -0,0 +1,91 @@
+package cn.com.qmth.examcloud.core.basic.api.controller;
+
+import cn.com.qmth.examcloud.api.commons.enums.DataRuleType;
+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.service.UserDataRuleService;
+import cn.com.qmth.examcloud.core.basic.service.bean.datarule.*;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import cn.com.qmth.examcloud.web.support.Naked;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@Api(tags = "用户数据权限相关接口")
+@RequestMapping("${$rmp.ctr.basic}/user/data/rule")
+public class UserDataRuleController extends ControllerSupport {
+
+    @Autowired
+    private UserDataRuleService userDataRuleService;
+
+    @PostMapping("/default/status")
+    @ApiOperation(value = "获取用户数据权限的“默认全部”状态")
+    public boolean defaultStatus(@RequestParam Long userId, @RequestParam DataRuleType type) {
+        return userDataRuleService.getUserDataRuleForDefaultStatus(userId, type);
+    }
+
+    @PostMapping("/default/status/update")
+    @ApiOperation(value = "修改用户数据权限的“默认全部”状态")
+    public void updateDefaultStatus(@RequestParam Long userId, @RequestParam DataRuleType type, @RequestParam boolean enabled) {
+        this.validateRoles();
+        userDataRuleService.updateUserDataRuleForDefaultStatus(userId, type, enabled);
+    }
+
+    @PostMapping("/list/for/org")
+    @ApiOperation(value = "获取用户“学习中心”数据权限列表(分页)")
+    public Page<UserDataRuleForOrgInfo> listForOrg(@RequestBody UserDataRuleQuery req) {
+        return userDataRuleService.getUserDataRuleListForOrg(req);
+    }
+
+    @PostMapping("/list/for/exam")
+    @ApiOperation(value = "获取用户“考试”数据权限列表(分页)")
+    public Page<UserDataRuleForExamInfo> listForExam(@RequestBody UserDataRuleQuery req) {
+        return userDataRuleService.getUserDataRuleListForExam(req);
+    }
+
+    @PostMapping("/list/for/course")
+    @ApiOperation(value = "获取用户“课程”数据权限列表(分页)")
+    public Page<UserDataRuleForCourseInfo> listForCourse(@RequestBody UserDataRuleQuery req) {
+        return userDataRuleService.getUserDataRuleListForCourse(req);
+    }
+
+    @PostMapping("/add")
+    @ApiOperation(value = "新增用户数据权限")
+    public void add(@RequestBody UserDataRuleForm req) {
+        this.validateRoles();
+        userDataRuleService.addUserDataRules(req);
+    }
+
+    @PostMapping("/delete")
+    @ApiOperation(value = "删除用户数据权限")
+    public void delete(@RequestBody UserDataRuleForm req) {
+        this.validateRoles();
+        userDataRuleService.deleteUserDataRule(req);
+    }
+
+    @PostMapping("/copy")
+    @ApiOperation(value = "复制用户数据权限")
+    public void copy(@RequestBody UserDataRuleCopyReq req) {
+        this.validateRoles();
+        userDataRuleService.copyUserDataRule(req);
+    }
+
+    @Naked
+    @PostMapping("/check")
+    @ApiOperation(value = "验证用户数据权限")
+    public boolean checkUserDataRule(@RequestParam Long userId, @RequestParam DataRuleType type, @RequestParam Long refId) {
+        return userDataRuleService.checkUserDataRule(userId, type, refId);
+    }
+
+    private void validateRoles() {
+        User accessUser = getAccessUser();
+        if (!hasAnyRoles(accessUser, RoleMeta.SUPER_ADMIN, RoleMeta.ORG_ADMIN)) {
+            throw new StatusException("500403", "没有数据操作权限!");
+        }
+    }
+
+}

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

@@ -34,6 +34,11 @@ public class UserFormDomain implements JsonSerializable {
 	private Boolean enable;
 
 	private String password;
+	
+	/**
+	 * 密码虚弱
+	 */
+	private Boolean passwordWeak;
 
 	private List<Long> roleIds;
 
@@ -125,4 +130,13 @@ public class UserFormDomain implements JsonSerializable {
 		this.roleIds = roleIds;
 	}
 
+	public Boolean getPasswordWeak() {
+		return passwordWeak;
+	}
+
+	public void setPasswordWeak(Boolean passwordWeak) {
+		this.passwordWeak = passwordWeak;
+	}
+
+	
 }

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

@@ -16,7 +16,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 @RestController
-@Api(tags = "登录相关接口")
+@Api(tags = "RPC-登录相关接口")
 @RequestMapping("${$rmp.cloud.basic}" + "auth")
 public class AuthCloudServiceProvider extends ControllerSupport implements AuthCloudService {
 

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

@@ -38,7 +38,7 @@ import java.util.List;
  * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
  */
 @RestController
-@Api(tags = "课程相关接口")
+@Api(tags = "RPC-课程相关接口")
 @RequestMapping("${$rmp.cloud.basic}" + "course")
 public class CourseCloudServiceProvider implements CourseCloudService {
 

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

@@ -39,7 +39,7 @@ import java.util.List;
  * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
  */
 @RestController
-@Api(tags = "学生人脸相关接口")
+@Api(tags = "RPC-学生人脸相关接口")
 @RequestMapping("${$rmp.cloud.basic}" + "studentFace")
 public class FaceCloudServiceProvider extends ControllerSupport implements FaceCloudService {
 

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

@@ -47,7 +47,7 @@ import java.util.List;
  * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
  */
 @RestController
-@Api(tags = "机构相关接口")
+@Api(tags = "RPC-机构相关接口")
 @RequestMapping("${$rmp.cloud.basic}" + "org")
 public class OrgCloudServiceProvider extends ControllerSupport implements OrgCloudService {
 

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

@@ -39,7 +39,7 @@ import java.util.List;
  * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
  */
 @RestController
-@Api(tags = "角色权限相关接口")
+@Api(tags = "RPC-角色权限相关接口")
 @RequestMapping("${$rmp.cloud.basic}" + "rolePrivilege")
 public class RolePrivilegeCloudServiceProvider extends ControllerSupport
         implements

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

@@ -1,10 +1,41 @@
 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.collections.CollectionUtils;
+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 com.google.common.collect.Lists;
+
 import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.commons.util.ByteUtil;
+import cn.com.qmth.examcloud.commons.util.SHA256;
 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.*;
-import cn.com.qmth.examcloud.core.basic.api.response.*;
+import cn.com.qmth.examcloud.core.basic.api.request.CountStudentReq;
+import cn.com.qmth.examcloud.core.basic.api.request.GetStudentListByIdsReq;
+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.GetStudentListByIdsResp;
+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;
@@ -17,18 +48,6 @@ 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;
-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 javax.persistence.criteria.Predicate;
-import java.util.ArrayList;
-import java.util.List;
 
 /**
  * {@link StatusException} 状态码范围:056XXX<br>
@@ -38,7 +57,7 @@ import java.util.List;
  * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
  */
 @RestController
-@Api(tags = "学生相关接口")
+@Api(tags = "RPC-学生相关接口")
 @RequestMapping("${$rmp.cloud.basic}" + "student")
 public class StudentCloudServiceProvider extends ControllerSupport implements StudentCloudService {
 
@@ -192,7 +211,9 @@ public class StudentCloudServiceProvider extends ControllerSupport implements St
             throw new StatusException("056003", "no student");
         }
 
-        entity.setPassword(password);
+        byte[] bytes = SHA256.encode(entity.getIdentityNumber() + password);
+        String encodePassword = ByteUtil.toHexAscii(bytes);
+        entity.setPassword(encodePassword);
         studentRepo.save(entity);
 
         UpdatePasswordResp resp = new UpdatePasswordResp();
@@ -253,5 +274,45 @@ public class StudentCloudServiceProvider extends ControllerSupport implements St
         resp.setCount(count);
         return resp;
     }
+    
+    @ApiOperation(value = "根据ID获取学生集合")
+    @PostMapping("getStudentListByIds")
+    @Override
+    public GetStudentListByIdsResp getStudentListByIds(@RequestBody GetStudentListByIdsReq req) {
+        Long rootOrgId = req.getRootOrgId();
+        List<Long> studentIdList = req.getStudentIdList();
+        if (CollectionUtils.isEmpty(studentIdList)) {
+            throw new StatusException("500", "studentIdList is empty");
+        }
+        if (100 < studentIdList.size()) {
+            throw new StatusException("500", "size of studentIdList is too large");
+        }
+
+        List<StudentEntity> studentEntityList = studentRepo.findByIdIn(studentIdList);
+
+        List<StudentBean> list = Lists.newArrayList();
+        for (StudentEntity student : studentEntityList) {
+        	StudentBean studentBean = new StudentBean();
+            studentBean.setId(student.getId());
+            studentBean.setName(student.getName());
+            studentBean.setIdentityNumber(student.getIdentityNumber());
+            studentBean.setRootOrgId(student.getRootOrgId());
+            studentBean.setOrgId(student.getOrgId());
+            studentBean.setPhoneNumber(student.getPhoneNumber());
+            studentBean.setPhotoPath(student.getPhotoPath());
+            studentBean.setRemark(student.getRemark());
+            studentBean.setSecurityPhone(student.getSecurityPhone());
+
+            if (!studentBean.getRootOrgId().equals(rootOrgId)) {
+                throw new StatusException("500", "params error");
+            }
+            list.add(studentBean);
+        }
+
+        GetStudentListByIdsResp resp = new GetStudentListByIdsResp();
+        resp.setStudentBeanList(list);
+
+        return resp;
+    }
 
 }

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

@@ -28,7 +28,7 @@ import java.util.Date;
  * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
  */
 @RestController
-@Api(tags = "系统配置相关接口")
+@Api(tags = "RPC-系统配置相关接口")
 @RequestMapping("${$rmp.cloud.basic}" + "systemProperty")
 public class SystemPropertyCloudServiceProvider extends ControllerSupport
         implements

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

@@ -1,7 +1,10 @@
 package cn.com.qmth.examcloud.core.basic.api.provider;
 
+import cn.com.qmth.examcloud.api.commons.enums.DataRuleType;
 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.ByteUtil;
+import cn.com.qmth.examcloud.commons.util.SHA256;
 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;
@@ -22,6 +25,7 @@ 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.UserDataRuleService;
 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;
@@ -57,7 +61,7 @@ import java.util.List;
  * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
  */
 @RestController
-@Api(tags = "用户相关接口")
+@Api(tags = "RPC-用户相关接口")
 @RequestMapping("${$rmp.cloud.basic}" + "user")
 public class UserCloudServiceProvider extends ControllerSupport implements UserCloudService {
 
@@ -78,6 +82,9 @@ public class UserCloudServiceProvider extends ControllerSupport implements UserC
     @Autowired
     UserRoleRelationRepo userRoleRelationRepo;
 
+    @Autowired
+    UserDataRuleService userDataRuleService;
+
     @ApiOperation(value = "保存用户")
     @PostMapping("addUser")
     @Transactional
@@ -100,9 +107,9 @@ public class UserCloudServiceProvider extends ControllerSupport implements UserC
         userEntity.setRootOrgId(rootOrgId);
         userEntity.setName(name);
         if (StringUtils.isBlank(req.getPassword())) {
-            userEntity.setPassword(BasicConsts.DEFAULT_PASSWORD);
+            userEntity.setPassword(ByteUtil.toHexAscii(SHA256.encode(userEntity.getLoginName() + BasicConsts.DEFAULT_PASSWORD)));
         } else {
-            userEntity.setPassword(req.getPassword());
+            userEntity.setPassword(ByteUtil.toHexAscii(SHA256.encode(userEntity.getLoginName() + req.getPassword())));
         }
 
         UserEntity saved = userService.save(userEntity);
@@ -132,9 +139,14 @@ public class UserCloudServiceProvider extends ControllerSupport implements UserC
 
         userRoleRelationRepo.deleteByUserId(saved.getId());
         userRoleRelationRepo.saveAll(userRoles);
+
+        if (saved.getOrgId() != null) {
+            // 用户与学习中心的数据权限
+            userDataRuleService.addUserDataRule(saved.getId(), DataRuleType.ORG, saved.getOrgId());
+        }
+
         AddUserResp resp = new AddUserResp();
         resp.setUserId(saved.getId());
-
         return resp;
     }
 

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

@@ -0,0 +1,68 @@
+package cn.com.qmth.examcloud.core.basic.api.provider;
+
+import cn.com.qmth.examcloud.core.basic.api.UserDataRuleCloudService;
+import cn.com.qmth.examcloud.core.basic.api.request.AddUserDataRuleReq;
+import cn.com.qmth.examcloud.core.basic.api.request.CheckUserDataRuleReq;
+import cn.com.qmth.examcloud.core.basic.api.request.QueryUserDataRuleReq;
+import cn.com.qmth.examcloud.core.basic.api.response.AddUserDataRuleResp;
+import cn.com.qmth.examcloud.core.basic.api.response.CheckUserDataRuleResp;
+import cn.com.qmth.examcloud.core.basic.api.response.QueryUserDataRuleResp;
+import cn.com.qmth.examcloud.core.basic.service.UserDataRuleService;
+import cn.com.qmth.examcloud.core.basic.service.bean.datarule.UserDataRuleForm;
+import cn.com.qmth.examcloud.core.basic.service.cache.UserDataRuleCacheBean;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+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;
+
+@RestController
+@Api(tags = "RPC-用户数据权限相关接口")
+@RequestMapping("${$rmp.cloud.basic}/user/data/rule")
+public class UserDataRuleCloudServiceProvider extends ControllerSupport implements UserDataRuleCloudService {
+
+    private static final long serialVersionUID = 1L;
+
+    @Autowired
+    private UserDataRuleService userDataRuleService;
+
+    @Override
+    @PostMapping("/query")
+    @ApiOperation(value = "_查询用户的数据权限")
+    public QueryUserDataRuleResp queryUserDataRule(@RequestBody QueryUserDataRuleReq req) {
+        UserDataRuleCacheBean cacheBean = userDataRuleService.loadUserDataRuleCache(req.getUserId(), req.getType());
+
+        QueryUserDataRuleResp resp = new QueryUserDataRuleResp();
+        resp.setGlobalStatus(cacheBean.getGlobalStatus());
+        resp.setRefIds(cacheBean.getRefIds());
+        return resp;
+    }
+
+    @Override
+    @PostMapping("/check")
+    @ApiOperation(value = "_验证用户的数据权限")
+    public CheckUserDataRuleResp checkUserDataRule(@RequestBody CheckUserDataRuleReq req) {
+        boolean pass = userDataRuleService.checkUserDataRule(req.getUserId(), req.getType(), req.getRefId());
+
+        CheckUserDataRuleResp resp = new CheckUserDataRuleResp();
+        resp.setPass(pass);
+        return resp;
+    }
+
+    @Override
+    @PostMapping("/add")
+    @ApiOperation(value = "_新增用户数据权限")
+    public AddUserDataRuleResp addUserDataRule(@RequestBody AddUserDataRuleReq req) {
+        UserDataRuleForm form = new UserDataRuleForm();
+        form.setUserId(req.getUserId());
+        form.setType(req.getType());
+        form.setRefIds(req.getRefIds());
+
+        userDataRuleService.addUserDataRules(form);
+        return new AddUserDataRuleResp();
+    }
+
+}

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

@@ -0,0 +1,12 @@
+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.AdminOperateEntity;
+
+public interface AdminOperateRepo extends JpaRepository<AdminOperateEntity, Long>,
+		QueryByExampleExecutor<AdminOperateEntity>, JpaSpecificationExecutor<AdminOperateEntity> {
+
+}

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

@@ -1,25 +1,25 @@
 package cn.com.qmth.examcloud.core.basic.dao;
 
-import java.util.List;
-
+import cn.com.qmth.examcloud.core.basic.dao.entity.CourseEntity;
 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;
+import java.util.List;
+import java.util.Set;
+
+public interface CourseRepo extends JpaRepository<CourseEntity, Long>,
+        QueryByExampleExecutor<CourseEntity>,
+        JpaSpecificationExecutor<CourseEntity> {
 
-public interface CourseRepo
-		extends
-			JpaRepository<CourseEntity, Long>,
-			QueryByExampleExecutor<CourseEntity>,
-			JpaSpecificationExecutor<CourseEntity> {
+    List<CourseEntity> findByRootOrgId(Long rootOrgId);
 
-	List<CourseEntity> findByRootOrgId(Long rootOrgId);
+    CourseEntity findByRootOrgIdAndCode(Long rootOrgId, String code);
 
-	CourseEntity findByRootOrgIdAndCode(Long rootOrgId, String code);
+    List<CourseEntity> findByIdNotIn(List<Long> ids);
 
-	List<CourseEntity> findByIdNotIn(List<Long> ids);
+    int countByRootOrgIdAndNameLike(Long rootOrgId, String name);
 
-	int countByRootOrgIdAndNameLike(Long rootOrgId, String name);
+    List<CourseEntity> findByIdIn(Set<Long> courseIds);
 
 }

+ 5 - 1
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/OrgRepo.java

@@ -6,12 +6,14 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
 import org.springframework.data.repository.query.QueryByExampleExecutor;
 
 import java.util.List;
+import java.util.Set;
 
 /**
  * Created by songyue on 17/1/13.
  */
 public interface OrgRepo extends JpaRepository<OrgEntity, Long>,
-        QueryByExampleExecutor<OrgEntity>, JpaSpecificationExecutor<OrgEntity> {
+        QueryByExampleExecutor<OrgEntity>,
+        JpaSpecificationExecutor<OrgEntity> {
 
     List<OrgEntity> findByRootId(Long rootId);
 
@@ -29,4 +31,6 @@ public interface OrgRepo extends JpaRepository<OrgEntity, Long>,
 
     int countByRootIdAndParentId(Long rootId, long parentId);
 
+    List<OrgEntity> findByIdIn(Set<Long> orgIds);
+
 }

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

@@ -27,4 +27,6 @@ public interface StudentRepo
 
 	List<StudentEntity> findByOrgId(Long orgId);
 
+	List<StudentEntity> findByIdIn(List<Long> studentIdList);
+
 }

+ 34 - 0
examcloud-core-basic-dao/src/main/java/cn/com/qmth/examcloud/core/basic/dao/UserDataRuleRepo.java

@@ -0,0 +1,34 @@
+package cn.com.qmth.examcloud.core.basic.dao;
+
+import cn.com.qmth.examcloud.api.commons.enums.DataRuleType;
+import cn.com.qmth.examcloud.core.basic.dao.entity.UserDataRuleEntity;
+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.transaction.annotation.Transactional;
+
+import java.util.List;
+
+public interface UserDataRuleRepo extends JpaRepository<UserDataRuleEntity, Long>, JpaSpecificationExecutor<UserDataRuleEntity> {
+
+    boolean existsByUserIdAndTypeAndRefId(Long userId, DataRuleType type, Long refId);
+
+    UserDataRuleEntity findByUserIdAndTypeAndRefId(Long userId, DataRuleType type, Long refId);
+
+    List<UserDataRuleEntity> findByUserIdAndType(Long userId, DataRuleType type);
+
+    List<UserDataRuleEntity> findByUserId(Long userId);
+
+    @Transactional
+    @Modifying
+    @Query("delete from UserDataRuleEntity where userId = :userId")
+    int deleteByUserId(@Param("userId") Long userId);
+
+    @Transactional
+    @Modifying
+    @Query("delete from UserDataRuleEntity where userId = :userId and type = :type and refId in :refIds")
+    int deleteByUserIdAndTypeAndRefIds(@Param("userId") Long userId, @Param("type") DataRuleType type, @Param("refIds") List<Long> refIds);
+
+}

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

@@ -27,8 +27,8 @@ public interface UserRepo
 
 	@Transactional
 	@Modifying
-	@Query("update UserEntity set password = :password where id = :id")
-	void updatePasswordById(@Param("id") long id, @Param("password") String password);
+	@Query("update UserEntity set password = :password,password_weak=0 where id = :id and password!=:password")
+	int updatePasswordById(@Param("id") long id, @Param("password") String password);
 
 	int countByLoginName(String loginName);
 

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

@@ -0,0 +1,90 @@
+package cn.com.qmth.examcloud.core.basic.dao.entity;
+
+import java.util.Date;
+
+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 javax.validation.constraints.NotNull;
+
+import cn.com.qmth.examcloud.web.jpa.JpaEntity;
+
+/**
+ * @author chenken
+ * 管理端操作日志
+ */
+@Entity
+@Table(name = "ec_b_admin_operate", indexes = {
+		@Index(name = "IDX_R_ADMIN_OPERATE_01", columnList = "rootOrgId,operateUserId", unique = false),
+		@Index(name = "IDX_R_ADMIN_OPERATE_02", columnList = "rootOrgId,operateUserId,operateTime", unique = false),
+		@Index(name = "IDX_R_ADMIN_OPERATE_03", columnList = "msgId", unique = true)})
+public class AdminOperateEntity extends JpaEntity {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -1746292650336810597L;
+	@Id
+	@GeneratedValue(strategy = GenerationType.IDENTITY)
+	private Long id;
+	@NotNull
+	private Long rootOrgId;
+	@NotNull
+	private Long operateUserId;
+	@NotNull
+	private String operateIp;
+	@NotNull
+	private Date operateTime;
+	@NotNull
+	private String operate;
+	private String operateInfo;
+	@NotNull
+	private String msgId;
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+	public Long getOperateUserId() {
+		return operateUserId;
+	}
+	public void setOperateUserId(Long operateUserId) {
+		this.operateUserId = operateUserId;
+	}
+	public String getOperateIp() {
+		return operateIp;
+	}
+	public void setOperateIp(String operateIp) {
+		this.operateIp = operateIp;
+	}
+	public Date getOperateTime() {
+		return operateTime;
+	}
+	public void setOperateTime(Date operateTime) {
+		this.operateTime = operateTime;
+	}
+	public String getOperate() {
+		return operate;
+	}
+	public void setOperate(String operate) {
+		this.operate = operate;
+	}
+	public String getMsgId() {
+		return msgId;
+	}
+	public void setMsgId(String msgId) {
+		this.msgId = msgId;
+	}
+	public String getOperateInfo() {
+		return operateInfo;
+	}
+	public void setOperateInfo(String operateInfo) {
+		this.operateInfo = operateInfo;
+	}
+
+	
+	
+}

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

@@ -59,6 +59,12 @@ public class StudentEntity extends JpaEntity {
 	 */
 	@Column(unique = true, nullable = true)
 	private String securityPhone;
+	
+	/**
+	 * 密码虚弱
+	 */
+	@Column(nullable = false)
+	private Boolean passwordWeak;
 
 	public StudentEntity() {
 	}
@@ -155,4 +161,13 @@ public class StudentEntity extends JpaEntity {
 		this.securityPhone = securityPhone;
 	}
 
+	public Boolean getPasswordWeak() {
+		return passwordWeak;
+	}
+
+	public void setPasswordWeak(Boolean passwordWeak) {
+		this.passwordWeak = passwordWeak;
+	}
+
+	
 }

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

@@ -0,0 +1,103 @@
+package cn.com.qmth.examcloud.core.basic.dao.entity;
+
+import cn.com.qmth.examcloud.api.commons.enums.DataRuleType;
+import cn.com.qmth.examcloud.web.jpa.JpaEntity;
+
+import javax.persistence.*;
+
+/**
+ * 用户数据权限规则
+ */
+@Entity
+@Table(name = "ec_b_user_data_rule", indexes = {
+        @Index(name = "IDX_USER_DATA_RULE_01", columnList = "userId,type,refId", unique = true),
+})
+public class UserDataRuleEntity extends JpaEntity {
+
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    /**
+     * 顶级机构ID
+     */
+    @Column(nullable = false)
+    private Long rootOrgId;
+
+    /**
+     * 用户ID
+     */
+    @Column(nullable = false)
+    private Long userId;
+
+    /**
+     * 规则类型
+     */
+    @Enumerated(EnumType.STRING)
+    @Column(length = 50, nullable = false)
+    private DataRuleType type;
+
+    /**
+     * 关联ID
+     * 为-1时,代表“全部”
+     */
+    @Column(nullable = false)
+    private Long refId;
+
+    /**
+     * 是否启用
+     */
+    @Column(nullable = false)
+    private Boolean enabled;
+
+    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 getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public DataRuleType getType() {
+        return type;
+    }
+
+    public void setType(DataRuleType type) {
+        this.type = type;
+    }
+
+    public Long getRefId() {
+        return refId;
+    }
+
+    public void setRefId(Long refId) {
+        this.refId = refId;
+    }
+
+    public Boolean getEnabled() {
+        return enabled;
+    }
+
+    public void setEnabled(Boolean enabled) {
+        this.enabled = enabled;
+    }
+
+}

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

@@ -53,6 +53,12 @@ public class UserEntity extends JpaEntity {
 
 	@Column(nullable = false)
 	private Boolean enable;
+	
+	/**
+	 * 密码虚弱
+	 */
+	@Column(nullable = false)
+	private Boolean passwordWeak;
 
 	public Long getId() {
 		return id;
@@ -134,4 +140,13 @@ public class UserEntity extends JpaEntity {
 		this.enable = enable;
 	}
 
+	public Boolean getPasswordWeak() {
+		return passwordWeak;
+	}
+
+	public void setPasswordWeak(Boolean passwordWeak) {
+		this.passwordWeak = passwordWeak;
+	}
+
+	
 }

+ 14 - 0
examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/AdminOperateService.java

@@ -0,0 +1,14 @@
+package cn.com.qmth.examcloud.core.basic.service;
+
+import cn.com.qmth.examcloud.api.commons.exchange.PageInfo;
+import cn.com.qmth.examcloud.core.basic.service.bean.AdminOperateBean;
+import cn.com.qmth.examcloud.core.basic.service.bean.AdminOperateQuery;
+import cn.com.qmth.examcloud.reports.commons.bean.AdminOperateReport;
+
+public interface AdminOperateService {
+
+	public void saveOperate(AdminOperateReport r, String msgId);
+	
+	public PageInfo<AdminOperateBean> queryPage(AdminOperateQuery query,Integer pageNo,Integer pageSize,Long rootOrgId);
+
+}

+ 1 - 1
examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/OrgService.java

@@ -52,7 +52,7 @@ public interface OrgService {
      * @return
      * @author WANGWEI
      */
-    public List<Map<String, Object>> importSubOrg(Long rootOrgId, File file);
+    public List<Map<String, Object>> importSubOrg(Long rootOrgId, Long userId, File file);
 
     /**
      * 批量获取顶层机构名称

+ 70 - 0
examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/UserDataRuleService.java

@@ -0,0 +1,70 @@
+package cn.com.qmth.examcloud.core.basic.service;
+
+import cn.com.qmth.examcloud.api.commons.enums.DataRuleType;
+import cn.com.qmth.examcloud.core.basic.service.bean.datarule.*;
+import cn.com.qmth.examcloud.core.basic.service.cache.UserDataRuleCacheBean;
+import org.springframework.data.domain.Page;
+
+public interface UserDataRuleService {
+
+    /**
+     * 获取用户数据权限的“默认全部”状态
+     */
+    boolean getUserDataRuleForDefaultStatus(Long userId, DataRuleType type);
+
+    /**
+     * 修改用户数据权限的“默认全部”状态
+     */
+    void updateUserDataRuleForDefaultStatus(Long userId, DataRuleType type, boolean enabled);
+
+    /**
+     * 获取用户数据权限列表(分页)
+     */
+    Page<UserDataRuleInfo> getUserDataRuleList(UserDataRuleQuery req);
+
+    /**
+     * 获取用户“学习中心”数据权限列表(分页)
+     */
+    Page<UserDataRuleForOrgInfo> getUserDataRuleListForOrg(UserDataRuleQuery req);
+
+    /**
+     * 获取用户“考试”数据权限列表(分页)
+     */
+    Page<UserDataRuleForExamInfo> getUserDataRuleListForExam(UserDataRuleQuery req);
+
+    /**
+     * 获取用户“课程”数据权限列表(分页)
+     */
+    Page<UserDataRuleForCourseInfo> getUserDataRuleListForCourse(UserDataRuleQuery req);
+
+    /**
+     * 新增用户数据权限
+     */
+    void addUserDataRules(UserDataRuleForm req);
+
+    /**
+     * 新增用户数据权限
+     */
+    void addUserDataRule(Long userId, DataRuleType type, Long refId);
+
+    /**
+     * 删除用户数据权限
+     */
+    void deleteUserDataRule(UserDataRuleForm req);
+
+    /**
+     * 复制用户数据权限
+     */
+    void copyUserDataRule(UserDataRuleCopyReq req);
+
+    /**
+     * 验证用户的数据权限
+     */
+    boolean checkUserDataRule(Long userId, DataRuleType type, Long refId);
+
+    /**
+     * 获取用户的数据权限(缓存)
+     */
+    UserDataRuleCacheBean loadUserDataRuleCache(Long userId, DataRuleType type);
+
+}

+ 6 - 3
examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/UserService.java

@@ -2,14 +2,17 @@ package cn.com.qmth.examcloud.core.basic.service;
 
 import cn.com.qmth.examcloud.core.basic.dao.entity.UserEntity;
 
+import java.util.Set;
+
 /**
  * 用户服务
- * 
- * @author WANGWEI
  *
+ * @author WANGWEI
  */
 public interface UserService {
 
-	UserEntity save(UserEntity user);
+    UserEntity save(UserEntity user);
+
+    Set<String> getUserRoleCodes(Long userId);
 
 }

+ 100 - 0
examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/bean/AdminOperateBean.java

@@ -0,0 +1,100 @@
+package cn.com.qmth.examcloud.core.basic.service.bean;
+
+import java.util.Date;
+
+import io.swagger.annotations.ApiModelProperty;
+
+public class AdminOperateBean {
+	
+	
+	@ApiModelProperty(value = "operateUserId")
+	private Long operateUserId;
+	
+	@ApiModelProperty(value = "loginName")
+	private String loginName;
+	
+	@ApiModelProperty(value = "roles")
+	private String roles;
+	
+	@ApiModelProperty(value = "name")
+	private String name;
+
+	@ApiModelProperty(value = "操作时间")
+	private Date operateTime;
+	
+	@ApiModelProperty(value = "ip")
+	private String operateIp;
+	
+	@ApiModelProperty(value = "操作内容")
+	private String operate;
+	
+	@ApiModelProperty(value = "操作详情")
+	private String operateInfo;
+
+	public Long getOperateUserId() {
+		return operateUserId;
+	}
+
+	public void setOperateUserId(Long operateUserId) {
+		this.operateUserId = operateUserId;
+	}
+
+	public String getLoginName() {
+		return loginName;
+	}
+
+	public void setLoginName(String loginName) {
+		this.loginName = loginName;
+	}
+
+	public String getRoles() {
+		return roles;
+	}
+
+	public void setRoles(String roles) {
+		this.roles = roles;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public Date getOperateTime() {
+		return operateTime;
+	}
+
+	public void setOperateTime(Date operateTime) {
+		this.operateTime = operateTime;
+	}
+
+	public String getOperateIp() {
+		return operateIp;
+	}
+
+	public void setOperateIp(String operateIp) {
+		this.operateIp = operateIp;
+	}
+
+	public String getOperate() {
+		return operate;
+	}
+
+	public void setOperate(String operate) {
+		this.operate = operate;
+	}
+
+	public String getOperateInfo() {
+		return operateInfo;
+	}
+
+	public void setOperateInfo(String operateInfo) {
+		this.operateInfo = operateInfo;
+	}
+
+	
+	
+}

+ 42 - 0
examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/bean/AdminOperateQuery.java

@@ -0,0 +1,42 @@
+package cn.com.qmth.examcloud.core.basic.service.bean;
+
+import io.swagger.annotations.ApiModelProperty;
+
+public class AdminOperateQuery {
+
+
+    @ApiModelProperty(value = "loginName")
+	private String loginName;
+    @ApiModelProperty(value = "roleId")
+	private Long roleId;
+    @ApiModelProperty(value = "startTime")
+	private String startTime;
+    @ApiModelProperty(value = "endTime")
+	private String endTime;
+	public String getLoginName() {
+		return loginName;
+	}
+	public void setLoginName(String loginName) {
+		this.loginName = loginName;
+	}
+	public Long getRoleId() {
+		return roleId;
+	}
+	public void setRoleId(Long roleId) {
+		this.roleId = roleId;
+	}
+	public String getStartTime() {
+		return startTime;
+	}
+	public void setStartTime(String startTime) {
+		this.startTime = startTime;
+	}
+	public String getEndTime() {
+		return endTime;
+	}
+	public void setEndTime(String endTime) {
+		this.endTime = endTime;
+	}
+
+	
+}

+ 34 - 0
examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/bean/datarule/UserDataRuleCopyReq.java

@@ -0,0 +1,34 @@
+package cn.com.qmth.examcloud.core.basic.service.bean.datarule;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.List;
+
+public class UserDataRuleCopyReq implements JsonSerializable {
+
+    private static final long serialVersionUID = 4853280464208226631L;
+
+    @ApiModelProperty(value = "源用户ID")
+    private Long sourceUserId;
+
+    @ApiModelProperty(value = "目标用户ID")
+    private List<Long> targetUserIds;
+
+    public Long getSourceUserId() {
+        return sourceUserId;
+    }
+
+    public void setSourceUserId(Long sourceUserId) {
+        this.sourceUserId = sourceUserId;
+    }
+
+    public List<Long> getTargetUserIds() {
+        return targetUserIds;
+    }
+
+    public void setTargetUserIds(List<Long> targetUserIds) {
+        this.targetUserIds = targetUserIds;
+    }
+
+}

+ 67 - 0
examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/bean/datarule/UserDataRuleForCourseInfo.java

@@ -0,0 +1,67 @@
+package cn.com.qmth.examcloud.core.basic.service.bean.datarule;
+
+import io.swagger.annotations.ApiModelProperty;
+
+public class UserDataRuleForCourseInfo extends UserDataRuleInfo {
+
+    private static final long serialVersionUID = 4853280464208226631L;
+
+    @ApiModelProperty(value = "课程ID")
+    private Long courseId;
+
+    @ApiModelProperty(value = "课程编码")
+    private String courseCode;
+
+    @ApiModelProperty(value = "课程名称")
+    private String courseName;
+
+    @ApiModelProperty(value = "课程层次")
+    private String courseLevel;
+
+    public UserDataRuleForCourseInfo(UserDataRuleInfo info) {
+        super.id = info.getId();
+        super.userId = info.getUserId();
+        super.type = info.getType();
+        super.refId = info.getRefId();
+        super.creationTime = info.getCreationTime();
+        super.updateTime = info.getUpdateTime();
+        this.courseId = info.getRefId();
+    }
+
+    public UserDataRuleForCourseInfo() {
+
+    }
+
+    public Long getCourseId() {
+        return courseId;
+    }
+
+    public void setCourseId(Long courseId) {
+        this.courseId = courseId;
+    }
+
+    public String getCourseCode() {
+        return courseCode;
+    }
+
+    public void setCourseCode(String courseCode) {
+        this.courseCode = courseCode;
+    }
+
+    public String getCourseName() {
+        return courseName;
+    }
+
+    public void setCourseName(String courseName) {
+        this.courseName = courseName;
+    }
+
+    public String getCourseLevel() {
+        return courseLevel;
+    }
+
+    public void setCourseLevel(String courseLevel) {
+        this.courseLevel = courseLevel;
+    }
+
+}

+ 67 - 0
examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/bean/datarule/UserDataRuleForExamInfo.java

@@ -0,0 +1,67 @@
+package cn.com.qmth.examcloud.core.basic.service.bean.datarule;
+
+import io.swagger.annotations.ApiModelProperty;
+
+public class UserDataRuleForExamInfo extends UserDataRuleInfo {
+
+    private static final long serialVersionUID = 4853280464208226631L;
+
+    @ApiModelProperty(value = "考试ID")
+    private Long examId;
+
+    @ApiModelProperty(value = "考试编码")
+    private String examCode;
+
+    @ApiModelProperty(value = "考试名称")
+    private String examName;
+
+    @ApiModelProperty(value = "考试类型")
+    private String examType;
+
+    public UserDataRuleForExamInfo(UserDataRuleInfo info) {
+        super.id = info.getId();
+        super.userId = info.getUserId();
+        super.type = info.getType();
+        super.refId = info.getRefId();
+        super.creationTime = info.getCreationTime();
+        super.updateTime = info.getUpdateTime();
+        this.examId = info.getRefId();
+    }
+
+    public UserDataRuleForExamInfo() {
+
+    }
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public String getExamCode() {
+        return examCode;
+    }
+
+    public void setExamCode(String examCode) {
+        this.examCode = examCode;
+    }
+
+    public String getExamName() {
+        return examName;
+    }
+
+    public void setExamName(String examName) {
+        this.examName = examName;
+    }
+
+    public String getExamType() {
+        return examType;
+    }
+
+    public void setExamType(String examType) {
+        this.examType = examType;
+    }
+
+}

+ 67 - 0
examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/bean/datarule/UserDataRuleForOrgInfo.java

@@ -0,0 +1,67 @@
+package cn.com.qmth.examcloud.core.basic.service.bean.datarule;
+
+import io.swagger.annotations.ApiModelProperty;
+
+public class UserDataRuleForOrgInfo extends UserDataRuleInfo {
+
+    private static final long serialVersionUID = 4853280464208226631L;
+
+    @ApiModelProperty(value = "学习中心ID")
+    private Long orgId;
+
+    @ApiModelProperty(value = "学习中心编码")
+    private String orgCode;
+
+    @ApiModelProperty(value = "学习中心名称")
+    private String orgName;
+
+    @ApiModelProperty(value = "负责人")
+    private String contacts;
+
+    public UserDataRuleForOrgInfo(UserDataRuleInfo info) {
+        super.id = info.getId();
+        super.userId = info.getUserId();
+        super.type = info.getType();
+        super.refId = info.getRefId();
+        super.creationTime = info.getCreationTime();
+        super.updateTime = info.getUpdateTime();
+        this.orgId = info.getRefId();
+    }
+
+    public UserDataRuleForOrgInfo() {
+
+    }
+
+    public Long getOrgId() {
+        return orgId;
+    }
+
+    public void setOrgId(Long orgId) {
+        this.orgId = orgId;
+    }
+
+    public String getOrgCode() {
+        return orgCode;
+    }
+
+    public void setOrgCode(String orgCode) {
+        this.orgCode = orgCode;
+    }
+
+    public String getOrgName() {
+        return orgName;
+    }
+
+    public void setOrgName(String orgName) {
+        this.orgName = orgName;
+    }
+
+    public String getContacts() {
+        return contacts;
+    }
+
+    public void setContacts(String contacts) {
+        this.contacts = contacts;
+    }
+
+}

+ 56 - 0
examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/bean/datarule/UserDataRuleForm.java

@@ -0,0 +1,56 @@
+package cn.com.qmth.examcloud.core.basic.service.bean.datarule;
+
+import cn.com.qmth.examcloud.api.commons.enums.DataRuleType;
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class UserDataRuleForm implements JsonSerializable {
+
+    private static final long serialVersionUID = 4853280464208226631L;
+
+    @ApiModelProperty(value = "用户ID")
+    private Long userId;
+
+    @ApiModelProperty("权限类型")
+    private DataRuleType type;
+
+    @ApiModelProperty(value = "关联ID")
+    private List<Long> refIds;
+
+    public UserDataRuleForm addRefId(Long refId) {
+        if (this.refIds == null) {
+            this.refIds = new ArrayList<>();
+        }
+
+        this.refIds.add(refId);
+        return this;
+    }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public DataRuleType getType() {
+        return type;
+    }
+
+    public void setType(DataRuleType type) {
+        this.type = type;
+    }
+
+    public List<Long> getRefIds() {
+        return refIds;
+    }
+
+    public void setRefIds(List<Long> refIds) {
+        this.refIds = refIds;
+    }
+
+}

+ 79 - 0
examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/bean/datarule/UserDataRuleInfo.java

@@ -0,0 +1,79 @@
+package cn.com.qmth.examcloud.core.basic.service.bean.datarule;
+
+import cn.com.qmth.examcloud.api.commons.enums.DataRuleType;
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.Date;
+
+public class UserDataRuleInfo implements JsonSerializable {
+
+    private static final long serialVersionUID = 4853280464208226631L;
+
+    @ApiModelProperty(value = "ID")
+    protected Long id;
+
+    @ApiModelProperty(value = "用户ID")
+    protected Long userId;
+
+    @ApiModelProperty("权限类型")
+    protected DataRuleType type;
+
+    @ApiModelProperty(value = "关联ID")
+    protected Long refId;
+
+    @ApiModelProperty(value = "创建时间")
+    protected Date creationTime;
+
+    @ApiModelProperty(value = "更新时间")
+    protected Date updateTime;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public DataRuleType getType() {
+        return type;
+    }
+
+    public void setType(DataRuleType type) {
+        this.type = type;
+    }
+
+    public Long getRefId() {
+        return refId;
+    }
+
+    public void setRefId(Long refId) {
+        this.refId = refId;
+    }
+
+    public Date getCreationTime() {
+        return creationTime;
+    }
+
+    public void setCreationTime(Date creationTime) {
+        this.creationTime = creationTime;
+    }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+
+}

+ 74 - 0
examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/bean/datarule/UserDataRuleQuery.java

@@ -0,0 +1,74 @@
+package cn.com.qmth.examcloud.core.basic.service.bean.datarule;
+
+import cn.com.qmth.examcloud.api.commons.enums.DataRuleType;
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import io.swagger.annotations.ApiModelProperty;
+
+public class UserDataRuleQuery implements JsonSerializable {
+
+    private static final long serialVersionUID = 4853280464208226631L;
+
+    @ApiModelProperty("当前页数")
+    private Integer pageNo;
+
+    @ApiModelProperty("每页条数")
+    private Integer pageSize;
+
+    @ApiModelProperty(value = "用户ID")
+    private Long userId;
+
+    @ApiModelProperty(value = "权限类型", hidden = true)
+    private DataRuleType type;
+
+    @ApiModelProperty(value = "关联ID")
+    private Long refId;
+
+    public Integer getPageNo() {
+        if (pageNo == null || pageNo < 1) {
+            // 默认值
+            pageNo = 1;
+        }
+        return pageNo;
+    }
+
+    public void setPageNo(Integer pageNo) {
+        this.pageNo = pageNo;
+    }
+
+    public Integer getPageSize() {
+        if (pageSize == null || pageSize < 1) {
+            // 默认值
+            pageSize = 10;
+        }
+        return pageSize;
+    }
+
+    public void setPageSize(Integer pageSize) {
+        this.pageSize = pageSize;
+    }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public DataRuleType getType() {
+        return type;
+    }
+
+    public void setType(DataRuleType type) {
+        this.type = type;
+    }
+
+    public Long getRefId() {
+        return refId;
+    }
+
+    public void setRefId(Long refId) {
+        this.refId = refId;
+    }
+
+}

+ 0 - 27
examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/cache/LoginRuleCacheBean.java

@@ -1,27 +0,0 @@
-package cn.com.qmth.examcloud.core.basic.service.cache;
-
-import java.io.Serializable;
-import java.util.Map;
-
-/**
- * 登录规则缓存对象
- */
-public class LoginRuleCacheBean implements Serializable {
-
-    private static final long serialVersionUID = 1L;
-
-    /**
-     * 是否开放列表(白名单)
-     * Map<rootOrgId, isAllow>
-     */
-    private Map<Long, Boolean> allowList;
-
-    public Map<Long, Boolean> getAllowList() {
-        return allowList;
-    }
-
-    public void setAllowList(Map<Long, Boolean> allowList) {
-        this.allowList = allowList;
-    }
-
-}

+ 39 - 0
examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/cache/UserDataRuleCacheBean.java

@@ -0,0 +1,39 @@
+package cn.com.qmth.examcloud.core.basic.service.cache;
+
+import java.io.Serializable;
+import java.util.Set;
+
+/**
+ * 用户数据权限缓存对象
+ */
+public class UserDataRuleCacheBean implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * “默认全部”状态
+     */
+    private Boolean globalStatus;
+
+    /**
+     * 关联ID列表
+     */
+    private Set<Long> refIds;
+
+    public Boolean getGlobalStatus() {
+        return globalStatus;
+    }
+
+    public void setGlobalStatus(Boolean globalStatus) {
+        this.globalStatus = globalStatus;
+    }
+
+    public Set<Long> getRefIds() {
+        return refIds;
+    }
+
+    public void setRefIds(Set<Long> refIds) {
+        this.refIds = refIds;
+    }
+
+}

+ 130 - 0
examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/impl/AdminOperateServiceImpl.java

@@ -0,0 +1,130 @@
+package cn.com.qmth.examcloud.core.basic.service.impl;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DuplicateKeyException;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Service;
+
+import com.google.common.collect.Lists;
+
+import cn.com.qmth.examcloud.api.commons.exchange.PageInfo;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.basic.dao.AdminOperateRepo;
+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.AdminOperateEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.RoleEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.UserRoleRelationEntity;
+import cn.com.qmth.examcloud.core.basic.service.AdminOperateService;
+import cn.com.qmth.examcloud.core.basic.service.bean.AdminOperateBean;
+import cn.com.qmth.examcloud.core.basic.service.bean.AdminOperateQuery;
+import cn.com.qmth.examcloud.reports.commons.bean.AdminOperateReport;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+
+@Service
+public class AdminOperateServiceImpl implements AdminOperateService {
+
+	@Autowired
+	private JdbcTemplate jdbcTemplate;
+	@Autowired
+	UserRepo userRepo;
+
+	@Autowired
+	OrgRepo orgRepo;
+
+	@Autowired
+	RoleRepo roleRepo;
+
+	@Autowired
+	UserRoleRelationRepo userRoleRelationRepo;
+	@Autowired
+	AdminOperateRepo adminOperateRepo;
+
+	@Override
+	public void saveOperate(AdminOperateReport r, String msgId) {
+		AdminOperateEntity e = new AdminOperateEntity();
+		e.setMsgId(msgId);
+		e.setRootOrgId(r.getRootOrgId());
+		e.setOperateUserId(r.getOperateUserId());
+		e.setOperateTime(r.getReportTime());
+		e.setOperateIp(r.getRemoteHost());
+		e.setOperate(r.getOperate());
+		e.setOperateInfo(r.getOperateInfo());
+		e.setCreationTime(new Date());
+		try {
+			adminOperateRepo.save(e);
+		} catch (DuplicateKeyException e1) {
+			// 忽略;
+		}
+	}
+
+	@Override
+	public PageInfo<AdminOperateBean> queryPage(AdminOperateQuery req, Integer pageNo, Integer pageSize,
+			Long rootOrgId) {
+		Long count = jdbcTemplate.queryForObject(" select count(1) from (" + getSql(req, rootOrgId) + ") tem", Long.class);
+		List<AdminOperateBean> list = new ArrayList<AdminOperateBean>();
+		if (count > 0) {
+			RowMapper<AdminOperateBean> rowMapper = new BeanPropertyRowMapper<AdminOperateBean>(AdminOperateBean.class);
+			list = jdbcTemplate.query(
+					getSql(req, rootOrgId) + " order by t.id desc limit " + ((pageNo - 1) * pageSize) + "," + pageSize,
+					rowMapper);
+			for (AdminOperateBean bean:list) {
+				List<UserRoleRelationEntity> relationList = userRoleRelationRepo.findAllByUserId(bean.getOperateUserId());
+				List<String> roleNameList = 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());
+				}
+				bean.setRoles(StringUtils.join(roleNameList, ","));
+			}
+		}
+		PageInfo<AdminOperateBean> ret = new PageInfo<AdminOperateBean>();
+		ret.setList(list);
+		ret.setTotal(count);
+		return ret;
+	}
+
+	private String getSql(AdminOperateQuery req, Long rootOrgId) {
+		String loginName = req.getLoginName();
+		String startTime = req.getStartTime();
+		String endTime = req.getEndTime();
+		Long roleId = req.getRoleId();
+
+		StringBuilder sb = new StringBuilder();
+		sb.append(
+				" select t.operate_time operateTime,t.operate_ip operateIp,t.operate_user_id operateUserId ");
+		sb.append(" ,t.operate,t.operate_info operateInfo,f.login_name loginName,f.name from ec_b_admin_operate t ");
+		sb.append(" inner join ec_b_user f on t.operate_user_id=f.id ");
+		sb.append(" where 1=1  ");
+		if (rootOrgId != null) {
+			sb.append(" and t.root_org_id=").append(rootOrgId);
+		}
+		if (StringUtils.isNotBlank(loginName)) {
+			sb.append(" and f.login_name like '").append(loginName).append("%'  ");
+		}
+		if (StringUtils.isNotBlank(endTime)) {
+			sb.append(" and t.operate_time <=STR_TO_DATE('").append(endTime).append("','%Y-%m-%d %H:%i:%s') ");
+		}
+		if (StringUtils.isNotBlank(startTime)) {
+			sb.append(" and t.operate_time >=STR_TO_DATE('").append(startTime).append("','%Y-%m-%d %H:%i:%s') ");
+		}
+		if (roleId != null) {
+			sb.append(" and EXISTS (select 1 from ec_b_user_role_relation h where h.role_id=").append(roleId)
+					.append(" and h.user_id=t.operate_user_id)");
+		}
+		return sb.toString();
+	}
+
+}

+ 8 - 3
examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/impl/AuthServiceImpl.java

@@ -193,6 +193,7 @@ public class AuthServiceImpl implements AuthService {
                 throw new StatusException("003203", "账号或密码错误");
             }
 
+            user.setPasswordWeak(userEntity.getPasswordWeak());
             user.setUserId(userEntity.getId());
             user.setUserType(UserType.COMMON);
             user.setDisplayName(userEntity.getLoginName() + " (" + userEntity.getName() + ")");
@@ -217,7 +218,9 @@ public class AuthServiceImpl implements AuthService {
                         throw new StatusException("003506", "系统维护中... ...");
                     }
                 }
-                if (!password.equals(userEntity.getPassword())) {
+                byte[] bytes = SHA256.encode(userEntity.getLoginName() + password);
+                String encodePassword = ByteUtil.toHexAscii(bytes);
+                if (!encodePassword.equals(userEntity.getPassword())) {
                     whenLoginError(accountTypeEnum, accountValue, clientIp);
                     throw new StatusException("003003", "账号或密码错误");
                 }
@@ -270,8 +273,9 @@ public class AuthServiceImpl implements AuthService {
                 if (StringUtils.isBlank(password)) {
                     throw new StatusException("003203", "账号或密码错误");
                 }
-                String rightPassword = student.getPassword();
-                if (!rightPassword.equals(password)) {
+                byte[] bytes = SHA256.encode(student.getIdentityNumber() + password);
+                String encodePassword = ByteUtil.toHexAscii(bytes);
+                if (!encodePassword.equals(student.getPassword())) {
                     whenLoginError(accountTypeEnum, accountValue, clientIp);
                     throw new StatusException("003003", "账号或密码错误");
                 }
@@ -280,6 +284,7 @@ public class AuthServiceImpl implements AuthService {
             user.setUserId(student.getId());
             user.setUserType(UserType.STUDENT);
             user.setDisplayName(student.getName());
+            user.setPasswordWeak(student.getPasswordWeak());
             orgId = student.getOrgId();
 
             List<Role> roleList = Lists.newArrayList();

+ 2 - 4
examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/impl/LoginRuleServiceImpl.java

@@ -171,8 +171,6 @@ public class LoginRuleServiceImpl implements LoginRuleService {
     }
 
     private void refreshLoginRule(LoginRuleType type) {
-        log.info("refreshLoginRule start...");
-
         List<LoginRuleEntity> entities = loginRuleRepo.findByType(type);
         Map<String, Boolean> values = new HashMap<>();
         if (CollectionUtils.isNotEmpty(entities)) {
@@ -189,7 +187,7 @@ public class LoginRuleServiceImpl implements LoginRuleService {
             this.uploadJsonFile(json);
         }
 
-        log.info("refreshLoginRule end...");
+        log.info("refreshLoginRule " + type.name());
     }
 
     private void uploadJsonFile(String json) {
@@ -198,7 +196,7 @@ public class LoginRuleServiceImpl implements LoginRuleService {
             FileStoragePathEnvInfo env = new FileStoragePathEnvInfo();
             env.setExt1("geetestConfig");
             YunPathInfo oss = FileStorageUtil.saveFile(LOGIN_RULE_SITE_ID, env, json.getBytes(), true);
-            log.info(oss.getRelativePath());
+            log.debug(oss.getRelativePath());
         } catch (Exception e) {
             log.error(e.getMessage(), e);
         }

+ 33 - 7
examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/impl/OrgServiceImpl.java

@@ -1,17 +1,22 @@
 package cn.com.qmth.examcloud.core.basic.service.impl;
 
+import cn.com.qmth.examcloud.api.commons.enums.DataRuleType;
 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.DynamicEnum;
 import cn.com.qmth.examcloud.commons.helpers.DynamicEnumManager;
 import cn.com.qmth.examcloud.commons.helpers.poi.ExcelReader;
+import cn.com.qmth.examcloud.commons.util.ByteUtil;
 import cn.com.qmth.examcloud.commons.util.PathUtil;
+import cn.com.qmth.examcloud.commons.util.SHA256;
 import cn.com.qmth.examcloud.core.basic.base.constants.BasicConsts;
 import cn.com.qmth.examcloud.core.basic.dao.*;
 import cn.com.qmth.examcloud.core.basic.dao.entity.*;
 import cn.com.qmth.examcloud.core.basic.dao.enums.OrgProperty;
 import cn.com.qmth.examcloud.core.basic.service.OrgService;
+import cn.com.qmth.examcloud.core.basic.service.UserDataRuleService;
 import cn.com.qmth.examcloud.core.basic.service.bean.OrgInfo;
+import cn.com.qmth.examcloud.core.basic.service.bean.datarule.UserDataRuleForm;
 import cn.com.qmth.examcloud.core.basic.service.cache.OrgCache;
 import cn.com.qmth.examcloud.core.basic.service.cache.OrgPropertyCache;
 import cn.com.qmth.examcloud.core.basic.service.cache.RootOrgCache;
@@ -65,6 +70,9 @@ public class OrgServiceImpl implements OrgService {
     @Autowired
     OrgPropertyCache orgPropertyCache;
 
+    @Autowired
+    UserDataRuleService userDataRuleService;
+
     private static final String[] EXCEL_HEADER = new String[]{"机构名称", "机构代码", "联系人", "联系电话"};
 
     /*
@@ -327,7 +335,7 @@ public class OrgServiceImpl implements OrgService {
     }
 
     @Override
-    public List<Map<String, Object>> importSubOrg(Long rootOrgId, File file) {
+    public List<Map<String, Object>> importSubOrg(Long rootOrgId, Long userId, File file) {
 
         List<String[]> lineList = null;
         try {
@@ -347,7 +355,7 @@ public class OrgServiceImpl implements OrgService {
         List<Map<String, Object>> failRecords = Collections
                 .synchronizedList(new ArrayList<Map<String, Object>>());
 
-        List<OrgEntity> courseList = Lists.newArrayList();
+        List<OrgEntity> orgList = Lists.newArrayList();
 
         for (int i = 0; i < lineList.size(); i++) {
             String[] line = lineList.get(i);
@@ -408,7 +416,7 @@ public class OrgServiceImpl implements OrgService {
             if (hasError) {
                 failRecords.add(newError(i + 1, msg.toString()));
             } else {
-                courseList.add(org);
+                orgList.add(org);
             }
 
         }
@@ -417,7 +425,8 @@ public class OrgServiceImpl implements OrgService {
             return failRecords;
         }
 
-        for (OrgEntity cur : courseList) {
+        List<Long> orgIds = new ArrayList<>();
+        for (OrgEntity cur : orgList) {
             OrgInfo info = new OrgInfo();
             info.setCode(cur.getCode());
             info.setParentId(rootOrgId);
@@ -429,9 +438,18 @@ public class OrgServiceImpl implements OrgService {
 
             OrgEntity saved = saveSubOrg(info);
 
+            orgIds.add(saved.getId());
+
             createLearnerCenterUser(saved);
         }
 
+        // 用户与学习中心的数据权限
+        UserDataRuleForm form = new UserDataRuleForm();
+        form.setUserId(userId);
+        form.setType(DataRuleType.ORG);
+        form.setRefIds(orgIds);
+        userDataRuleService.addUserDataRules(form);
+
         return failRecords;
     }
 
@@ -457,6 +475,7 @@ public class OrgServiceImpl implements OrgService {
         if (null != user) {
             return;
         }
+
         user = new UserEntity();
         user.setRootOrgId(org.getRootId());
         user.setOrgId(org.getId());
@@ -464,14 +483,21 @@ public class OrgServiceImpl implements OrgService {
         user.setName(org.getContacts());
         user.setPhoneNumber(org.getTelephone());
         user.setEnable(true);
-        user.setPassword(BasicConsts.DEFAULT_PASSWORD);
+        user.setPasswordWeak(true);
+        user.setPassword(ByteUtil.toHexAscii(SHA256.encode(user.getLoginName() + BasicConsts.DEFAULT_PASSWORD)));
         UserEntity savedUser = userRepo.save(user);
+
         List<UserRoleRelationEntity> userRoles = Lists.newArrayList();
         RoleEntity role = roleRepo.findByCode(RoleMeta.LC_USER.name());
-        UserRoleRelationEntity relation = new UserRoleRelationEntity(savedUser.getId(),
-                role.getId());
+
+        UserRoleRelationEntity relation = new UserRoleRelationEntity(savedUser.getId(), role.getId());
         userRoles.add(relation);
         userRoleRelationRepo.saveAll(userRoles);
+
+        if (savedUser.getOrgId() != null) {
+            // 用户与学习中心的数据权限
+            userDataRuleService.addUserDataRule(savedUser.getId(), DataRuleType.ORG, savedUser.getOrgId());
+        }
     }
 
     private Map<String, Object> newError(int lineNum, String msg) {

+ 13 - 6
examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/impl/StudentServiceImpl.java

@@ -14,6 +14,8 @@ import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 
 import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.commons.util.ByteUtil;
+import cn.com.qmth.examcloud.commons.util.SHA256;
 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;
@@ -149,13 +151,18 @@ public class StudentServiceImpl implements StudentService {
 			}
 		} else {
 			student = new StudentEntity();
+			student.setPasswordWeak(true);
 			student.setEnable(true);
-			if (identityNumber.matches("[0-9a-zA-Z]{6,}")) {
-				student.setPassword(
-						StringUtils.substring(identityNumber, -6, identityNumber.length()));
-			} else {
-				student.setPassword(BasicConsts.DEFAULT_PASSWORD);
-			}
+			String passwd=null;
+            if (StringUtils.isNotEmpty(identityNumber)
+                    && identityNumber.matches("[0-9a-zA-Z]{6,}")) {
+            	passwd=StringUtils.substring(identityNumber, -6, identityNumber.length());
+            } else {
+            	passwd=BasicConsts.DEFAULT_PASSWORD;
+            }
+            byte[] bytes = SHA256.encode(identityNumber + passwd);
+	        String encodePassword = ByteUtil.toHexAscii(bytes);
+	        student.setPassword(encodePassword);
 
 			student.setRootOrgId(rootOrgId);
 			student.setIdentityNumber(identityNumber);

+ 455 - 0
examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/impl/UserDataRuleServiceImpl.java

@@ -0,0 +1,455 @@
+package cn.com.qmth.examcloud.core.basic.service.impl;
+
+import cn.com.qmth.examcloud.api.commons.enums.DataRuleType;
+import cn.com.qmth.examcloud.api.commons.security.enums.RoleMeta;
+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.core.basic.dao.CourseRepo;
+import cn.com.qmth.examcloud.core.basic.dao.OrgRepo;
+import cn.com.qmth.examcloud.core.basic.dao.UserDataRuleRepo;
+import cn.com.qmth.examcloud.core.basic.dao.UserRepo;
+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.dao.entity.UserDataRuleEntity;
+import cn.com.qmth.examcloud.core.basic.dao.entity.UserEntity;
+import cn.com.qmth.examcloud.core.basic.service.UserDataRuleService;
+import cn.com.qmth.examcloud.core.basic.service.UserService;
+import cn.com.qmth.examcloud.core.basic.service.bean.datarule.*;
+import cn.com.qmth.examcloud.core.basic.service.cache.UserDataRuleCacheBean;
+import cn.com.qmth.examcloud.examwork.api.ExamCloudService;
+import cn.com.qmth.examcloud.examwork.api.bean.ExamBean;
+import cn.com.qmth.examcloud.examwork.api.request.GetExamMapsReq;
+import cn.com.qmth.examcloud.examwork.api.response.GetExamMapsResp;
+import cn.com.qmth.examcloud.web.redis.RedisClient;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.*;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.stereotype.Service;
+
+import javax.persistence.criteria.Predicate;
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Service
+public class UserDataRuleServiceImpl implements UserDataRuleService {
+
+    private static final ExamCloudLog log = ExamCloudLogFactory.getLog("INTERFACE_LOGGER");
+
+    private static final String DATA_RULE_CACHE_KEY = "$DATA_RULE:%s_%s";//“数据权限”的缓存KEY规则
+
+    private static final int DATA_RULE_CACHE_TIMEOUT = 600;// 过期时间(秒)
+
+    private static final long DEFAULT_ID = -1L;// 为-1时,代表“全部”
+
+    @Autowired
+    private UserDataRuleRepo userDataRuleRepo;
+
+    @Autowired
+    private UserRepo userRepo;
+
+    @Autowired
+    private OrgRepo orgRepo;
+
+    @Autowired
+    private CourseRepo courseRepo;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private ExamCloudService examCloudService;
+
+    @Autowired
+    private RedisClient redisClient;
+
+    @Override
+    public boolean getUserDataRuleForDefaultStatus(Long userId, DataRuleType type) {
+        if (userId == null) {
+            throw new StatusException("400400", "用户ID不能为空!");
+        }
+
+        if (type == null) {
+            throw new StatusException("400400", "权限类型不能为空!");
+        }
+
+        UserDataRuleEntity userDataRule = userDataRuleRepo.findByUserIdAndTypeAndRefId(userId, type, DEFAULT_ID);
+        if (userDataRule != null) {
+            return userDataRule.getEnabled();
+        }
+
+        // 不存在时,默认false
+        return false;
+    }
+
+    @Override
+    public void updateUserDataRuleForDefaultStatus(Long userId, DataRuleType type, boolean enabled) {
+        if (userId == null) {
+            throw new StatusException("400400", "用户ID不能为空!");
+        }
+
+        if (type == null) {
+            throw new StatusException("400400", "权限类型不能为空!");
+        }
+
+        UserDataRuleEntity entity = userDataRuleRepo.findByUserIdAndTypeAndRefId(userId, type, DEFAULT_ID);
+        if (entity == null) {
+            Optional<UserEntity> user = userRepo.findById(userId);
+            if (!user.isPresent()) {
+                throw new StatusException("400400", "此用户不存在!");
+            }
+
+            entity = new UserDataRuleEntity();
+            entity.setRootOrgId(user.get().getRootOrgId());
+            entity.setUserId(userId);
+            entity.setType(type);
+            entity.setRefId(DEFAULT_ID);
+            entity.setEnabled(enabled);
+        } else {
+            entity.setEnabled(enabled);
+        }
+
+        userDataRuleRepo.save(entity);
+
+        // 清理缓存
+        this.clearUserDataRuleCache(userId, type);
+    }
+
+    @Override
+    public Page<UserDataRuleInfo> getUserDataRuleList(UserDataRuleQuery req) {
+        if (req.getUserId() == null) {
+            throw new StatusException("400400", "用户ID不能为空!");
+        }
+
+        Specification<UserDataRuleEntity> spec = (root, query, cb) -> {
+            List<Predicate> predicates = new ArrayList<>();
+            predicates.add(cb.equal(root.get("userId"), req.getUserId()));
+
+            if (req.getType() != null) {
+                predicates.add(cb.equal(root.get("type"), req.getType()));
+            }
+
+            if (req.getRefId() != null) {
+                predicates.add(cb.equal(root.get("refId"), req.getRefId()));
+            } else {
+                predicates.add(cb.notEqual(root.get("refId"), DEFAULT_ID));
+            }
+
+            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+        };
+
+        Sort sort = Sort.by(Sort.Order.desc("id"));
+        Pageable pageable = PageRequest.of(req.getPageNo() - 1, req.getPageSize(), sort);
+        Page<UserDataRuleEntity> page = userDataRuleRepo.findAll(spec, pageable);
+
+        if (!page.hasContent()) {
+            return new PageImpl<>(new ArrayList<>(), pageable, 0);
+        }
+
+        List<UserDataRuleInfo> list = page.getContent().stream().map(entity -> {
+            UserDataRuleInfo info = new UserDataRuleInfo();
+            info.setId(entity.getId());
+            info.setUserId(entity.getUserId());
+            info.setType(entity.getType());
+            info.setRefId(entity.getRefId());
+            info.setCreationTime(entity.getCreationTime());
+            info.setUpdateTime(entity.getUpdateTime());
+            return info;
+        }).collect(Collectors.toList());
+
+        return new PageImpl<>(list, pageable, page.getTotalElements());
+    }
+
+    @Override
+    public void addUserDataRules(UserDataRuleForm req) {
+        if (req.getUserId() == null) {
+            throw new StatusException("400400", "用户ID不能为空!");
+        }
+
+        if (req.getType() == null) {
+            throw new StatusException("400400", "权限类型不能为空!");
+        }
+
+        if (CollectionUtils.isEmpty(req.getRefIds())) {
+            throw new StatusException("400400", "关联ID不能为空!");
+        }
+
+        Optional<UserEntity> user = userRepo.findById(req.getUserId());
+        if (!user.isPresent()) {
+            throw new StatusException("400400", "此用户不存在!");
+        }
+
+        for (Long refId : req.getRefIds()) {
+            boolean existed = userDataRuleRepo.existsByUserIdAndTypeAndRefId(req.getUserId(), req.getType(), refId);
+            if (existed) {
+                continue;
+            }
+
+            UserDataRuleEntity entity = new UserDataRuleEntity();
+            entity.setRootOrgId(user.get().getRootOrgId());
+            entity.setUserId(req.getUserId());
+            entity.setType(req.getType());
+            entity.setRefId(refId);
+            entity.setEnabled(true);
+            userDataRuleRepo.save(entity);
+        }
+
+        // 清理缓存
+        this.clearUserDataRuleCache(req.getUserId(), req.getType());
+    }
+
+    @Override
+    public void addUserDataRule(Long userId, DataRuleType type, Long refId) {
+        if (refId == null) {
+            throw new StatusException("400400", "关联ID不能为空!");
+        }
+
+        UserDataRuleForm req = new UserDataRuleForm();
+        req.setUserId(userId);
+        req.setType(type);
+        req.addRefId(refId);
+
+        this.addUserDataRules(req);
+    }
+
+    @Override
+    public void deleteUserDataRule(UserDataRuleForm req) {
+        if (req.getUserId() == null) {
+            throw new StatusException("400400", "用户ID不能为空!");
+        }
+
+        if (req.getType() == null) {
+            throw new StatusException("400400", "权限类型不能为空!");
+        }
+
+        if (CollectionUtils.isEmpty(req.getRefIds())) {
+            throw new StatusException("400400", "关联ID不能为空!");
+        }
+
+        userDataRuleRepo.deleteByUserIdAndTypeAndRefIds(req.getUserId(), req.getType(), req.getRefIds());
+
+        // 清理缓存
+        this.clearUserDataRuleCache(req.getUserId(), req.getType());
+    }
+
+    @Override
+    public void copyUserDataRule(UserDataRuleCopyReq req) {
+        if (req.getSourceUserId() == null) {
+            throw new StatusException("400400", "源用户ID不能为空!");
+        }
+
+        if (CollectionUtils.isEmpty(req.getTargetUserIds())) {
+            throw new StatusException("400400", "目标用户ID不能为空!");
+        }
+
+        Optional<UserEntity> sourceUser = userRepo.findById(req.getSourceUserId());
+        if (!sourceUser.isPresent()) {
+            throw new StatusException("400400", "源用户不存在!");
+        }
+
+        Set<String> sourceRoleCodes = userService.getUserRoleCodes(req.getSourceUserId());
+        if (sourceRoleCodes.contains(RoleMeta.SUPER_ADMIN.name()) || sourceRoleCodes.contains(RoleMeta.ORG_ADMIN.name())) {
+            throw new StatusException("400400", "超管或机构管理员的数据权限不可复制!");
+        }
+
+        Map<Long, Long> targetUserMaps = new HashMap<>();
+        for (Long targetUserId : req.getTargetUserIds()) {
+            if (targetUserId.equals(req.getSourceUserId())) {
+                log.warn(String.format("跳过复制,targetUserId与sourceUserId相同... id = %s", targetUserId));
+                continue;
+            }
+
+            Optional<UserEntity> targetUser = userRepo.findById(targetUserId);
+            if (!targetUser.isPresent()) {
+                log.warn(String.format("User is not exist... targetUserId = %s", targetUserId));
+                continue;
+            }
+
+            Set<String> targetRoleCodes = userService.getUserRoleCodes(targetUserId);
+            if (targetRoleCodes.contains(RoleMeta.SUPER_ADMIN.name()) || targetRoleCodes.contains(RoleMeta.ORG_ADMIN.name())) {
+                throw new StatusException("400400", "超管、机构管理员的数据权限不可复制!");
+            }
+
+            targetUserMaps.put(targetUserId, targetUser.get().getRootOrgId());
+        }
+
+        if (targetUserMaps.isEmpty()) {
+            return;
+        }
+
+        List<UserDataRuleEntity> sourceUserRules = userDataRuleRepo.findByUserId(req.getSourceUserId());
+
+        for (Map.Entry<Long, Long> targetUser : targetUserMaps.entrySet()) {
+            userDataRuleRepo.deleteByUserId(targetUser.getKey());
+
+            for (UserDataRuleEntity sourceUserRule : sourceUserRules) {
+                UserDataRuleEntity copyRule = new UserDataRuleEntity();
+                copyRule.setRootOrgId(targetUser.getValue());
+                copyRule.setUserId(targetUser.getKey());
+                copyRule.setType(sourceUserRule.getType());
+                copyRule.setRefId(sourceUserRule.getRefId());
+                copyRule.setEnabled(sourceUserRule.getEnabled());
+                userDataRuleRepo.save(copyRule);
+            }
+
+            for (DataRuleType type : DataRuleType.values()) {
+                // 清理缓存
+                this.clearUserDataRuleCache(targetUser.getKey(), type);
+            }
+        }
+    }
+
+    @Override
+    public Page<UserDataRuleForOrgInfo> getUserDataRuleListForOrg(UserDataRuleQuery req) {
+        req.setType(DataRuleType.ORG);
+
+        Page<UserDataRuleInfo> page = this.getUserDataRuleList(req);
+        if (!page.hasContent()) {
+            return new PageImpl<>(new ArrayList<>(), page.getPageable(), page.getTotalElements());
+        }
+
+        Set<Long> orgIds = page.getContent().stream().map(UserDataRuleInfo::getRefId).collect(Collectors.toSet());
+        List<OrgEntity> orgList = orgRepo.findByIdIn(orgIds);
+        Map<Long, OrgEntity> orgMaps = orgList.stream().collect(Collectors.toMap(OrgEntity::getId, v -> v, (k, v) -> v));
+
+        List<UserDataRuleForOrgInfo> list = new ArrayList<>();
+        for (UserDataRuleInfo info : page.getContent()) {
+            UserDataRuleForOrgInfo allInfo = new UserDataRuleForOrgInfo(info);
+
+            OrgEntity org = orgMaps.get(info.getRefId());
+            if (org != null) {
+                allInfo.setOrgCode(org.getCode());
+                allInfo.setOrgName(org.getName());
+                allInfo.setContacts(org.getContacts());
+            }
+
+            list.add(allInfo);
+        }
+
+        return new PageImpl<>(list, page.getPageable(), page.getTotalElements());
+    }
+
+    @Override
+    public Page<UserDataRuleForExamInfo> getUserDataRuleListForExam(UserDataRuleQuery req) {
+        req.setType(DataRuleType.EXAM);
+
+        Page<UserDataRuleInfo> page = this.getUserDataRuleList(req);
+        if (!page.hasContent()) {
+            return new PageImpl<>(new ArrayList<>(), page.getPageable(), page.getTotalElements());
+        }
+
+        Set<Long> examIds = page.getContent().stream().map(UserDataRuleInfo::getRefId).collect(Collectors.toSet());
+        GetExamMapsReq examsReq = new GetExamMapsReq();
+        examsReq.setExamIds(examIds);
+        GetExamMapsResp examsResp = examCloudService.getExamMaps(examsReq);
+        Map<Long, ExamBean> examMaps = examsResp.getExamMaps();
+
+        List<UserDataRuleForExamInfo> list = new ArrayList<>();
+        for (UserDataRuleInfo info : page.getContent()) {
+            UserDataRuleForExamInfo allInfo = new UserDataRuleForExamInfo(info);
+
+            ExamBean exam = examMaps.get(info.getRefId());
+            if (exam != null) {
+                allInfo.setExamCode(exam.getCode());
+                allInfo.setExamName(exam.getName());
+                allInfo.setExamType(exam.getExamType());
+            }
+
+            list.add(allInfo);
+        }
+
+        return new PageImpl<>(list, page.getPageable(), page.getTotalElements());
+    }
+
+    @Override
+    public Page<UserDataRuleForCourseInfo> getUserDataRuleListForCourse(UserDataRuleQuery req) {
+        req.setType(DataRuleType.COURSE);
+
+        Page<UserDataRuleInfo> page = this.getUserDataRuleList(req);
+        if (!page.hasContent()) {
+            return new PageImpl<>(new ArrayList<>(), page.getPageable(), page.getTotalElements());
+        }
+
+        Set<Long> courseIds = page.getContent().stream().map(UserDataRuleInfo::getRefId).collect(Collectors.toSet());
+        List<CourseEntity> courseList = courseRepo.findByIdIn(courseIds);
+        Map<Long, CourseEntity> courseMaps = courseList.stream().collect(Collectors.toMap(CourseEntity::getId, v -> v, (k, v) -> v));
+
+        List<UserDataRuleForCourseInfo> list = new ArrayList<>();
+        for (UserDataRuleInfo info : page.getContent()) {
+            UserDataRuleForCourseInfo allInfo = new UserDataRuleForCourseInfo(info);
+
+            CourseEntity course = courseMaps.get(info.getRefId());
+            if (course != null) {
+                allInfo.setCourseCode(course.getCode());
+                allInfo.setCourseName(course.getName());
+                allInfo.setCourseLevel(course.getLevel().getName());
+            }
+
+            list.add(allInfo);
+        }
+
+        return new PageImpl<>(list, page.getPageable(), page.getTotalElements());
+    }
+
+    @Override
+    public boolean checkUserDataRule(Long userId, DataRuleType type, Long refId) {
+        if (refId == null) {
+            throw new StatusException("400400", "关联ID不能为空!");
+        }
+
+        UserDataRuleCacheBean cacheBean = this.loadUserDataRuleCache(userId, type);
+
+        if (cacheBean.getGlobalStatus()) {
+            return cacheBean.getGlobalStatus();
+        }
+
+        return cacheBean.getRefIds().contains(refId);
+    }
+
+    @Override
+    public UserDataRuleCacheBean loadUserDataRuleCache(Long userId, DataRuleType type) {
+        if (userId == null) {
+            throw new StatusException("400400", "用户ID不能为空!");
+        }
+
+        if (type == null) {
+            throw new StatusException("400400", "权限类型不能为空!");
+        }
+
+        String cacheKey = String.format(DATA_RULE_CACHE_KEY, userId, type.name());
+        UserDataRuleCacheBean cacheBean = redisClient.get(cacheKey, UserDataRuleCacheBean.class);
+        if (cacheBean != null) {
+            return cacheBean;
+        }
+
+        Set<Long> refIds = new HashSet<>();
+        cacheBean = new UserDataRuleCacheBean();
+        cacheBean.setGlobalStatus(false);
+        cacheBean.setRefIds(refIds);
+
+        List<UserDataRuleEntity> rules = userDataRuleRepo.findByUserIdAndType(userId, type);
+        for (UserDataRuleEntity rule : rules) {
+            if (rule.getRefId().equals(DEFAULT_ID)) {
+                cacheBean.setGlobalStatus(rule.getEnabled());
+                continue;
+            }
+            refIds.add(rule.getRefId());
+        }
+
+        if (log.isInfoEnabled()) {
+            log.info(String.format("userId = %s, dataRuleType = %s, globalStatus = %s, refIds = [%s]",
+                    userId, type.name(), cacheBean.getGlobalStatus(), StringUtils.join(refIds, ",")));
+        }
+
+        redisClient.set(cacheKey, cacheBean, DATA_RULE_CACHE_TIMEOUT);
+        return cacheBean;
+    }
+
+    private void clearUserDataRuleCache(Long userId, DataRuleType type) {
+        String cacheKey = String.format(DATA_RULE_CACHE_KEY, userId, type.name());
+        redisClient.delete(cacheKey);
+    }
+
+}

+ 61 - 39
examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/impl/UserServiceImpl.java

@@ -1,14 +1,18 @@
 package cn.com.qmth.examcloud.core.basic.service.impl;
 
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
 import cn.com.qmth.examcloud.commons.exception.StatusException;
 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.UserService;
 import cn.com.qmth.examcloud.task.api.DataSyncCloudService;
 import cn.com.qmth.examcloud.task.api.request.SyncUserReq;
+import com.google.common.collect.Sets;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Set;
 
 /**
  * 用户服务类 Created by songyue on 17/1/13.
@@ -16,41 +20,59 @@ import cn.com.qmth.examcloud.task.api.request.SyncUserReq;
 @Service
 public class UserServiceImpl implements UserService {
 
-	@Autowired
-	UserRepo userRepo;
-
-	@Autowired
-	DataSyncCloudService dataSyncCloudService;
-
-	public UserEntity save(UserEntity user) {
-		UserEntity one = userRepo.findByRootOrgIdAndLoginName(user.getRootOrgId(),
-				user.getLoginName());
-		if (one != null && (!one.getId().equals(user.getId()))) {
-			throw new StatusException("150002", "登录名已存在");
-		}
-
-		long updateTime = 0L;
-		if (null != one && null != one.getUpdateTime()) {
-			updateTime = one.getUpdateTime().getTime();
-		}
-
-		UserEntity saved = userRepo.saveAndFlush(user);
-
-		if (updateTime != saved.getUpdateTime().getTime()) {
-			SyncUserReq r = new SyncUserReq();
-			r.setEnable(saved.getEnable());
-			r.setId(saved.getId());
-			r.setLoginName(saved.getLoginName());
-			r.setName(saved.getName());
-			r.setOrgId(saved.getOrgId());
-			r.setPhoneNumber(saved.getPhoneNumber());
-			r.setRootOrgId(saved.getRootOrgId());
-			r.setSyncType("update");
-
-			dataSyncCloudService.syncUser(r);
-		}
-
-		return saved;
-	}
+    @Autowired
+    UserRepo userRepo;
+
+    @Autowired
+    DataSyncCloudService dataSyncCloudService;
+
+    @Autowired
+    private JdbcTemplate jdbcTemplate;
+
+    public UserEntity save(UserEntity user) {
+        UserEntity one = userRepo.findByRootOrgIdAndLoginName(user.getRootOrgId(),
+                user.getLoginName());
+        if (one != null && (!one.getId().equals(user.getId()))) {
+            throw new StatusException("150002", "登录名已存在");
+        }
+
+        long updateTime = 0L;
+        if (null != one && null != one.getUpdateTime()) {
+            updateTime = one.getUpdateTime().getTime();
+        }
+
+        if (user.getId() == null) {
+            user.setPasswordWeak(true);
+        }
+
+        UserEntity saved = userRepo.saveAndFlush(user);
+
+        if (updateTime != saved.getUpdateTime().getTime()) {
+            SyncUserReq r = new SyncUserReq();
+            r.setEnable(saved.getEnable());
+            r.setId(saved.getId());
+            r.setLoginName(saved.getLoginName());
+            r.setName(saved.getName());
+            r.setOrgId(saved.getOrgId());
+            r.setPhoneNumber(saved.getPhoneNumber());
+            r.setRootOrgId(saved.getRootOrgId());
+            r.setSyncType("update");
+
+            dataSyncCloudService.syncUser(r);
+        }
+
+        return saved;
+    }
+
+    @Override
+    public Set<String> getUserRoleCodes(Long userId) {
+        StringBuilder querySql = new StringBuilder()
+                .append("select r.code from ec_b_user_role_relation ur ")
+                .append("inner join ec_b_role r on r.id = ur.role_id ")
+                .append("where ur.user_id = ").append(userId);
+
+        List<String> roles = jdbcTemplate.queryForList(querySql.toString(), String.class);
+        return Sets.newHashSet(roles);
+    }
 
 }

+ 68 - 0
examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/report/ExamProcessRecordReport.java

@@ -0,0 +1,68 @@
+package cn.com.qmth.examcloud.core.basic.service.report;
+
+import cn.com.qmth.examcloud.reports.commons.bean.BaseReport;
+import cn.com.qmth.examcloud.reports.commons.enums.Tag;
+import cn.com.qmth.examcloud.reports.commons.enums.Topic;
+import cn.com.qmth.examcloud.support.enums.ExamProcess;
+
+import java.util.Date;
+
+/**
+ * @Description 考试过程记录
+ * @Author lideyin
+ * @Date 2020/8/20 14:21
+ * @Version 1.0
+ */
+public class ExamProcessRecordReport extends BaseReport {
+
+    /**
+     * 考试记录ID
+     */
+    private Long examRecordDataId;
+
+    /**
+     * 考试过程
+     */
+    private ExamProcess examProcess;
+
+    /**
+     * 过程记录时间
+     */
+    private Date recordTime;
+
+    public Long getExamRecordDataId() {
+        return examRecordDataId;
+    }
+
+    public void setExamRecordDataId(Long examRecordDataId) {
+        this.examRecordDataId = examRecordDataId;
+    }
+
+    public ExamProcess getExamProcess() {
+        return examProcess;
+    }
+
+    public void setExamProcess(ExamProcess examProcess) {
+        this.examProcess = examProcess;
+    }
+
+    public Date getRecordTime() {
+        return recordTime;
+    }
+
+    public void setRecordTime(Date recordTime) {
+        this.recordTime = recordTime;
+    }
+
+    public ExamProcessRecordReport() {
+        super();
+    }
+
+    public ExamProcessRecordReport(Long examRecordDataId, ExamProcess examProcess, Date recordTime) {
+        this.examRecordDataId = examRecordDataId;
+        this.examProcess = examProcess;
+        this.recordTime = recordTime;
+        this.topic = Topic.REPORT_TOPIC.getCode();
+        this.tag = Tag.EXAM_PROCESS_RECORD.getCode();
+    }
+}

+ 72 - 0
examcloud-core-basic-service/src/main/java/cn/com/qmth/examcloud/core/basic/service/report/RocketMqConsumerListener.java

@@ -0,0 +1,72 @@
+package cn.com.qmth.examcloud.core.basic.service.report;
+
+import java.util.Properties;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.alibaba.fastjson.JSON;
+import com.aliyun.openservices.ons.api.Action;
+import com.aliyun.openservices.ons.api.ConsumeContext;
+import com.aliyun.openservices.ons.api.Consumer;
+import com.aliyun.openservices.ons.api.Message;
+import com.aliyun.openservices.ons.api.MessageListener;
+import com.aliyun.openservices.ons.api.ONSFactory;
+import com.aliyun.openservices.ons.api.PropertyKeyConst;
+
+import cn.com.qmth.examcloud.core.basic.service.AdminOperateService;
+import cn.com.qmth.examcloud.reports.commons.bean.AdminOperateReport;
+import cn.com.qmth.examcloud.reports.commons.enums.Tag;
+import cn.com.qmth.examcloud.reports.commons.enums.Topic;
+import cn.com.qmth.examcloud.web.bootstrap.PropertyHolder;
+import cn.com.qmth.examcloud.web.support.SpringContextHolder;
+
+public class RocketMqConsumerListener {
+    private final static Logger logger = LoggerFactory.getLogger(RocketMqConsumerListener.class);
+    private static AdminOperateService adminOperateService = SpringContextHolder.getBean(AdminOperateService.class);
+    private static Properties properties = new Properties();
+
+    static {
+        properties.put(PropertyKeyConst.AccessKey, PropertyHolder.getString("$rocketmq-accesskey"));
+        // AccessKeySecret 阿里云身份验证,在阿里云服务器管理控制台创建。
+        properties.put(PropertyKeyConst.SecretKey, PropertyHolder.getString("$rocketmq-secretkey"));
+        // 设置发送超时时间,单位毫秒。
+        properties.setProperty(PropertyKeyConst.SendMsgTimeoutMillis, "3000");
+        // 设置 TCP 接入域名,进入控制台的实例详情页面的 TCP 协议客户端接入点区域查看。
+        properties.put(PropertyKeyConst.NAMESRV_ADDR, PropertyHolder.getString("$rocketmq-namesrv-addr"));
+        // 顺序消息消费失败进行重试前的等待时间,单位(毫秒),取值范围: 10 毫秒 ~ 30,000 毫秒
+        properties.put(PropertyKeyConst.SuspendTimeMillis, "100");
+        // 消息消费失败时的最大重试次数
+        properties.put(PropertyKeyConst.MaxReconsumeTimes, "10");
+    }
+
+    public static void start() {
+    	adminOperate();
+    }
+
+    private static void adminOperate() {
+        properties.put(PropertyKeyConst.GROUP_ID, Tag.ADMIN_OPERATE_INFO.getGroup());
+        Consumer consumer = ONSFactory.createConsumer(properties);
+        consumer.subscribe(Topic.REPORT_TOPIC.getCode(), Tag.ADMIN_OPERATE_INFO.getCode(), new MessageListener() {
+            @Override
+            public Action consume(Message message, ConsumeContext context) {
+                try {
+                    String msg = new String(message.getBody(), "utf-8");
+                    onMessageAdminOperate(msg,message.getMsgID());
+                    return Action.CommitMessage;
+                } catch (Exception e) {
+                    logger.error("consumer failed MsgID:" + message.getMsgID(), e);
+                    return Action.ReconsumeLater;
+                }
+            }
+
+        });
+
+        consumer.start();
+    }
+
+    private static void onMessageAdminOperate(String message,String msgId) {
+    	AdminOperateReport r = JSON.parseObject(message, AdminOperateReport.class);
+        adminOperateService.saveOperate(r, msgId);
+    }
+}

+ 65 - 63
examcloud-core-basic-starter/src/main/java/cn/com/qmth/examcloud/core/basic/starter/CoreBasicApp.java

@@ -1,5 +1,13 @@
 package cn.com.qmth.examcloud.core.basic.starter;
 
+import cn.com.qmth.examcloud.core.basic.dao.UniqueRuleHolder;
+import cn.com.qmth.examcloud.core.basic.dao.enums.OrgProperty;
+import cn.com.qmth.examcloud.core.basic.service.LoginRuleService;
+import cn.com.qmth.examcloud.core.basic.service.cache.AppCache;
+import cn.com.qmth.examcloud.support.filestorage.FileStorageUtil;
+import cn.com.qmth.examcloud.web.bootstrap.AppBootstrap;
+import cn.com.qmth.examcloud.web.jpa.DataIntegrityViolationTransverter;
+import cn.com.qmth.examcloud.web.support.SpringContextHolder;
 import org.apache.logging.log4j.ThreadContext;
 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
@@ -16,14 +24,6 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
 import org.springframework.web.multipart.MultipartResolver;
 import org.springframework.web.multipart.commons.CommonsMultipartResolver;
 
-import cn.com.qmth.examcloud.core.basic.dao.UniqueRuleHolder;
-import cn.com.qmth.examcloud.core.basic.dao.enums.OrgProperty;
-import cn.com.qmth.examcloud.core.basic.service.cache.AppCache;
-import cn.com.qmth.examcloud.support.filestorage.FileStorageUtil;
-import cn.com.qmth.examcloud.web.bootstrap.AppBootstrap;
-import cn.com.qmth.examcloud.web.jpa.DataIntegrityViolationTransverter;
-import cn.com.qmth.examcloud.web.support.SpringContextHolder;
-
 @SpringBootApplication
 @Configuration
 @EnableJpaAuditing
@@ -36,67 +36,69 @@ import cn.com.qmth.examcloud.web.support.SpringContextHolder;
 @EnableAutoConfiguration(exclude = {MultipartAutoConfiguration.class})
 public class CoreBasicApp {
 
-	static {
-		String runtimeLevel = System.getProperty("log.commonLevel");
-		if (null == runtimeLevel) {
-			System.setProperty("log.commonLevel", "DEBUG");
-		}
-		System.setProperty("hibernate.dialect.storage_engine", "innodb");
+    static {
+        String runtimeLevel = System.getProperty("log.commonLevel");
+        if (null == runtimeLevel) {
+            System.setProperty("log.commonLevel", "INFO");
+        }
+        System.setProperty("hibernate.dialect.storage_engine", "innodb");
+
+        OrgProperty.init();
+        DataIntegrityViolationTransverter.setUniqueRules(UniqueRuleHolder.getUniqueRuleList());
+    }
 
-		OrgProperty.init();
-		DataIntegrityViolationTransverter.setUniqueRules(UniqueRuleHolder.getUniqueRuleList());
-	}
+    /**
+     * main
+     *
+     * @param args
+     * @throws Exception
+     * @author WANGWEI
+     */
+    public static void main(String[] args) throws Exception {
+        AppBootstrap.run(CoreBasicApp.class, args);
 
-	/**
-	 * main
-	 *
-	 * @author WANGWEI
-	 * @param args
-	 * @throws Exception
-	 */
-	public static void main(String[] args) throws Exception {
+        initCache();
+        FileStorageUtil.initYunSite();
+        FileStorageUtil.initYunClient();
+        // test();
+    }
 
-		AppBootstrap.run(CoreBasicApp.class, args);
-		initCache();
-		FileStorageUtil.initYunSite();
-		FileStorageUtil.initYunClient();
+    /**
+     * 测试方法
+     *
+     * @author WANGWEI
+     */
+    private static void test() {
+        Tester tester = SpringContextHolder.getBean(Tester.class);
+        tester.test();
+    }
 
-		test();
-	}
+    /**
+     * 初始化缓存
+     *
+     * @author WANGWEI
+     */
+    private static void initCache() {
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                ThreadContext.put("TRACE_ID", Thread.currentThread().getName());
 
-	/**
-	 * 测试方法
-	 *
-	 * @author WANGWEI
-	 */
-	private static void test() {
-		Tester tester = SpringContextHolder.getBean(Tester.class);
-		tester.test();
-	}
+                SpringContextHolder.getBean(AppCache.class).loadAllFromResource();
 
-	/**
-	 * 初始化缓存
-	 *
-	 * @author WANGWEI
-	 */
-	private static void initCache() {
-		new Thread(new Runnable() {
-			@Override
-			public void run() {
-				ThreadContext.put("TRACE_ID", Thread.currentThread().getName());
-				SpringContextHolder.getBean(AppCache.class).loadAllFromResource();
-			}
-		}, "cache").start();
-	}
+                SpringContextHolder.getBean(LoginRuleService.class).refreshAllLoginRule();
+            }
+        }, "cache").start();
+    }
 
-	@Bean(name = "multipartResolver")
-	public MultipartResolver multipartResolver() {
-		CommonsMultipartResolver resolver = new CommonsMultipartResolver();
-		resolver.setDefaultEncoding("UTF-8");
-		resolver.setResolveLazily(true);
-		resolver.setMaxInMemorySize(2);
-		resolver.setMaxUploadSize(200 * 1024 * 1024);
-		return resolver;
-	}
+    @Bean(name = "multipartResolver")
+    public MultipartResolver multipartResolver() {
+        CommonsMultipartResolver resolver = new CommonsMultipartResolver();
+        resolver.setDefaultEncoding("UTF-8");
+        resolver.setResolveLazily(true);
+        resolver.setMaxInMemorySize(2);
+        resolver.setMaxUploadSize(200 * 1024 * 1024);
+        return resolver;
+    }
 
 }

+ 126 - 111
examcloud-core-basic-starter/src/main/java/cn/com/qmth/examcloud/core/basic/starter/config/ExamCloudResourceManager.java

@@ -1,27 +1,26 @@
 package cn.com.qmth.examcloud.core.basic.starter.config;
 
-import java.util.List;
-import java.util.Set;
-
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-import com.google.common.collect.Sets;
-
-import cn.com.qmth.examcloud.api.commons.security.bean.AccessApp;
-import cn.com.qmth.examcloud.api.commons.security.bean.Role;
-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.api.commons.enums.DataRuleType;
+import cn.com.qmth.examcloud.api.commons.security.bean.*;
 import cn.com.qmth.examcloud.api.commons.security.enums.RoleMeta;
 import cn.com.qmth.examcloud.commons.util.PropertiesUtil;
 import cn.com.qmth.examcloud.commons.util.RegExpUtil;
+import cn.com.qmth.examcloud.core.basic.api.UserDataRuleCloudService;
+import cn.com.qmth.examcloud.core.basic.api.request.QueryUserDataRuleReq;
+import cn.com.qmth.examcloud.core.basic.api.response.QueryUserDataRuleResp;
 import cn.com.qmth.examcloud.support.cache.CacheHelper;
 import cn.com.qmth.examcloud.support.cache.bean.AppCacheBean;
 import cn.com.qmth.examcloud.web.redis.RedisClient;
 import cn.com.qmth.examcloud.web.security.ResourceManager;
 import cn.com.qmth.examcloud.web.support.ApiInfo;
+import com.google.common.collect.Sets;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Set;
 
 /**
  * Demo资源管理器
@@ -33,102 +32,118 @@ import cn.com.qmth.examcloud.web.support.ApiInfo;
 @Component
 public class ExamCloudResourceManager implements ResourceManager {
 
-	@Autowired
-	RedisClient redisClient;
-
-	static {
-		PropertiesUtil.loadFromResource("security.properties");
-	}
-
-	@Override
-	public AccessApp getAccessApp(Long appId) {
-		AppCacheBean appCacheBean = CacheHelper.getApp(appId);
-		if (null == appCacheBean) {
-			return null;
-		}
-		AccessApp app = new AccessApp();
-		app.setAppId(appCacheBean.getId());
-		app.setAppCode(appCacheBean.getCode());
-		app.setAppName(appCacheBean.getName());
-		app.setSecretKey(appCacheBean.getSecretKey());
-		app.setTimeRange(appCacheBean.getTimeRange());
-		return app;
-	}
-
-	@Override
-	public boolean isNaked(ApiInfo apiInfo, String mapping) {
-		if (null == apiInfo) {
-			return true;
-		}
-
-		if (mapping.matches(".*swagger.*")) {
-			return true;
-		}
-
-		if (null != apiInfo) {
-			if (apiInfo.isNaked()) {
-				return true;
-			}
-		}
-
-		return false;
-	}
-
-	@Override
-	public boolean hasPermission(User user, ApiInfo apiInfo, String mapping) {
-
-		// 学生鉴权
-		if (user.getUserType().equals(UserType.STUDENT)) {
-			String key = "[s]" + mapping;
-			return PropertiesUtil.getBoolean(key, false);
-		}
-
-		List<Role> roleList = user.getRoleList();
-
-		if (CollectionUtils.isEmpty(roleList)) {
-			return false;
-		}
-
-		for (Role role : roleList) {
-			if (role.getRoleCode().equals(RoleMeta.SUPER_ADMIN.name())) {
-				return true;
-			}
-		}
-
-		// 权限组集合
-		String privilegeGroups = PropertiesUtil.getString(mapping);
-		if (StringUtils.isBlank(privilegeGroups)) {
-			return true;
-		}
-
-		// 用户权限集合
-		Set<String> rolePrivilegeList = Sets.newHashSet();
-		Long rootOrgId = user.getRootOrgId();
-		for (Role role : roleList) {
-			String key = "$_P_" + rootOrgId + "_" + role.getRoleId();
-			String rolePrivileges = redisClient.get(key, String.class);
-
-			List<String> rpList = RegExpUtil.findAll(rolePrivileges, "\\w+");
-			rolePrivilegeList.addAll(rpList);
-		}
-
-		List<String> privilegeGroupList = RegExpUtil.findAll(privilegeGroups, "[^\\;]+");
-
-		for (String pg : privilegeGroupList) {
-			pg = pg.trim();
-			if (StringUtils.isBlank(pg)) {
-				continue;
-			}
-
-			List<String> pList = RegExpUtil.findAll(pg, "[^\\,]+");
-			if (rolePrivilegeList.containsAll(pList)) {
-				return true;
-			} else {
-				continue;
-			}
-		}
-
-		return false;
-	}
+    @Autowired
+    RedisClient redisClient;
+
+    @Autowired
+    UserDataRuleCloudService userDataRuleCloudService;
+
+    static {
+        PropertiesUtil.loadFromResource("security.properties");
+    }
+
+    @Override
+    public AccessApp getAccessApp(Long appId) {
+        AppCacheBean appCacheBean = CacheHelper.getApp(appId);
+        if (null == appCacheBean) {
+            return null;
+        }
+        AccessApp app = new AccessApp();
+        app.setAppId(appCacheBean.getId());
+        app.setAppCode(appCacheBean.getCode());
+        app.setAppName(appCacheBean.getName());
+        app.setSecretKey(appCacheBean.getSecretKey());
+        app.setTimeRange(appCacheBean.getTimeRange());
+        return app;
+    }
+
+    @Override
+    public boolean isNaked(ApiInfo apiInfo, String mapping) {
+        if (null == apiInfo) {
+            return true;
+        }
+
+        if (mapping.matches(".*swagger.*")) {
+            return true;
+        }
+
+        if (null != apiInfo) {
+            if (apiInfo.isNaked()) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    public boolean hasPermission(User user, ApiInfo apiInfo, String mapping) {
+
+        // 学生鉴权
+        if (user.getUserType().equals(UserType.STUDENT)) {
+            String key = "[s]" + mapping;
+            return PropertiesUtil.getBoolean(key, false);
+        }
+
+        List<Role> roleList = user.getRoleList();
+
+        if (CollectionUtils.isEmpty(roleList)) {
+            return false;
+        }
+
+        for (Role role : roleList) {
+            if (role.getRoleCode().equals(RoleMeta.SUPER_ADMIN.name())) {
+                return true;
+            }
+        }
+
+        // 权限组集合
+        String privilegeGroups = PropertiesUtil.getString(mapping);
+        if (StringUtils.isBlank(privilegeGroups)) {
+            return true;
+        }
+
+        // 用户权限集合
+        Set<String> rolePrivilegeList = Sets.newHashSet();
+        Long rootOrgId = user.getRootOrgId();
+        for (Role role : roleList) {
+            String key = "$_P_" + rootOrgId + "_" + role.getRoleId();
+            String rolePrivileges = redisClient.get(key, String.class);
+
+            List<String> rpList = RegExpUtil.findAll(rolePrivileges, "\\w+");
+            rolePrivilegeList.addAll(rpList);
+        }
+
+        List<String> privilegeGroupList = RegExpUtil.findAll(privilegeGroups, "[^\\;]+");
+
+        for (String pg : privilegeGroupList) {
+            pg = pg.trim();
+            if (StringUtils.isBlank(pg)) {
+                continue;
+            }
+
+            List<String> pList = RegExpUtil.findAll(pg, "[^\\,]+");
+            if (rolePrivilegeList.containsAll(pList)) {
+                return true;
+            } else {
+                continue;
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    public UserDataRule loadUserDataRule(Long userId, DataRuleType dataRuleType) {
+        QueryUserDataRuleReq req = new QueryUserDataRuleReq();
+        req.setUserId(userId);
+        req.setType(dataRuleType);
+        QueryUserDataRuleResp resp = userDataRuleCloudService.queryUserDataRule(req);
+
+        UserDataRule userDataRule = new UserDataRule();
+        userDataRule.setGlobalStatus(resp.getGlobalStatus());
+        userDataRule.setRefIds(resp.getRefIds());
+        return userDataRule;
+    }
 
 }

+ 30 - 29
examcloud-core-basic-starter/src/main/java/cn/com/qmth/examcloud/core/basic/starter/config/ExamCloudWebMvcConfigurer.java

@@ -1,19 +1,19 @@
 package cn.com.qmth.examcloud.core.basic.starter.config;
 
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.web.servlet.config.annotation.CorsRegistry;
-import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
-import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
-
-import cn.com.qmth.examcloud.web.interceptor.FirstInterceptor;
-import cn.com.qmth.examcloud.web.interceptor.SeqlockInterceptor;
 import cn.com.qmth.examcloud.web.interceptor.ApiFlowLimitedInterceptor;
 import cn.com.qmth.examcloud.web.interceptor.ApiStatisticInterceptor;
+import cn.com.qmth.examcloud.web.interceptor.FirstInterceptor;
+import cn.com.qmth.examcloud.web.interceptor.SeqlockInterceptor;
 import cn.com.qmth.examcloud.web.redis.RedisClient;
+import cn.com.qmth.examcloud.web.security.DataRuleInterceptor;
 import cn.com.qmth.examcloud.web.security.RequestPermissionInterceptor;
 import cn.com.qmth.examcloud.web.security.ResourceManager;
 import cn.com.qmth.examcloud.web.security.RpcInterceptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
 /**
  * WebMvcConfigurer
@@ -25,29 +25,30 @@ import cn.com.qmth.examcloud.web.security.RpcInterceptor;
 @Configuration
 public class ExamCloudWebMvcConfigurer implements WebMvcConfigurer {
 
-	@Autowired
-	ResourceManager resourceManager;
+    @Autowired
+    ResourceManager resourceManager;
+
+    @Autowired
+    RedisClient redisClient;
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        registry.addInterceptor(new FirstInterceptor()).addPathPatterns("/**");
+        registry.addInterceptor(new ApiFlowLimitedInterceptor()).addPathPatterns("/**");
+        registry.addInterceptor(new RpcInterceptor(resourceManager)).addPathPatterns("/**");
 
-	@Autowired
-	RedisClient redisClient;
+        RequestPermissionInterceptor permissionInterceptor = new RequestPermissionInterceptor(resourceManager, redisClient);
+        registry.addInterceptor(permissionInterceptor).addPathPatterns("/**");
+        registry.addInterceptor(new SeqlockInterceptor(redisClient)).addPathPatterns("/**");
+        registry.addInterceptor(new ApiStatisticInterceptor()).addPathPatterns("/**");
 
-	@Override
-	public void addInterceptors(InterceptorRegistry registry) {
-		registry.addInterceptor(new FirstInterceptor()).addPathPatterns("/**");
-		registry.addInterceptor(new ApiFlowLimitedInterceptor()).addPathPatterns("/**");
-		registry.addInterceptor(new RpcInterceptor(resourceManager)).addPathPatterns("/**");
+        registry.addInterceptor(new DataRuleInterceptor(resourceManager)).addPathPatterns("/api/**");
+    }
 
-		RequestPermissionInterceptor permissionInterceptor = new RequestPermissionInterceptor(
-				resourceManager, redisClient);
-		registry.addInterceptor(permissionInterceptor).addPathPatterns("/**");
-		registry.addInterceptor(new SeqlockInterceptor(redisClient)).addPathPatterns("/**");
-		registry.addInterceptor(new ApiStatisticInterceptor()).addPathPatterns("/**");
-	}
+    @Override
+    public void addCorsMappings(CorsRegistry registry) {
+        registry.addMapping("/**").allowedOrigins("*").allowCredentials(false).allowedMethods("*").maxAge(3600);
+    }
 
-	@Override
-	public void addCorsMappings(CorsRegistry registry) {
-		registry.addMapping("/**").allowedOrigins("*").allowCredentials(false).allowedMethods("*")
-				.maxAge(3600);
-	}
+}
 
-}

+ 44 - 0
examcloud-core-basic-starter/src/main/java/cn/com/qmth/examcloud/core/basic/starter/config/MqConsumerListenerStartup.java

@@ -0,0 +1,44 @@
+package cn.com.qmth.examcloud.core.basic.starter.config;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import cn.com.qmth.examcloud.core.basic.service.report.RocketMqConsumerListener;
+import cn.com.qmth.examcloud.reports.commons.enums.MqType;
+import cn.com.qmth.examcloud.web.bootstrap.PropertyHolder;
+
+@Component
+@Order(999)
+public class MqConsumerListenerStartup implements ApplicationRunner {
+	private final static Logger logger = LoggerFactory.getLogger(MqConsumerListenerStartup.class);
+	private void startConsumerListener() {
+
+		String mqType = PropertyHolder.getString("$report.mq-type");
+
+		Boolean reportEnable = PropertyHolder.getBoolean("$report.enable", false);
+
+		if (reportEnable) {
+			if(MqType.ROCKETMQ.getCode().equals(mqType)) {
+				RocketMqConsumerListener.start();
+			}else {
+				logger.error("no $report.mq-type property config!");
+			}
+		}
+
+	}
+
+	@Override
+	public void run(ApplicationArguments args) throws Exception {
+		new Thread() {
+			@Override
+			public void run() {
+				startConsumerListener();
+			}
+			
+		}.start();
+	}
+}