Bläddra i källkod

第三方接口支持
spring-web配置简化
接口鉴权模式修改
内存锁bug修改

luoshi 5 år sedan
förälder
incheckning
e6f62bdbd8
94 ändrade filer med 5344 tillägg och 4414 borttagningar
  1. 0 30
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/api/auth/annotation/AuthValidate.java
  2. 0 26
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/api/auth/exception/ApiException.java
  3. 0 10
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/api/auth/interfaces/AuthValidator.java
  4. 0 9
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/api/auth/model/AuthInfo.java
  5. 0 36
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/api/auth/model/BasicAuthInfo.java
  6. 0 40
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/api/auth/service/AuthInfoService.java
  7. 0 43
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/api/auth/service/validator/AdminUserValidator.java
  8. 0 41
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/api/auth/service/validator/AuthValidateService.java
  9. 0 51
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/api/auth/service/validator/DefaultValidator.java
  10. 0 35
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/api/auth/service/validator/ScannerValidator.java
  11. 19 6
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/MarkerDao.java
  12. 30 22
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/MarkGroup.java
  13. 10 10
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/MarkGroupPK.java
  14. 81 19
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/Marker.java
  15. 2 3
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/MarkGroupService.java
  16. 8 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/MarkerService.java
  17. 3 6
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/MarkGroupServiceImpl.java
  18. 25 6
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/MarkerServiceImpl.java
  19. 8 10
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/lock/LockService.java
  20. 37 36
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/MarkCronService.java
  21. 11 10
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/school/dao/SchoolDao.java
  22. 193 160
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/school/model/School.java
  23. 23 18
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/school/service/SchoolService.java
  24. 16 2
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/school/service/impl/SchoolServiceImpl.java
  25. 23 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/user/dao/OpenAccountDao.java
  26. 125 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/user/model/OpenAccount.java
  27. 213 200
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/user/model/User.java
  28. 47 34
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/user/service/UserService.java
  29. 217 162
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/user/service/impl/UserServiceImpl.java
  30. 47 45
      stmms-biz/src/main/resources/service-context.xml
  31. 22 35
      stmms-biz/src/test/resources/application.properties
  32. 29 0
      stmms-common/src/main/java/cn/com/qmth/stmms/common/enums/AccountType.java
  33. 42 41
      stmms-common/src/main/java/cn/com/qmth/stmms/common/enums/Role.java
  34. 209 0
      stmms-common/src/main/java/cn/com/qmth/stmms/common/signature/SignatureInfo.java
  35. 17 0
      stmms-common/src/main/java/cn/com/qmth/stmms/common/signature/SignatureType.java
  36. 38 0
      stmms-common/src/main/java/cn/com/qmth/stmms/common/utils/AccessControlUtils.java
  37. 11 17
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ArbitrateController.java
  38. 9 9
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ExamController.java
  39. 111 110
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ImageCheckController.java
  40. 10 11
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/InspectedController.java
  41. 7 11
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/LibraryController.java
  42. 56 69
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/MarkGroupController.java
  43. 24 27
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/MarkQualityController.java
  44. 98 54
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/MarkerController.java
  45. 21 23
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/PaperController.java
  46. 115 113
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ScoreCheckController.java
  47. 27 30
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ScoreController.java
  48. 18 26
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/StudentController.java
  49. 1 1
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/SubjectController.java
  50. 13 18
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/TrialController.java
  51. 43 44
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/interceptor/AdminExamInterceptor.java
  52. 57 60
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/interceptor/AdminInterceptor.java
  53. 31 33
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/interceptor/SysAdminInterceptor.java
  54. 190 188
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/school/SchoolController.java
  55. 156 156
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/user/UserController.java
  56. 4 3
      stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/BaseApiController.java
  57. 32 36
      stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/ExamInfoController.java
  58. 12 12
      stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/ExamPackageController.java
  59. 36 44
      stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/ExamStudentController.java
  60. 41 34
      stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/LoginController.java
  61. 7 6
      stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/PictureController.java
  62. 7 7
      stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/ScanController.java
  63. 53 0
      stmms-web/src/main/java/cn/com/qmth/stmms/api/dto/UserDTO.java
  64. 29 0
      stmms-web/src/main/java/cn/com/qmth/stmms/api/exception/ApiException.java
  65. 135 92
      stmms-web/src/main/java/cn/com/qmth/stmms/api/interceptor/ApiInterceptor.java
  66. 0 16
      stmms-web/src/main/java/cn/com/qmth/stmms/api/utils/AuthInfoUtils.java
  67. 38 39
      stmms-web/src/main/java/cn/com/qmth/stmms/common/annotation/RoleRequire.java
  68. 45 0
      stmms-web/src/main/java/cn/com/qmth/stmms/common/authorization/AuthorizationService.java
  69. 28 20
      stmms-web/src/main/java/cn/com/qmth/stmms/common/controller/BaseController.java
  70. 11 14
      stmms-web/src/main/java/cn/com/qmth/stmms/common/controller/LoginController.java
  71. 45 0
      stmms-web/src/main/java/cn/com/qmth/stmms/common/domain/ApiUser.java
  72. 55 0
      stmms-web/src/main/java/cn/com/qmth/stmms/common/domain/AuthInfo.java
  73. 63 0
      stmms-web/src/main/java/cn/com/qmth/stmms/common/domain/OpenUser.java
  74. 28 52
      stmms-web/src/main/java/cn/com/qmth/stmms/common/domain/WebUser.java
  75. 0 97
      stmms-web/src/main/java/cn/com/qmth/stmms/common/filter/SessionFilter.java
  76. 0 116
      stmms-web/src/main/java/cn/com/qmth/stmms/common/interceptor/CommonInterceptor.java
  77. 62 65
      stmms-web/src/main/java/cn/com/qmth/stmms/common/interceptor/MethodInterceptor.java
  78. 90 0
      stmms-web/src/main/java/cn/com/qmth/stmms/common/interceptor/SessionInterceptor.java
  79. 137 61
      stmms-web/src/main/java/cn/com/qmth/stmms/common/session/model/StmmsSession.java
  80. 35 37
      stmms-web/src/main/java/cn/com/qmth/stmms/common/session/service/SessionService.java
  81. 101 114
      stmms-web/src/main/java/cn/com/qmth/stmms/common/session/service/impl/CookieSessionServiceImpl.java
  82. 152 159
      stmms-web/src/main/java/cn/com/qmth/stmms/common/utils/RequestUtils.java
  83. 28 26
      stmms-web/src/main/java/cn/com/qmth/stmms/mark/MarkController.java
  84. 25 30
      stmms-web/src/main/java/cn/com/qmth/stmms/mark/interceptor/MarkInterceptor.java
  85. 132 0
      stmms-web/src/main/java/cn/com/qmth/stmms/open/controller/OpenAccountController.java
  86. 50 0
      stmms-web/src/main/java/cn/com/qmth/stmms/open/interceptor/OpenInterceptor.java
  87. 7 11
      stmms-web/src/main/webapp/WEB-INF/application.properties
  88. 33 35
      stmms-web/src/main/webapp/WEB-INF/applicationContext.xml
  89. 142 142
      stmms-web/src/main/webapp/WEB-INF/spring-mvc.xml
  90. 59 0
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/markerAccount.jsp
  91. 319 284
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/markerList.jsp
  92. 76 64
      stmms-web/src/main/webapp/WEB-INF/views/modules/sys/schoolEdit.jsp
  93. 67 79
      stmms-web/src/main/webapp/WEB-INF/web.xml
  94. 667 573
      stmms-web/src/main/webapp/sql/stmms_ft.sql

+ 0 - 30
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/api/auth/annotation/AuthValidate.java

@@ -1,30 +0,0 @@
-package cn.com.qmth.stmms.biz.api.auth.annotation;
-
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-/**
- * API方法权限验证器定义
- * 
- * @author LS
- * 
- */
-@Target({ METHOD })
-@Retention(RUNTIME)
-public @interface AuthValidate {
-
-    /**
-     * 指定权限验证器的名称
-     * 
-     * @return
-     */
-    String[] value() default {};
-
-    /**
-     * true-与 false-或
-     */
-    boolean nand() default false;
-}

+ 0 - 26
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/api/auth/exception/ApiException.java

@@ -1,26 +0,0 @@
-package cn.com.qmth.stmms.biz.api.auth.exception;
-
-public class ApiException extends RuntimeException {
-
-	private static final long serialVersionUID = -5671666616240173459L;
-
-    private int code;
-
-    private ApiException(int code, String message) {
-        super(message);
-        this.code = code;
-    }
-
-    public int getCode() {
-        return code;
-    }
-
-    public static final ApiException USER_UNEXISTS = new ApiException(401, "user unexists");
-
-    public static final ApiException USER_PASSWORD_ERROR = new ApiException(401, "user password error");
-
-    public static final ApiException USER_NOT_ADMIN = new ApiException(401, "user is not admin");
-
-    public static final ApiException EXAM_NOT_ACCESSIBLED = new ApiException(401, "user cannot access specified exam");
-
-}

+ 0 - 10
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/api/auth/interfaces/AuthValidator.java

@@ -1,10 +0,0 @@
-package cn.com.qmth.stmms.biz.api.auth.interfaces;
-
-import cn.com.qmth.stmms.biz.api.auth.model.AuthInfo;
-
-public interface AuthValidator {
-
-    public String getName();
-
-    public boolean validate(AuthInfo auth);
-}

+ 0 - 9
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/api/auth/model/AuthInfo.java

@@ -1,9 +0,0 @@
-package cn.com.qmth.stmms.biz.api.auth.model;
-
-import cn.com.qmth.stmms.biz.user.model.User;
-
-public interface AuthInfo {
-
-    public User getLoginUser();
-
-}

+ 0 - 36
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/api/auth/model/BasicAuthInfo.java

@@ -1,36 +0,0 @@
-package cn.com.qmth.stmms.biz.api.auth.model;
-
-import cn.com.qmth.stmms.biz.user.model.User;
-
-public class BasicAuthInfo implements AuthInfo {
-
-    private User loginUser;
-
-    private String loginName;
-
-    private String password;
-
-    public String getLoginName() {
-        return loginName;
-    }
-
-    public void setLoginName(String loginName) {
-        this.loginName = loginName;
-    }
-
-    public String getPassword() {
-        return password;
-    }
-
-    public void setPassword(String password) {
-        this.password = password;
-    }
-
-    public User getLoginUser() {
-        return loginUser;
-    }
-
-    public void setLoginUser(User loginUser) {
-        this.loginUser = loginUser;
-    }
-}

+ 0 - 40
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/api/auth/service/AuthInfoService.java

@@ -1,40 +0,0 @@
-package cn.com.qmth.stmms.biz.api.auth.service;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.commons.lang.StringUtils;
-import org.springframework.stereotype.Service;
-
-import cn.com.qmth.stmms.biz.api.auth.model.AuthInfo;
-import cn.com.qmth.stmms.biz.api.auth.model.BasicAuthInfo;
-
-@Service("authInfoService")
-public class AuthInfoService {
-
-    private static final String ENTRY_SPLITE = ";";
-
-    private static final String KV_SPLITE = "=";
-
-    public AuthInfo getAuthInfo(String infoString) {
-        Map<String, String> infoMap = buildInfoMap(infoString);
-        BasicAuthInfo info = new BasicAuthInfo();
-        info.setLoginName(infoMap.get("loginname"));
-        info.setPassword(infoMap.get("password"));
-        return info;
-    }
-
-    private Map<String, String> buildInfoMap(String infoString) {
-        Map<String, String> infoMap = new HashMap<String, String>();
-        String[] values = StringUtils.split(infoString, ENTRY_SPLITE);
-        if (values != null && values.length > 0) {
-            for (String value : values) {
-                String[] pair = StringUtils.split(value, KV_SPLITE);
-                if (pair != null && pair.length == 2) {
-                    infoMap.put(pair[0].trim(), pair[1].trim());
-                }
-            }
-        }
-        return infoMap;
-    }
-}

+ 0 - 43
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/api/auth/service/validator/AdminUserValidator.java

@@ -1,43 +0,0 @@
-package cn.com.qmth.stmms.biz.api.auth.service.validator;
-
-import javax.annotation.PostConstruct;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-import cn.com.qmth.stmms.biz.api.auth.exception.ApiException;
-import cn.com.qmth.stmms.biz.api.auth.interfaces.AuthValidator;
-import cn.com.qmth.stmms.biz.api.auth.model.AuthInfo;
-import cn.com.qmth.stmms.biz.user.model.User;
-import cn.com.qmth.stmms.common.enums.Role;
-
-@Component
-public class AdminUserValidator implements AuthValidator {
-
-    @Autowired
-    private AuthValidateService validateService;
-
-    @PostConstruct
-    protected void init() {
-        validateService.addValidator(this);
-    }
-
-    @Override
-    public boolean validate(AuthInfo auth) {
-        User user = auth.getLoginUser();
-        if (user == null) {
-            throw ApiException.USER_UNEXISTS;
-        }
-        if(user.isEnable() && user.getRole() == Role.SCHOOL_ADMIN){;
-        	return true;
-        } else {
-            throw ApiException.USER_NOT_ADMIN;
-        }
-    }
-
-    @Override
-    public String getName() {
-        return "adminUser";
-    }
-
-}

+ 0 - 41
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/api/auth/service/validator/AuthValidateService.java

@@ -1,41 +0,0 @@
-package cn.com.qmth.stmms.biz.api.auth.service.validator;
-
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-
-import org.springframework.stereotype.Service;
-
-import cn.com.qmth.stmms.biz.api.auth.interfaces.AuthValidator;
-
-@Service("authValidateService")
-public class AuthValidateService {
-
-    private Map<String, AuthValidator> map = new HashMap<String, AuthValidator>();
-
-    public List<AuthValidator> getValidators(String[] names) {
-        List<AuthValidator> list = new LinkedList<AuthValidator>();
-        list.add(getValidator("default"));
-
-        if (names != null && names.length > 0) {
-            for (String name : names) {
-                AuthValidator v = getValidator(name);
-                if (v != null) {
-                    list.add(v);
-                }
-            }
-        }
-        return list;
-    }
-
-    public AuthValidator getValidator(String name) {
-        return map.get(name);
-    }
-
-    protected void addValidator(AuthValidator validator) {
-        if (validator != null) {
-            map.put(validator.getName(), validator);
-        }
-    }
-}

+ 0 - 51
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/api/auth/service/validator/DefaultValidator.java

@@ -1,51 +0,0 @@
-package cn.com.qmth.stmms.biz.api.auth.service.validator;
-
-import javax.annotation.PostConstruct;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-import cn.com.qmth.stmms.biz.api.auth.exception.ApiException;
-import cn.com.qmth.stmms.biz.api.auth.interfaces.AuthValidator;
-import cn.com.qmth.stmms.biz.api.auth.model.AuthInfo;
-import cn.com.qmth.stmms.biz.api.auth.model.BasicAuthInfo;
-import cn.com.qmth.stmms.biz.user.model.User;
-import cn.com.qmth.stmms.biz.user.service.UserService;
-import cn.com.qmth.stmms.common.utils.Md5EncryptUtils;
-
-@Component
-public class DefaultValidator implements AuthValidator {
-
-    @Autowired
-    private AuthValidateService validateService;
-
-    @Autowired
-    private UserService userService;
-
-    @PostConstruct
-    protected void init() {
-        validateService.addValidator(this);
-    }
-
-    @Override
-    public String getName() {
-        return "default";
-    }
-
-    @Override
-    public boolean validate(AuthInfo auth) {
-        BasicAuthInfo info = (BasicAuthInfo) auth;
-        User user = userService.findByLoginName(info.getLoginName());
-        if (user != null) {
-        	if (user.isEnable() && user.getPassword().equals(Md5EncryptUtils.md5(info.getPassword()))) {
-        		info.setLoginUser(user);
-        		return true;
-        	}else{
-        		throw ApiException.USER_PASSWORD_ERROR;
-        	}
-        }else{
-        	throw ApiException.USER_UNEXISTS;
-        }
-    }
-
-}

+ 0 - 35
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/api/auth/service/validator/ScannerValidator.java

@@ -1,35 +0,0 @@
-package cn.com.qmth.stmms.biz.api.auth.service.validator;
-
-import javax.annotation.PostConstruct;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-import cn.com.qmth.stmms.biz.api.auth.interfaces.AuthValidator;
-import cn.com.qmth.stmms.biz.api.auth.model.AuthInfo;
-import cn.com.qmth.stmms.biz.user.model.User;
-import cn.com.qmth.stmms.common.enums.Role;
-
-@Component
-public class ScannerValidator implements AuthValidator {
-
-    @Autowired
-    private AuthValidateService validateService;
-
-    @PostConstruct
-    protected void init() {
-        validateService.addValidator(this);
-    }
-
-    @Override
-    public boolean validate(AuthInfo auth) {
-        User user = auth.getLoginUser();
-        return user != null && user.isEnable() && user.getRole() == Role.SCANNER;
-    }
-
-    @Override
-    public String getName() {
-        return "scanner";
-    }
-
-}

+ 19 - 6
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/MarkerDao.java

@@ -2,6 +2,8 @@ package cn.com.qmth.stmms.biz.exam.dao;
 
 import java.util.List;
 
+import cn.com.qmth.stmms.common.enums.AccountType;
+import cn.com.qmth.stmms.common.enums.MarkStatus;
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
 import org.springframework.data.jpa.repository.Modifying;
@@ -22,8 +24,7 @@ public interface MarkerDao extends PagingAndSortingRepository<Marker, Integer>,
 
     public List<Marker> findByExamIdAndSubjectCode(int examId, String subjectCode, Pageable page);
 
-    public List<Marker> findByExamIdAndSubjectCodeAndGroupNumber(int examId, String subjectCode, int number,
-            Pageable page);
+    public List<Marker> findByExamIdAndSubjectCodeAndGroupNumber(int examId, String subjectCode, int number, Pageable page);
 
     public List<Marker> findByLoginName(String loginName);
 
@@ -47,15 +48,17 @@ public interface MarkerDao extends PagingAndSortingRepository<Marker, Integer>,
     public void resetById(Integer id);
 
     @Modifying
-    @Query("update Marker m set m.finishCount=?2, m.validCount=?3, m.avgSpeed=?4, m.avgScore=?5, m.stdevScore=?6 "
-            + "where m.id=?1")
-    public void updateQualityById(Integer id, int finishCount, int validCount, double avgSpeed, double avgScore,
-            double stdevScore);
+    @Query("update Marker m set m.finishCount=?2, m.validCount=?3, m.avgSpeed=?4, m.avgScore=?5, m.stdevScore=?6 " + "where m.id=?1")
+    public void updateQualityById(Integer id, int finishCount, int validCount, double avgSpeed, double avgScore, double stdevScore);
 
     @Modifying
     @Query("update Marker m set m.markSetting=?2 where m.id=?1")
     public void updateMarkSettingById(Integer id, String setting);
 
+    @Modifying
+    @Query("update Marker m set m.accountType=?2, m.accountId=?3 where m.id=?1")
+    public void updateAccountById(Integer id, AccountType accountType, Integer accountId);
+
     @Modifying
     @Query("update Marker m set m.finishCount=null, m.validCount=null, m.avgSpeed=null, m.avgScore=null, m.stdevScore=null "
             + "where m.examId=?1 and m.subjectCode=?2 and m.groupNumber=?3")
@@ -68,4 +71,14 @@ public interface MarkerDao extends PagingAndSortingRepository<Marker, Integer>,
     public long countByLoginName(String loginName);
 
     public List<Marker> findByMode(String common);
+
+    @Query(value = "select m from Marker m, MarkGroup g where m.accountType=?1 and m.accountId=?2 and m.enable=?3 "
+            + "and m.examId=g.pk.examId and m.subjectCode=g.pk.subjectCode and m.groupNumber=g.pk.number and g.status in (?4)")
+    List<Marker> findByAccountTypeAndAccountIdAndEnableAndMarkStatus(AccountType accountType, Integer accountId, boolean enable,
+            MarkStatus... status);
+
+    @Query("select count(m) from Marker m where m.examId=?1 and m.subjectCode=?2 and m.groupNumber=?3 and m.accountType=?4 and m.accountId=?5")
+    long countByExamIdAndSubjectCodeAndGroupNumberAndAccountTypeAndAccountId(Integer examId, String subjectCode, Integer number,
+            AccountType accountType, Integer accountId);
+
 }

+ 30 - 22
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/MarkGroup.java

@@ -27,6 +27,9 @@ import cn.com.qmth.stmms.common.enums.MarkStatus;
 import cn.com.qmth.stmms.common.enums.ScorePolicy;
 import cn.com.qmth.stmms.common.enums.ThirdPolicy;
 
+/**
+ * 评卷分组对象
+ */
 @Entity
 @Table(name = "eb_mark_group")
 public class MarkGroup implements Serializable {
@@ -36,9 +39,15 @@ public class MarkGroup implements Serializable {
     @EmbeddedId
     private MarkGroupPK pk;
 
+    /**
+     * 评卷图片设置
+     */
     @Column(name = "pic_list", length = 255, nullable = false)
     private String picList;
 
+    /**
+     * 分组总分
+     */
     @Column(name = "total_score", nullable = false)
     private Double totalScore;
 
@@ -63,31 +72,40 @@ public class MarkGroup implements Serializable {
     /**
      * 合分策略(1-平均,2-最高,3-最低)
      */
-    @Column(name = "score_policy", nullable = true)
+    @Column(name = "score_policy", nullable = true, length = 16)
     @Enumerated(EnumType.STRING)
     private ScorePolicy scorePolicy;
-    
+
     /**
      * 三评策略(1-关闭,2-低差值高均分)
      */
-    @Column(name = "third_policy", nullable = true)
+    @Column(name = "third_policy", nullable = true, length = 16)
     @Enumerated(EnumType.STRING)
     private ThirdPolicy thirdPolicy;
 
     /**
      * 强制评卷模式
      */
-    @Column(name = "mark_mode", nullable = true)
+    @Column(name = "mark_mode", nullable = true, length = 16)
     @Enumerated(EnumType.STRING)
     private MarkMode markMode;
 
+    /**
+     * 开启/关闭 原卷显示
+     */
+    @Column(name = "sheet_view", nullable = false)
+    private boolean sheetView;
+
     /**
      * 当前评卷状态
      */
-    @Column(name = "status", length = 16, nullable = false)
+    @Column(name = "status", nullable = false, length = 16)
     @Enumerated(EnumType.STRING)
     private MarkStatus status;
 
+    /**
+     * 正评任务最后构造时间
+     */
     @Temporal(TemporalType.TIMESTAMP)
     @Column(name = "build_time", nullable = true)
     private Date buildTime;
@@ -101,12 +119,6 @@ public class MarkGroup implements Serializable {
     @Column(name = "left_count")
     private Integer leftCount;
 
-    /**
-     * 开启/关闭 原卷显示
-     */
-    @Column(name = "sheet_view", nullable = false)
-    private boolean sheetView;
-
     @Transient
     private int currentCount;
 
@@ -138,16 +150,14 @@ public class MarkGroup implements Serializable {
         this.pk = new MarkGroupPK();
     }
 
-    public MarkGroup(Integer examId, String subjectCode, Integer number, List<PictureConfigItem> configList,
-            Double totalScore, Double doubleRate, Double arbitrateThreshold, Integer scorePolicy, String markMode,
-            Integer trialCount, boolean sheetView,Integer thirdPolicy) {
+    public MarkGroup(Integer examId, String subjectCode, Integer number, List<PictureConfigItem> configList, Double totalScore,
+            Double doubleRate, Double arbitrateThreshold, Integer scorePolicy, String markMode, Integer trialCount, boolean sheetView,
+            Integer thirdPolicy) {
         this.pk = new MarkGroupPK();
         this.pk.setExamId(examId);
         this.pk.setNumber(number);
         this.pk.setSubjectCode(subjectCode);
-        this.picList = configList != null && configList.size() > 0
-                ? StringUtils.join(configList, PictureConfigItem.DB_ITEM_JOINER)
-                : "";
+        this.picList = configList != null && configList.size() > 0 ? StringUtils.join(configList, PictureConfigItem.DB_ITEM_JOINER) : "";
         this.totalScore = totalScore;
         this.libraryCount = 0;
         this.markedCount = 0;
@@ -174,7 +184,7 @@ public class MarkGroup implements Serializable {
             this.status = MarkStatus.FORMAL;
         }
         this.sheetView = sheetView;
-        
+
         if (thirdPolicy != null) {
             this.thirdPolicy = ThirdPolicy.findByValue(thirdPolicy);
         } else {
@@ -215,9 +225,7 @@ public class MarkGroup implements Serializable {
     }
 
     public void setPicList(List<PictureConfigItem> configList) {
-        this.picList = configList != null && configList.size() > 0
-                ? StringUtils.join(configList, PictureConfigItem.DB_ITEM_JOINER)
-                : "";
+        this.picList = configList != null && configList.size() > 0 ? StringUtils.join(configList, PictureConfigItem.DB_ITEM_JOINER) : "";
     }
 
     public List<PictureConfigItem> getPictureConfigList() {
@@ -413,7 +421,7 @@ public class MarkGroup implements Serializable {
     public ThirdPolicy getThirdPolicy() {
         return thirdPolicy;
     }
-    
+
     public void setThirdPolicy(ThirdPolicy thirdPolicy) {
         this.thirdPolicy = thirdPolicy;
     }

+ 10 - 10
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/MarkGroupPK.java

@@ -8,17 +8,17 @@ import javax.persistence.Embeddable;
 @Embeddable
 public class MarkGroupPK implements Serializable {
 
-	private static final long serialVersionUID = -7637998031337761686L;
+    private static final long serialVersionUID = -7637998031337761686L;
 
-	@Column(name = "exam_id")
+    @Column(name = "exam_id", nullable = false)
     private Integer examId;
 
-    @Column(name = "subject_code")
+    @Column(name = "subject_code", nullable = false, length = 32)
     private String subjectCode;
 
+    @Column(name = "number", nullable = false)
     private Integer number;
 
-
     public Integer getExamId() {
         return examId;
     }
@@ -36,14 +36,14 @@ public class MarkGroupPK implements Serializable {
     }
 
     public Integer getNumber() {
-		return number;
-	}
+        return number;
+    }
 
-	public void setNumber(Integer number) {
-		this.number = number;
-	}
+    public void setNumber(Integer number) {
+        this.number = number;
+    }
 
-	@Override
+    @Override
     public int hashCode() {
         final int PRIME = 31;
         int result = 1;

+ 81 - 19
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/Marker.java

@@ -12,6 +12,10 @@ import javax.persistence.Id;
 import javax.persistence.Table;
 import javax.persistence.Transient;
 
+import cn.com.qmth.stmms.biz.user.model.OpenAccount;
+import cn.com.qmth.stmms.biz.user.model.User;
+import cn.com.qmth.stmms.common.enums.AccountType;
+import cn.com.qmth.stmms.common.utils.AccessControlUtils;
 import org.apache.commons.lang.StringUtils;
 
 import cn.com.qmth.stmms.common.annotation.ExcelField;
@@ -29,34 +33,41 @@ public class Marker implements Serializable {
     @GeneratedValue
     private Integer id;
 
-    @Column(name = "exam_id")
+    @Column(name = "exam_id", nullable = false)
     private Integer examId;
 
-    @Column(name = "subject_code")
+    @Column(name = "subject_code", nullable = false)
     @ExcelField(title = "科目代码", align = 2, sort = 10)
     private String subjectCode;
 
-    @Column(name = "group_number")
+    @Column(name = "group_number", nullable = false)
     private Integer groupNumber;
 
-    @Column(name = "login_name")
+    @Column(name = "login_name", nullable = false)
     @ExcelField(title = "登录名", align = 2, sort = 30)
     private String loginName;
 
-    @Column(name = "name")
+    @Column(name = "name", nullable = true)
     private String name;
 
-    @Column(name = "password")
+    @Column(name = "password", nullable = false)
     @ExcelField(title = "密码", align = 2, sort = 40)
     private String password;
 
-    @Column(name = "enable")
+    @Column(name = "account_type", nullable = true, length = 16)
+    @Enumerated(EnumType.STRING)
+    private AccountType accountType;
+
+    @Column(name = "account_id", nullable = true)
+    private Integer accountId;
+
+    @Column(name = "enable", nullable = false)
     private boolean enable;
 
-    @Column(name = "last_login_ip")
+    @Column(name = "last_login_ip", nullable = true)
     private String lastLoginIp;
 
-    @Column(name = "last_login_time")
+    @Column(name = "last_login_time", nullable = true)
     private Date lastLoginTime;
 
     @Column(name = "mode", nullable = true)
@@ -93,6 +104,18 @@ public class Marker implements Serializable {
     @Column(name = "mark_setting", nullable = true, columnDefinition = "text")
     private String markSetting;
 
+    /**
+     * 访问令牌
+     */
+    @Column(name = "access_token", nullable = true, length = 64)
+    private String accessToken;
+
+    /**
+     * 令牌刷新时间
+     */
+    @Column(name = "access_token_refresh_time", nullable = true)
+    private Date accessTokenRefreshTime;
+
     @Transient
     @ExcelField(title = "科目(导入时可为空)", align = 2, sort = 20)
     private String subjectName;
@@ -101,12 +124,6 @@ public class Marker implements Serializable {
     @ExcelField(title = "大题", align = 2, sort = 20)
     private String groupName;
 
-    /**
-     * 登录token
-     */
-    @Column(name = "web_token")
-    private String webToken;
-
     @Transient
     private int number;
 
@@ -116,12 +133,21 @@ public class Marker implements Serializable {
     @Transient
     private MarkGroup group;
 
+    @Transient
+    private OpenAccount openAccount;
+
+    @Transient
+    private User user;
+
     @Transient
     private long markedCount;
 
     @Transient
     private long currentCount;
 
+    public Marker() {
+    }
+
     public Integer getId() {
         return id;
     }
@@ -154,6 +180,22 @@ public class Marker implements Serializable {
         this.password = password;
     }
 
+    public AccountType getAccountType() {
+        return accountType;
+    }
+
+    public void setAccountType(AccountType accountType) {
+        this.accountType = accountType;
+    }
+
+    public Integer getAccountId() {
+        return accountId;
+    }
+
+    public void setAccountId(Integer accountId) {
+        this.accountId = accountId;
+    }
+
     public String getLastLoginIp() {
         return lastLoginIp;
     }
@@ -229,6 +271,22 @@ public class Marker implements Serializable {
         this.subject = subject;
     }
 
+    public OpenAccount getOpenAccount() {
+        return openAccount;
+    }
+
+    public void setOpenAccount(OpenAccount openAccount) {
+        this.openAccount = openAccount;
+    }
+
+    public User getUser() {
+        return user;
+    }
+
+    public void setUser(User user) {
+        this.user = user;
+    }
+
     public long getMarkedCount() {
         return markedCount;
     }
@@ -357,12 +415,16 @@ public class Marker implements Serializable {
         this.markSetting = markSetting;
     }
 
-    public String getWebToken() {
-        return webToken;
+    public String getAccessToken() {
+        return accessToken;
     }
 
-    public void setWebToken(String webToken) {
-        this.webToken = webToken;
+    public Date getAccessTokenRefreshTime() {
+        return accessTokenRefreshTime;
     }
 
+    public void refreshAccessToken() {
+        accessToken = AccessControlUtils.randomString();
+        accessTokenRefreshTime = new Date();
+    }
 }

+ 2 - 3
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/MarkGroupService.java

@@ -46,8 +46,7 @@ public interface MarkGroupService {
 
     double sumTotalScore(Integer examId, String subjectCode);
 
-    boolean updateStatus(int examId, String subjectCode, Integer number, MarkStatus newStatus,
-            MarkStatus... currentStatus);
+    boolean updateStatus(int examId, String subjectCode, Integer number, MarkStatus newStatus, MarkStatus... currentStatus);
 
     List<Integer> findExamIdByStatus(MarkStatus... status);
 
@@ -57,6 +56,6 @@ public interface MarkGroupService {
 
     long countByExamAndStatus(Integer examId, MarkStatus... status);
 
-    boolean validateStatus(Integer examId, String subjectCode, Integer number, MarkStatus status);
+    boolean validateStatus(Integer examId, String subjectCode, Integer number, MarkStatus... status);
 
 }

+ 8 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/MarkerService.java

@@ -5,6 +5,8 @@ import java.util.List;
 import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
 import cn.com.qmth.stmms.biz.exam.model.Marker;
 import cn.com.qmth.stmms.biz.exam.query.MarkerSearchQuery;
+import cn.com.qmth.stmms.common.enums.AccountType;
+import cn.com.qmth.stmms.common.enums.MarkStatus;
 
 public interface MarkerService {
 
@@ -38,4 +40,10 @@ public interface MarkerService {
 
     void updateMarkSetting(Integer id, String setting);
 
+    void updateAccountById(Integer id, AccountType accountType, Integer accountId);
+
+    List<Marker> findByAccountAndMarkStatus(AccountType accountType, Integer accountId, MarkStatus... status);
+
+    long countByExamAndSubjectAndGroupAndAccount(Integer examId, String subjectCode, Integer groupNumber, AccountType accountType,
+            Integer accountId);
 }

+ 3 - 6
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/MarkGroupServiceImpl.java

@@ -39,9 +39,7 @@ public class MarkGroupServiceImpl extends BaseQueryService<MarkGroup> implements
     @Override
     public void updatePicList(int examId, String subjectCode, int number, List<PictureConfigItem> configList) {
         groupDao.updatePicList(examId, subjectCode, number,
-                configList != null && configList.size() > 0
-                        ? StringUtils.join(configList, PictureConfigItem.DB_ITEM_JOINER)
-                        : "");
+                configList != null && configList.size() > 0 ? StringUtils.join(configList, PictureConfigItem.DB_ITEM_JOINER) : "");
     }
 
     @Transactional
@@ -82,8 +80,7 @@ public class MarkGroupServiceImpl extends BaseQueryService<MarkGroup> implements
 
     @Transactional
     @Override
-    public boolean updateStatus(int examId, String subjectCode, Integer number, MarkStatus newStatus,
-            MarkStatus... currentStatus) {
+    public boolean updateStatus(int examId, String subjectCode, Integer number, MarkStatus newStatus, MarkStatus... currentStatus) {
         return groupDao.updateStatus(examId, subjectCode, number, newStatus, currentStatus) > 0;
     }
 
@@ -160,7 +157,7 @@ public class MarkGroupServiceImpl extends BaseQueryService<MarkGroup> implements
     }
 
     @Override
-    public boolean validateStatus(Integer examId, String subjectCode, Integer number, MarkStatus status) {
+    public boolean validateStatus(Integer examId, String subjectCode, Integer number, MarkStatus... status) {
         return groupDao.countByExamIdAndSubjectCodeAndNumberAndStatus(examId, subjectCode, number, status) > 0;
     }
 

+ 25 - 6
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/MarkerServiceImpl.java

@@ -9,6 +9,8 @@ import javax.persistence.criteria.CriteriaQuery;
 import javax.persistence.criteria.Predicate;
 import javax.persistence.criteria.Root;
 
+import cn.com.qmth.stmms.common.enums.AccountType;
+import cn.com.qmth.stmms.common.enums.MarkStatus;
 import org.apache.commons.lang.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.Page;
@@ -59,6 +61,12 @@ public class MarkerServiceImpl extends BaseQueryService<Marker> implements Marke
         markerDao.updateMarkSettingById(id, setting);
     }
 
+    @Transactional
+    @Override
+    public void updateAccountById(Integer id, AccountType accountType, Integer accountId) {
+        markerDao.updateAccountById(id, accountType, accountId);
+    }
+
     @Override
     public Marker findById(Integer id) {
         return markerDao.findOne(id);
@@ -139,8 +147,8 @@ public class MarkerServiceImpl extends BaseQueryService<Marker> implements Marke
 
     private Marker findLastMarker(int examId, String subjectCode) {
         Marker marker = null;
-        List<Marker> list = markerDao.findByExamIdAndSubjectCode(examId, subjectCode,
-                new BaseQuery<Marker>(1, 1, new Sort(Direction.DESC, "id")));
+        List<Marker> list = markerDao
+                .findByExamIdAndSubjectCode(examId, subjectCode, new BaseQuery<Marker>(1, 1, new Sort(Direction.DESC, "id")));
         if (list != null && !list.isEmpty()) {
             marker = list.get(0);
         }
@@ -154,7 +162,7 @@ public class MarkerServiceImpl extends BaseQueryService<Marker> implements Marke
     @Override
     public List<Marker> getMarkCount(int examId) {
         List<Marker> list = new LinkedList<Marker>();
-        List<Object[]> result = libraryDao.countMarkerAndStatus(examId, LibraryStatus.MARKED,LibraryStatus.INSPECTED);
+        List<Object[]> result = libraryDao.countMarkerAndStatus(examId, LibraryStatus.MARKED, LibraryStatus.INSPECTED);
         if (result != null) {
             for (Object[] array : result) {
                 try {
@@ -171,7 +179,7 @@ public class MarkerServiceImpl extends BaseQueryService<Marker> implements Marke
     @Override
     public List<Marker> getMarkCount(int examId, String subjectCode) {
         List<Marker> list = new LinkedList<Marker>();
-        List<Object[]> result = libraryDao.countMarkerAndStatus(examId, subjectCode, LibraryStatus.MARKED,LibraryStatus.INSPECTED);
+        List<Object[]> result = libraryDao.countMarkerAndStatus(examId, subjectCode, LibraryStatus.MARKED, LibraryStatus.INSPECTED);
         if (result != null) {
             for (Object[] array : result) {
                 try {
@@ -217,8 +225,7 @@ public class MarkerServiceImpl extends BaseQueryService<Marker> implements Marke
                 if (StringUtils.isNotBlank(query.getName())) {
                     predicates.add(cb.equal(root.get("name"), query.getName()));
                 }
-                return predicates.isEmpty() ? cb.conjunction()
-                        : cb.and(predicates.toArray(new Predicate[predicates.size()]));
+                return predicates.isEmpty() ? cb.conjunction() : cb.and(predicates.toArray(new Predicate[predicates.size()]));
             }
         };
     }
@@ -243,4 +250,16 @@ public class MarkerServiceImpl extends BaseQueryService<Marker> implements Marke
     public List<Marker> findMode(String commo) {
         return markerDao.findByMode(commo);
     }
+
+    @Override
+    public List<Marker> findByAccountAndMarkStatus(AccountType accountType, Integer accountId, MarkStatus... status) {
+        return markerDao.findByAccountTypeAndAccountIdAndEnableAndMarkStatus(accountType, accountId, true, status);
+    }
+
+    @Override
+    public long countByExamAndSubjectAndGroupAndAccount(Integer examId, String subjectCode, Integer groupNumber, AccountType accountType,
+            Integer accountId) {
+        return markerDao.countByExamIdAndSubjectCodeAndGroupNumberAndAccountTypeAndAccountId(examId, subjectCode, groupNumber, accountType,
+                accountId);
+    }
 }

+ 8 - 10
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/lock/LockService.java

@@ -25,7 +25,7 @@ public class LockService implements InitializingBean, ApplicationContextAware {
 
     /**
      * 尝试获取排他锁,立即返回获取结果
-     * 
+     *
      * @param type
      * @param keys
      * @return
@@ -36,7 +36,7 @@ public class LockService implements InitializingBean, ApplicationContextAware {
 
     /**
      * 等待获取排他锁,线程阻塞直到成功获取
-     * 
+     *
      * @param type
      * @param keys
      */
@@ -45,9 +45,8 @@ public class LockService implements InitializingBean, ApplicationContextAware {
     }
 
     /**
-     * 
      * 释放已获取的排他锁
-     * 
+     *
      * @param type
      * @param keys
      */
@@ -57,7 +56,7 @@ public class LockService implements InitializingBean, ApplicationContextAware {
 
     /**
      * 检测排他锁是否已被获取
-     * 
+     *
      * @param type
      * @param keys
      * @return
@@ -68,23 +67,22 @@ public class LockService implements InitializingBean, ApplicationContextAware {
 
     /**
      * 等待获取可重入的读锁
-     * 
+     *
      * @param type
      * @param keys
      */
     public void watch(LockType type, Object... keys) {
-        provider.waitLock(type, getKeys(keys));
+        provider.watch(type, getKeys(keys));
     }
 
     /**
-     * 
      * 释放已获取的读锁
-     * 
+     *
      * @param type
      * @param keys
      */
     public void unwatch(LockType type, Object... keys) {
-        provider.unlock(type, getKeys(keys));
+        provider.unwatch(type, getKeys(keys));
     }
 
     /**

+ 37 - 36
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/MarkCronService.java

@@ -31,9 +31,8 @@ import cn.com.qmth.stmms.common.enums.MarkStatus;
 
 /**
  * 与评卷相关的所有定时任务
- * 
- * @author luoshi
  *
+ * @author luoshi
  */
 @Component
 public class MarkCronService {
@@ -130,11 +129,9 @@ public class MarkCronService {
         // 获取主观题总分大于0的科目
         List<ExamSubject> subjects = subjectService.list(examId, 0);
         for (ExamSubject subject : subjects) {
-            List<MarkGroup> groups = groupService.findByExamAndSubjectAndStatus(examId, subject.getCode(),
-                    MarkStatus.FORMAL);
+            List<MarkGroup> groups = groupService.findByExamAndSubjectAndStatus(examId, subject.getCode(), MarkStatus.FORMAL);
             for (MarkGroup markGroup : groups) {
-                List<Marker> markers = markerService.findByExamAndSubjectAndGroup(examId, subject.getCode(),
-                        markGroup.getNumber());
+                List<Marker> markers = markerService.findByExamAndSubjectAndGroup(examId, subject.getCode(), markGroup.getNumber());
                 for (Marker marker : markers) {
                     try {
                         markService.updateQuality(marker);
@@ -151,8 +148,7 @@ public class MarkCronService {
         List<ExamSubject> subjects = subjectService.listSubjectiveScore(examId, 0d);
         for (ExamSubject subject : subjects) {
             // 清除缺考考生和违纪考生
-            List<ExamStudent> list = studentService.findAbsentOrBreachLibraryStudent(subject.getExamId(),
-                    subject.getCode());
+            List<ExamStudent> list = studentService.findAbsentOrBreachLibraryStudent(subject.getExamId(), subject.getCode());
             if (list != null) {
                 for (ExamStudent student : list) {
                     try {
@@ -166,25 +162,26 @@ public class MarkCronService {
                 }
             }
             // 处理正常考生
-            // 生成正评任务
-            List<MarkGroup> groups = groupService.findByExamAndSubjectAndStatus(subject.getExamId(), subject.getCode(),
-                    MarkStatus.FORMAL);
-            for (MarkGroup group : groups) {
-                buildFormalLibrary(subject, campusMap, group);
-            }
             // 生成试评任务
-            groups = groupService.findByExamAndSubjectAndStatus(subject.getExamId(), subject.getCode(),
-                    MarkStatus.TRIAL);
+            List<MarkGroup> groups = groupService.findByExamAndSubjectAndStatus(subject.getExamId(), subject.getCode(), MarkStatus.TRIAL);
             for (MarkGroup group : groups) {
                 buildTrialLibrary(campusMap, group);
             }
+            // 生成正评任务
+            // 试评状态的分组也提前生成
+            groups = groupService
+                    .findByExamAndSubjectAndStatus(subject.getExamId(), subject.getCode(), MarkStatus.FORMAL, MarkStatus.TRIAL);
+            for (MarkGroup group : groups) {
+                buildFormalLibrary(subject, campusMap, group);
+            }
         }
     }
 
     private void buildFormalLibrary(ExamSubject subject, Map<String, Campus> campusMap, MarkGroup group) {
         Date lastBuildTime = group.getBuildTime();
-        ExamStudent student = studentService.findUnLibraryStudent(group.getExamId(), group.getSubjectCode(),
-                group.getNumber(), lastBuildTime);
+        int count = 0;
+        ExamStudent student = studentService
+                .findUnLibraryStudent(group.getExamId(), group.getSubjectCode(), group.getNumber(), lastBuildTime);
         while (student != null) {
             // 补充学习中心集合
             Campus campus = getCampus(student, campusMap);
@@ -196,35 +193,40 @@ public class MarkCronService {
             try {
                 lockService.watch(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
                 lockService.watch(LockType.STUDENT, student.getId());
-                // 上锁后重新验证状态
-                if (!groupService.validateStatus(group.getExamId(), group.getSubjectCode(), group.getNumber(),
-                        MarkStatus.FORMAL)) {
-                    return;
+                // 上锁后重复验证分组状态
+                if (!groupService.validateStatus(group.getExamId(), group.getSubjectCode(), group.getNumber(), MarkStatus.FORMAL,
+                        MarkStatus.TRIAL)) {
+                    break;
                 }
+                // 上锁后重复验证考生状态
                 if (studentService.validateStatus(student.getId())) {
                     markService.buildFormalLibrary(student, campus, group, subject);
-                    markService.updateLibraryCount(group);
-                    markService.updateMarkedCount(group);
                     lastBuildTime = student.getUploadTime();
+                    count++;
                 }
             } catch (Exception e) {
-                log.error("build formal library error for studentId=" + student.getId() + ", groupNumber="
-                        + group.getNumber(), e);
+                log.error("build formal library error for studentId=" + student.getId() + ", groupNumber=" + group.getNumber(), e);
             } finally {
                 lockService.unwatch(LockType.STUDENT, student.getId());
                 lockService.unwatch(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
             }
             // 取下一个考生
-            student = studentService.findUnLibraryStudent(group.getExamId(), group.getSubjectCode(), group.getNumber(),
-                    lastBuildTime);
+            student = studentService.findUnLibraryStudent(group.getExamId(), group.getSubjectCode(), group.getNumber(), lastBuildTime);
+        }
+        // 有新任务创建,同时正评状态,此时才需要更新任务数量
+        if (count > 0) {
+            group = groupService.findOne(group.getExamId(), group.getSubjectCode(), group.getNumber());
+            if (group.getStatus() == MarkStatus.FORMAL) {
+                markService.updateLibraryCount(group);
+                markService.updateMarkedCount(group);
+            }
         }
     }
 
     private void buildTrialLibrary(Map<String, Campus> campusMap, MarkGroup group) {
         while (group.getTrialCount() > group.getLibraryCount()) {
             // 随机取一个未生成试评任务的考生
-            ExamStudent student = studentService.randomUnTrialStudent(group.getExamId(), group.getSubjectCode(),
-                    group.getNumber());
+            ExamStudent student = studentService.randomUnTrialStudent(group.getExamId(), group.getSubjectCode(), group.getNumber());
             if (student == null) {
                 return;
             }
@@ -238,19 +240,18 @@ public class MarkCronService {
             try {
                 lockService.watch(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
                 lockService.watch(LockType.STUDENT, student.getId());
-                // 上锁后重新验证状态
-                if (!groupService.validateStatus(group.getExamId(), group.getSubjectCode(), group.getNumber(),
-                        MarkStatus.TRIAL)) {
-                    return;
+                // 上锁后重新验证分组状态
+                if (!groupService.validateStatus(group.getExamId(), group.getSubjectCode(), group.getNumber(), MarkStatus.TRIAL)) {
+                    break;
                 }
+                // 上锁后重复验证考生状态
                 if (studentService.validateStatus(student.getId())) {
                     markService.buildTrialLibrary(student, campus, group);
                     markService.updateLibraryCount(group);
                     markService.updateMarkedCount(group);
                 }
             } catch (Exception e) {
-                log.error("build trial library error for studentId=" + student.getId() + ", groupNumber="
-                        + group.getNumber(), e);
+                log.error("build trial library error for studentId=" + student.getId() + ", groupNumber=" + group.getNumber(), e);
             } finally {
                 lockService.unwatch(LockType.STUDENT, student.getId());
                 lockService.unwatch(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());

+ 11 - 10
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/school/dao/SchoolDao.java

@@ -1,10 +1,11 @@
-package cn.com.qmth.stmms.biz.school.dao;
-
-import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
-import org.springframework.data.repository.PagingAndSortingRepository;
-
-import cn.com.qmth.stmms.biz.school.model.School;
-
-public interface SchoolDao extends PagingAndSortingRepository<School, Integer>,
-        JpaSpecificationExecutor<School> {
-}
+package cn.com.qmth.stmms.biz.school.dao;
+
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.repository.PagingAndSortingRepository;
+
+import cn.com.qmth.stmms.biz.school.model.School;
+
+public interface SchoolDao extends PagingAndSortingRepository<School, Integer>, JpaSpecificationExecutor<School> {
+
+    School findFirstByAccessKey(String accessKey);
+}

+ 193 - 160
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/school/model/School.java

@@ -1,160 +1,193 @@
-package cn.com.qmth.stmms.biz.school.model;
-
-import java.io.Serializable;
-import java.util.Date;
-
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.GeneratedValue;
-import javax.persistence.Id;
-import javax.persistence.Table;
-
-@Entity
-@Table(name = "b_school")
-public class School implements Serializable {
-
-    private static final long serialVersionUID = -241744624240739912L;
-
-    /**
-     * 主键
-     */
-    @Id
-    @GeneratedValue
-    private Integer id;
-
-    /**
-     * 名称
-     */
-    private String name;
-
-    /**
-     * 省份
-     */
-    private String province;
-
-    /**
-     * 城市
-     */
-    private String city;
-
-    /**
-     * 地址
-     */
-    private String address;
-
-    /**
-     * 电话
-     */
-    private String phone;
-
-    /**
-     * 图片地址、名称
-     */
-    @Column(name = "logo_url")
-    private String logoUrl;
-
-    /**
-     * 描述
-     */
-    private String description;
-
-    /**
-     * 是否启用
-     */
-    private boolean enable;
-
-    @Column(name = "time_create")
-    private Date timeCreate;
-
-    @Column(name = "time_modified")
-    private Date timeModified;
-
-    public Integer getId() {
-        return id;
-    }
-
-    public void setId(Integer id) {
-        this.id = id;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public void setName(String name) {
-        this.name = name;
-    }
-
-    public String getProvince() {
-        return province;
-    }
-
-    public void setProvince(String province) {
-        this.province = province;
-    }
-
-    public String getCity() {
-        return city;
-    }
-
-    public void setCity(String city) {
-        this.city = city;
-    }
-
-    public String getAddress() {
-        return address;
-    }
-
-    public void setAddress(String address) {
-        this.address = address;
-    }
-
-    public String getPhone() {
-        return phone;
-    }
-
-    public void setPhone(String phone) {
-        this.phone = phone;
-    }
-
-    public String getLogoUrl() {
-        return logoUrl;
-    }
-
-    public void setLogoUrl(String logoUrl) {
-        this.logoUrl = logoUrl;
-    }
-
-    public String getDescription() {
-        return description;
-    }
-
-    public void setDescription(String description) {
-        this.description = description;
-    }
-
-    public boolean isEnable() {
-        return enable;
-    }
-
-    public void setEnable(boolean enable) {
-        this.enable = enable;
-    }
-
-    public Date getTimeCreate() {
-        return timeCreate;
-    }
-
-    public void setTimeCreate(Date timeCreate) {
-        this.timeCreate = timeCreate;
-    }
-
-    public Date getTimeModified() {
-        return timeModified;
-    }
-
-    public void setTimeModified(Date timeModified) {
-        this.timeModified = timeModified;
-    }
-
-}
+package cn.com.qmth.stmms.biz.school.model;
+
+import cn.com.qmth.stmms.common.utils.AccessControlUtils;
+import org.apache.commons.lang3.RandomStringUtils;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.UUID;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "b_school")
+public class School implements Serializable {
+
+    private static final int ACCESS_SECRET_LENGTH = 24;
+
+    private static final long serialVersionUID = -241744624240739912L;
+
+    /**
+     * 主键
+     */
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    /**
+     * 名称
+     */
+    private String name;
+
+    /**
+     * 省份
+     */
+    private String province;
+
+    /**
+     * 城市
+     */
+    private String city;
+
+    /**
+     * 地址
+     */
+    private String address;
+
+    /**
+     * 电话
+     */
+    private String phone;
+
+    /**
+     * 图片地址、名称
+     */
+    @Column(name = "logo_url")
+    private String logoUrl;
+
+    /**
+     * 描述
+     */
+    private String description;
+
+    /**
+     * 是否启用
+     */
+    private boolean enable;
+
+    @Column(name = "access_key", nullable = true, length = 64)
+    private String accessKey;
+
+    @Column(name = "access_secret", nullable = true, length = 64)
+    private String accessSecret;
+
+    @Column(name = "time_create")
+    private Date timeCreate;
+
+    @Column(name = "time_modified")
+    private Date timeModified;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getProvince() {
+        return province;
+    }
+
+    public void setProvince(String province) {
+        this.province = province;
+    }
+
+    public String getCity() {
+        return city;
+    }
+
+    public void setCity(String city) {
+        this.city = city;
+    }
+
+    public String getAddress() {
+        return address;
+    }
+
+    public void setAddress(String address) {
+        this.address = address;
+    }
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+
+    public String getLogoUrl() {
+        return logoUrl;
+    }
+
+    public void setLogoUrl(String logoUrl) {
+        this.logoUrl = logoUrl;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public boolean isEnable() {
+        return enable;
+    }
+
+    public void setEnable(boolean enable) {
+        this.enable = enable;
+    }
+
+    public String getAccessKey() {
+        return accessKey;
+    }
+
+    public void setAccessKey(String accessKey) {
+        this.accessKey = accessKey;
+    }
+
+    public String getAccessSecret() {
+        return accessSecret;
+    }
+
+    public void setAccessSecret(String accessSecret) {
+        this.accessSecret = accessSecret;
+    }
+
+    public Date getTimeCreate() {
+        return timeCreate;
+    }
+
+    public void setTimeCreate(Date timeCreate) {
+        this.timeCreate = timeCreate;
+    }
+
+    public Date getTimeModified() {
+        return timeModified;
+    }
+
+    public void setTimeModified(Date timeModified) {
+        this.timeModified = timeModified;
+    }
+
+    public void resetAccessKeyAndSecret() {
+        accessKey = AccessControlUtils.uuidString();
+        accessSecret = AccessControlUtils.randomString(ACCESS_SECRET_LENGTH);
+    }
+
+}

+ 23 - 18
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/school/service/SchoolService.java

@@ -1,18 +1,23 @@
-package cn.com.qmth.stmms.biz.school.service;
-
-import cn.com.qmth.stmms.biz.school.model.School;
-import cn.com.qmth.stmms.biz.school.query.SchoolSearchQuery;
-
-public interface SchoolService {
-
-    School save(School school);
-
-    School findById(Integer id);
-
-    public SchoolSearchQuery findByQuery(final SchoolSearchQuery query);
-
-    void deleteById(Integer id);
-
-    void delete(School school);
-
-}
+package cn.com.qmth.stmms.biz.school.service;
+
+import cn.com.qmth.stmms.biz.school.model.School;
+import cn.com.qmth.stmms.biz.school.query.SchoolSearchQuery;
+import org.springframework.transaction.annotation.Transactional;
+
+public interface SchoolService {
+
+    School save(School school);
+
+    School findById(Integer id);
+
+    School findByAccessKey(String accessKey);
+
+    public SchoolSearchQuery findByQuery(final SchoolSearchQuery query);
+
+    void deleteById(Integer id);
+
+    void delete(School school);
+
+    School resetAccessKeyAndSecret(School school);
+
+}

+ 16 - 2
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/school/service/impl/SchoolServiceImpl.java

@@ -33,6 +33,11 @@ public class SchoolServiceImpl extends BaseQueryService<School> implements Schoo
         return schoolDao.findOne(id);
     }
 
+    @Override
+    public School findByAccessKey(String accessKey) {
+        return schoolDao.findFirstByAccessKey(accessKey);
+    }
+
     public SchoolSearchQuery findByQuery(final SchoolSearchQuery query) {
         checkQuery(query);
         Page<School> result = schoolDao.findAll(new Specification<School>() {
@@ -49,8 +54,7 @@ public class SchoolServiceImpl extends BaseQueryService<School> implements Schoo
                 if (StringUtils.isNotBlank(query.getCity())) {
                     predicates.add(cb.equal(root.get("city"), query.getCity()));
                 }
-                return predicates.isEmpty() ? cb.conjunction()
-                        : cb.and(predicates.toArray(new Predicate[predicates.size()]));
+                return predicates.isEmpty() ? cb.conjunction() : cb.and(predicates.toArray(new Predicate[predicates.size()]));
             }
 
         }, query);
@@ -77,4 +81,14 @@ public class SchoolServiceImpl extends BaseQueryService<School> implements Schoo
     public void delete(School school) {
         schoolDao.delete(school);
     }
+
+    @Transactional
+    public School resetAccessKeyAndSecret(School school) {
+        while (true) {
+            school.resetAccessKeyAndSecret();
+            if (schoolDao.findFirstByAccessKey(school.getAccessKey()) == null) {
+                return schoolDao.save(school);
+            }
+        }
+    }
 }

+ 23 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/user/dao/OpenAccountDao.java

@@ -0,0 +1,23 @@
+package cn.com.qmth.stmms.biz.user.dao;
+
+import cn.com.qmth.stmms.biz.user.model.OpenAccount;
+import cn.com.qmth.stmms.biz.user.model.User;
+import cn.com.qmth.stmms.common.enums.Role;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.PagingAndSortingRepository;
+
+import java.util.List;
+import java.util.Set;
+
+public interface OpenAccountDao extends PagingAndSortingRepository<OpenAccount, Integer>, JpaSpecificationExecutor<OpenAccount> {
+
+    List<OpenAccount> findBySchoolIdAndEnable(Integer schoolId, boolean enable);
+
+    @Query("select o from OpenAccount o where o.schoolId=?1 and o.enable=?2 and not exists "
+            + "(select m.id from Marker m where m.examId=?3 and m.subjectCode=?4 and m.groupNumber=?5 and m.openAccountId=o.id)")
+    List<OpenAccount> findUnbind(Integer schoolId, boolean enable, Integer examId, String subjectCode, Integer groupNumber);
+
+    OpenAccount findFirstBySchoolIdAndAccount(Integer schoolId, String account);
+
+}

+ 125 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/user/model/OpenAccount.java

@@ -0,0 +1,125 @@
+package cn.com.qmth.stmms.biz.user.model;
+
+import cn.com.qmth.stmms.common.utils.AccessControlUtils;
+
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.Date;
+
+@Entity
+@Table(name = "b_open_account")
+public class OpenAccount implements Serializable {
+
+    private static final long serialVersionUID = 6570825224244775263L;
+
+    /**
+     * 主键
+     */
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    /**
+     * 所属学校ID
+     */
+    @Column(name = "school_id", nullable = false)
+    private Integer schoolId;
+
+    /**
+     * 登录名
+     */
+    @Column(name = "account", length = 64, nullable = false)
+    private String account;
+
+    /**
+     * 名称
+     */
+    @Column(name = "name", length = 64, nullable = false)
+    private String name;
+
+    /**
+     * 状态
+     */
+    @Column(name = "enable", length = 1, nullable = false, columnDefinition = "tinyint")
+    private boolean enable;
+
+    /**
+     * 访问令牌
+     */
+    @Column(name = "access_token", length = 64, nullable = true)
+    private String accessToken;
+
+    /**
+     * 令牌刷新时间
+     */
+    @Column(name = "access_token_refresh_time", nullable = true, columnDefinition = "datetime")
+    private Date accessTokenRefreshTime;
+
+    /**
+     * 账号首次创建时间
+     */
+    @Column(name = "create_time", nullable = false, columnDefinition = "datetime")
+    private Date createTime;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Integer getSchoolId() {
+        return schoolId;
+    }
+
+    public void setSchoolId(Integer schoolId) {
+        this.schoolId = schoolId;
+    }
+
+    public String getAccount() {
+        return account;
+    }
+
+    public void setAccount(String account) {
+        this.account = account;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public boolean isEnable() {
+        return enable;
+    }
+
+    public void setEnable(boolean enable) {
+        this.enable = enable;
+    }
+
+    public String getAccessToken() {
+        return accessToken;
+    }
+
+    public Date getAccessTokenRefreshTime() {
+        return accessTokenRefreshTime;
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    public void refreshAccessToken() {
+        this.accessToken = AccessControlUtils.randomString();
+        this.accessTokenRefreshTime = new Date();
+    }
+
+}

+ 213 - 200
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/user/model/User.java

@@ -1,200 +1,213 @@
-package cn.com.qmth.stmms.biz.user.model;
-
-import java.io.Serializable;
-import java.util.Date;
-
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.EnumType;
-import javax.persistence.Enumerated;
-import javax.persistence.GeneratedValue;
-import javax.persistence.Id;
-import javax.persistence.Table;
-
-import cn.com.qmth.stmms.common.enums.Role;
-
-@Entity
-@Table(name = "b_user")
-public class User implements Serializable {
-
-    private static final long serialVersionUID = -2094897723165518198L;
-
-    /**
-     * 主键
-     */
-    @Id
-    @GeneratedValue
-    private Integer id;
-
-    /**
-     * 登录名
-     */
-    @Column(name = "login_name")
-    private String loginName;
-
-    /**
-     * 名称
-     */
-    @Column(name = "name")
-    private String name;
-
-    /**
-     * 密码
-     */
-    @Column(name = "password")
-    private String password;
-
-    /**
-     * 角色
-     */
-    @Column(name = "role", length = 16, nullable = false)
-    @Enumerated(EnumType.STRING)
-    private Role role;
-
-    /**
-     * 状态
-     */
-    @Column(name = "enable")
-    private boolean enable;
-
-    /**
-     * 所属学校ID
-     */
-    @Column(name = "school_id")
-    private Integer schoolId;
-
-    /**
-     * 绑定科目代码
-     */
-    @Column(name = "subject_code")
-    private String subjectCode;
-
-    /**
-     * 最后一次登录时间
-     */
-    @Column(name = "last_login_time")
-    private Date lastLoginTime;
-
-    /**
-     * 最后一次登录IP
-     */
-    @Column(name = "last_login_ip")
-    private String lastLoginIp;
-
-    @Column(name = "created_time", nullable = false)
-    private Date createdTime;
-
-    @Column(name = "updated_time")
-    private Date updatedTime;
-
-    /**
-     * 登录token
-     */
-    @Column(name = "web_token")
-    private String webToken;
-
-    public Integer getId() {
-        return id;
-    }
-
-    public void setId(Integer id) {
-        this.id = id;
-    }
-
-    public String getLoginName() {
-        return loginName;
-    }
-
-    public void setLoginName(String loginName) {
-        this.loginName = loginName;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public void setName(String name) {
-        this.name = name;
-    }
-
-    public String getPassword() {
-        return password;
-    }
-
-    public void setPassword(String password) {
-        this.password = password;
-    }
-
-    public Role getRole() {
-        return role;
-    }
-
-    public void setRole(Role role) {
-        this.role = role;
-    }
-
-    public Integer getSchoolId() {
-        return schoolId;
-    }
-
-    public void setSchoolId(Integer schoolId) {
-        this.schoolId = schoolId;
-    }
-
-    public Date getLastLoginTime() {
-        return lastLoginTime;
-    }
-
-    public void setLastLoginTime(Date lastLoginTime) {
-        this.lastLoginTime = lastLoginTime;
-    }
-
-    public String getLastLoginIp() {
-        return lastLoginIp;
-    }
-
-    public void setLastLoginIp(String lastLoginIp) {
-        this.lastLoginIp = lastLoginIp;
-    }
-
-    public Date getCreatedTime() {
-        return createdTime;
-    }
-
-    public void setCreatedTime(Date createdTime) {
-        this.createdTime = createdTime;
-    }
-
-    public Date getUpdatedTime() {
-        return updatedTime;
-    }
-
-    public void setUpdatedTime(Date updatedTime) {
-        this.updatedTime = updatedTime;
-    }
-
-    public String getSubjectCode() {
-        return subjectCode;
-    }
-
-    public void setSubjectCode(String subjectCode) {
-        this.subjectCode = subjectCode;
-    }
-
-    public boolean isEnable() {
-        return enable;
-    }
-
-    public void setEnable(boolean enable) {
-        this.enable = enable;
-    }
-
-    public String getWebToken() {
-        return webToken;
-    }
-
-    public void setWebToken(String webToken) {
-        this.webToken = webToken;
-    }
-
-}
+package cn.com.qmth.stmms.biz.user.model;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+import cn.com.qmth.stmms.common.enums.Role;
+import cn.com.qmth.stmms.common.utils.AccessControlUtils;
+
+@Entity
+@Table(name = "b_user")
+public class User implements Serializable {
+
+    private static final long serialVersionUID = -2094897723165518198L;
+
+    public static final int ACCESS_TOKEN_EXPIRE_SECOND = 3600 * 24;
+
+    /**
+     * 主键
+     */
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    /**
+     * 登录名
+     */
+    @Column(name = "login_name")
+    private String loginName;
+
+    /**
+     * 名称
+     */
+    @Column(name = "name")
+    private String name;
+
+    /**
+     * 密码
+     */
+    @Column(name = "password")
+    private String password;
+
+    /**
+     * 角色
+     */
+    @Column(name = "role", length = 16, nullable = false)
+    @Enumerated(EnumType.STRING)
+    private Role role;
+
+    /**
+     * 状态
+     */
+    @Column(name = "enable")
+    private boolean enable;
+
+    /**
+     * 所属学校ID
+     */
+    @Column(name = "school_id")
+    private Integer schoolId;
+
+    /**
+     * 绑定科目代码
+     */
+    @Column(name = "subject_code")
+    private String subjectCode;
+
+    /**
+     * 最后一次登录时间
+     */
+    @Column(name = "last_login_time")
+    private Date lastLoginTime;
+
+    /**
+     * 最后一次登录IP
+     */
+    @Column(name = "last_login_ip")
+    private String lastLoginIp;
+
+    @Column(name = "created_time", nullable = false)
+    private Date createdTime;
+
+    @Column(name = "updated_time")
+    private Date updatedTime;
+
+    /**
+     * 访问token
+     */
+    @Column(name = "access_token", nullable = true, length = 64)
+    private String accessToken;
+
+    /**
+     * 访问token的刷新时间
+     */
+    @Column(name = "access_token_refresh_time", nullable = true)
+    private Date accessTokenRefreshTime;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getLoginName() {
+        return loginName;
+    }
+
+    public void setLoginName(String loginName) {
+        this.loginName = loginName;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public Role getRole() {
+        return role;
+    }
+
+    public void setRole(Role role) {
+        this.role = role;
+    }
+
+    public Integer getSchoolId() {
+        return schoolId;
+    }
+
+    public void setSchoolId(Integer schoolId) {
+        this.schoolId = schoolId;
+    }
+
+    public Date getLastLoginTime() {
+        return lastLoginTime;
+    }
+
+    public void setLastLoginTime(Date lastLoginTime) {
+        this.lastLoginTime = lastLoginTime;
+    }
+
+    public String getLastLoginIp() {
+        return lastLoginIp;
+    }
+
+    public void setLastLoginIp(String lastLoginIp) {
+        this.lastLoginIp = lastLoginIp;
+    }
+
+    public Date getCreatedTime() {
+        return createdTime;
+    }
+
+    public void setCreatedTime(Date createdTime) {
+        this.createdTime = createdTime;
+    }
+
+    public Date getUpdatedTime() {
+        return updatedTime;
+    }
+
+    public void setUpdatedTime(Date updatedTime) {
+        this.updatedTime = updatedTime;
+    }
+
+    public String getSubjectCode() {
+        return subjectCode;
+    }
+
+    public void setSubjectCode(String subjectCode) {
+        this.subjectCode = subjectCode;
+    }
+
+    public boolean isEnable() {
+        return enable;
+    }
+
+    public void setEnable(boolean enable) {
+        this.enable = enable;
+    }
+
+    public String getAccessToken() {
+        return accessToken;
+    }
+
+    public Date getAccessTokenRefreshTime() {
+        return accessTokenRefreshTime;
+    }
+
+    public void refreshAccessToken() {
+        this.accessToken = AccessControlUtils.randomString();
+        this.accessTokenRefreshTime = new Date();
+    }
+}

+ 47 - 34
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/user/service/UserService.java

@@ -1,34 +1,47 @@
-package cn.com.qmth.stmms.biz.user.service;
-
-import java.util.List;
-import java.util.Set;
-
-import cn.com.qmth.stmms.biz.user.model.User;
-import cn.com.qmth.stmms.biz.user.service.query.UserSearchQuery;
-import cn.com.qmth.stmms.common.enums.Role;
-
-public interface UserService {
-
-    User findByLoginName(String loginName);
-
-    List<User> findByLoginNameAndPassword(String loginName, String password);
-
-    User findById(Integer id);
-
-    public List<User> findBySchoolIdAndRole(int schoolId, Role role);
-
-    public List<User> findBySchoolIdAndRoleSet(int schoolId, Set<Role> roleSet);
-
-    public UserSearchQuery findByQuery(final UserSearchQuery query);
-
-    public User save(User user);
-
-    public void deleteById(int id);
-
-    public void delete(User user);
-
-    public User findSchoolAdmin(int schoolId);
-
-	public User findSchoolViewer(Integer schoolId);
-
-}
+package cn.com.qmth.stmms.biz.user.service;
+
+import java.util.List;
+import java.util.Set;
+
+import cn.com.qmth.stmms.biz.user.model.OpenAccount;
+import cn.com.qmth.stmms.biz.user.model.User;
+import cn.com.qmth.stmms.biz.user.service.query.UserSearchQuery;
+import cn.com.qmth.stmms.common.enums.Role;
+
+public interface UserService {
+
+    OpenAccount findOpenAccount(Integer id);
+
+    List<OpenAccount> listOpenAccount(Integer schoolId);
+
+    List<OpenAccount> listUnbindOpenAccount(Integer schoolId, Integer examId, String subjectCode, Integer groupNumber);
+
+    OpenAccount openAccountLogin(Integer schoolId, String account);
+
+    OpenAccount saveOpenAccount(Integer schoolId, String account, String name, boolean enable);
+
+    User login(String loginName, String password, String ip);
+
+    User findByLoginName(String loginName);
+
+    List<User> findByLoginNameAndPassword(String loginName, String password);
+
+    User findById(Integer id);
+
+    List<User> findBySchoolIdAndRole(int schoolId, Role role);
+
+    List<User> findBySchoolIdAndRoleSet(int schoolId, Set<Role> roleSet);
+
+    UserSearchQuery findByQuery(final UserSearchQuery query);
+
+    User save(User user);
+
+    void deleteById(int id);
+
+    void delete(User user);
+
+    User findSchoolAdmin(int schoolId);
+
+    User findSchoolViewer(Integer schoolId);
+
+}

+ 217 - 162
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/user/service/impl/UserServiceImpl.java

@@ -1,162 +1,217 @@
-package cn.com.qmth.stmms.biz.user.service.impl;
-
-import java.util.Date;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-
-import javax.persistence.criteria.CriteriaBuilder;
-import javax.persistence.criteria.CriteriaQuery;
-import javax.persistence.criteria.Predicate;
-import javax.persistence.criteria.Root;
-
-import org.apache.commons.lang.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.domain.Page;
-import org.springframework.data.jpa.domain.Specification;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import cn.com.qmth.stmms.biz.user.dao.UserDao;
-import cn.com.qmth.stmms.biz.user.model.User;
-import cn.com.qmth.stmms.biz.user.service.UserService;
-import cn.com.qmth.stmms.biz.user.service.query.UserSearchQuery;
-import cn.com.qmth.stmms.common.enums.Role;
-
-/**
- * 用户Service
- * 
- * @author LS
- * 
- */
-@Service("userService")
-public class UserServiceImpl implements UserService {
-
-    @Autowired
-    private UserDao userDao;
-
-    @Override
-    public User findByLoginName(String loginName) {
-        List<User> list = userDao.findByLoginName(loginName);
-        return list != null && list.size() > 0 ? list.get(0) : null;
-    }
-
-    @Override
-    public List<User> findByLoginNameAndPassword(String loginName, String password) {
-        return userDao.findByLoginNameAndPassword(loginName, password);
-    }
-
-    public List<User> findBySchoolIdAndRole(int schoolId, Role role) {
-        return userDao.findBySchoolIdAndRole(schoolId, role);
-    }
-
-    public List<User> findBySchoolIdAndRoleSet(int schoolId, Set<Role> roleSet) {
-        return userDao.findBySchoolIdAndRoleIn(schoolId, roleSet);
-    }
-
-    public User findById(Integer id) {
-        return userDao.findOne(id);
-    }
-
-    public User findSchoolAdmin(int schoolId) {
-        UserSearchQuery query = new UserSearchQuery();
-        query.setSchoolId(schoolId);
-        query.addIncludeRole(Role.SCHOOL_ADMIN);
-        query.setPageNumber(1);
-        query.setPageSize(1);
-        query = findByQuery(query);
-        if (query.getCurrentCount() > 0) {
-            return query.getResult().get(0);
-        } else {
-            return null;
-        }
-    }
-
-    public UserSearchQuery findByQuery(final UserSearchQuery query) {
-        if (query.getPageNumber() < 1) {
-            query.setPageNumber(1);
-        }
-        if (query.getPageSize() < 1) {
-            query.setPageSize(10);
-        }
-
-        Page<User> result = userDao.findAll(new Specification<User>() {
-
-            @Override
-            public Predicate toPredicate(Root<User> root, CriteriaQuery<?> cQuery, CriteriaBuilder cb) {
-                List<Predicate> predicates = new LinkedList<Predicate>();
-                if (query.getSchoolId() > 0) {
-                    predicates.add(cb.equal(root.get("schoolId"), query.getSchoolId()));
-                }
-                if (StringUtils.isNotBlank(query.getLoginName())) {
-                    predicates.add(cb.like(root.get("loginName").as(String.class), query.getLoginName() + "%"));
-                }
-                if (StringUtils.isNotBlank(query.getName())) {
-                    predicates.add(cb.like(root.get("name").as(String.class), query.getName() + "%"));
-                }
-                if (query.getEnable() != null) {
-                    predicates.add(cb.equal(root.get("enable"), query.getEnable()));
-                }
-                if (query.getRole() != null) {
-                    predicates.add(cb.equal(root.get("role"), query.getRole()));
-                }
-                if (query.getIncludeRoles() != null && query.getIncludeRoles().size() > 0) {
-                    for (Role role : query.getIncludeRoles()) {
-                        predicates.add(cb.equal(root.get("role"), role));
-                    }
-                }
-                return predicates.isEmpty() ? cb.conjunction()
-                        : cb.and(predicates.toArray(new Predicate[predicates.size()]));
-            }
-        }, query);
-        if (result != null) {
-            query.setTotalCount(result.getTotalElements());
-            query.setTotalPage(result.getTotalPages());
-            query.setResult(result.getContent());
-            query.setCurrentCount(result.getNumberOfElements());
-        } else {
-            query.setTotalCount(0);
-            query.setTotalPage(0);
-            query.setCurrentCount(0);
-        }
-        return query;
-    }
-
-    @Transactional
-    public User save(User user) {
-        if (user.getId() != null && user.getId() > 0) {
-            user.setUpdatedTime(new Date());
-        } else {
-            user.setCreatedTime(new Date());
-        }
-        return userDao.save(user);
-    }
-
-    @Transactional
-    public void deleteById(int id) {
-        userDao.delete(id);
-    }
-
-    @Transactional
-    public void delete(User user) {
-        if (user != null) {
-            userDao.delete(user);
-        }
-    }
-
-	@Override
-	public User findSchoolViewer(Integer schoolId) {
-        UserSearchQuery query = new UserSearchQuery();
-        query.setSchoolId(schoolId);
-        query.addIncludeRole(Role.SCHOOL_VIEWER);
-        query.setPageNumber(1);
-        query.setPageSize(1);
-        query = findByQuery(query);
-        if (query.getCurrentCount() > 0) {
-            return query.getResult().get(0);
-        } else {
-            return null;
-        }
-	}
-
-}
+package cn.com.qmth.stmms.biz.user.service.impl;
+
+import java.util.*;
+
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import cn.com.qmth.stmms.biz.user.dao.OpenAccountDao;
+import cn.com.qmth.stmms.biz.user.model.OpenAccount;
+import cn.com.qmth.stmms.common.utils.Md5EncryptUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import cn.com.qmth.stmms.biz.user.dao.UserDao;
+import cn.com.qmth.stmms.biz.user.model.User;
+import cn.com.qmth.stmms.biz.user.service.UserService;
+import cn.com.qmth.stmms.biz.user.service.query.UserSearchQuery;
+import cn.com.qmth.stmms.common.enums.Role;
+
+/**
+ * 用户Service
+ *
+ * @author LS
+ */
+@Service("userService")
+public class UserServiceImpl implements UserService {
+
+    @Autowired
+    private UserDao userDao;
+
+    @Autowired
+    private OpenAccountDao openAccountDao;
+
+    @Override
+    public User findByLoginName(String loginName) {
+        List<User> list = userDao.findByLoginName(loginName);
+        return list != null && list.size() > 0 ? list.get(0) : null;
+    }
+
+    @Override
+    public List<User> findByLoginNameAndPassword(String loginName, String password) {
+        return userDao.findByLoginNameAndPassword(loginName, password);
+    }
+
+    public List<User> findBySchoolIdAndRole(int schoolId, Role role) {
+        return userDao.findBySchoolIdAndRole(schoolId, role);
+    }
+
+    public List<User> findBySchoolIdAndRoleSet(int schoolId, Set<Role> roleSet) {
+        return userDao.findBySchoolIdAndRoleIn(schoolId, roleSet);
+    }
+
+    public User findById(Integer id) {
+        return userDao.findOne(id);
+    }
+
+    public User findSchoolAdmin(int schoolId) {
+        UserSearchQuery query = new UserSearchQuery();
+        query.setSchoolId(schoolId);
+        query.addIncludeRole(Role.SCHOOL_ADMIN);
+        query.setPageNumber(1);
+        query.setPageSize(1);
+        query = findByQuery(query);
+        if (query.getCurrentCount() > 0) {
+            return query.getResult().get(0);
+        } else {
+            return null;
+        }
+    }
+
+    public UserSearchQuery findByQuery(final UserSearchQuery query) {
+        if (query.getPageNumber() < 1) {
+            query.setPageNumber(1);
+        }
+        if (query.getPageSize() < 1) {
+            query.setPageSize(10);
+        }
+
+        Page<User> result = userDao.findAll(new Specification<User>() {
+
+            @Override
+            public Predicate toPredicate(Root<User> root, CriteriaQuery<?> cQuery, CriteriaBuilder cb) {
+                List<Predicate> predicates = new LinkedList<Predicate>();
+                if (query.getSchoolId() > 0) {
+                    predicates.add(cb.equal(root.get("schoolId"), query.getSchoolId()));
+                }
+                if (StringUtils.isNotBlank(query.getLoginName())) {
+                    predicates.add(cb.like(root.get("loginName").as(String.class), query.getLoginName() + "%"));
+                }
+                if (StringUtils.isNotBlank(query.getName())) {
+                    predicates.add(cb.like(root.get("name").as(String.class), query.getName() + "%"));
+                }
+                if (query.getEnable() != null) {
+                    predicates.add(cb.equal(root.get("enable"), query.getEnable()));
+                }
+                if (query.getRole() != null) {
+                    predicates.add(cb.equal(root.get("role"), query.getRole()));
+                }
+                if (query.getIncludeRoles() != null && query.getIncludeRoles().size() > 0) {
+                    for (Role role : query.getIncludeRoles()) {
+                        predicates.add(cb.equal(root.get("role"), role));
+                    }
+                }
+                return predicates.isEmpty() ? cb.conjunction() : cb.and(predicates.toArray(new Predicate[predicates.size()]));
+            }
+        }, query);
+        if (result != null) {
+            query.setTotalCount(result.getTotalElements());
+            query.setTotalPage(result.getTotalPages());
+            query.setResult(result.getContent());
+            query.setCurrentCount(result.getNumberOfElements());
+        } else {
+            query.setTotalCount(0);
+            query.setTotalPage(0);
+            query.setCurrentCount(0);
+        }
+        return query;
+    }
+
+    @Transactional
+    public User save(User user) {
+        if (user.getId() != null && user.getId() > 0) {
+            user.setUpdatedTime(new Date());
+        } else {
+            user.setCreatedTime(new Date());
+        }
+        return userDao.save(user);
+    }
+
+    @Transactional
+    public void deleteById(int id) {
+        userDao.delete(id);
+    }
+
+    @Transactional
+    public void delete(User user) {
+        if (user != null) {
+            userDao.delete(user);
+        }
+    }
+
+    @Override
+    public User findSchoolViewer(Integer schoolId) {
+        UserSearchQuery query = new UserSearchQuery();
+        query.setSchoolId(schoolId);
+        query.addIncludeRole(Role.SCHOOL_VIEWER);
+        query.setPageNumber(1);
+        query.setPageSize(1);
+        query = findByQuery(query);
+        if (query.getCurrentCount() > 0) {
+            return query.getResult().get(0);
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    @Transactional
+    public User login(String loginName, String password, String ip) {
+        User user = findByLoginName(loginName);
+        if (user != null && user.getPassword().equals(Md5EncryptUtils.md5(password))) {
+            user.setLastLoginIp(StringUtils.trimToEmpty(ip));
+            user.setLastLoginTime(new Date());
+            user.refreshAccessToken();
+            return userDao.save(user);
+        }
+        return null;
+    }
+
+    @Override
+    @Transactional
+    public OpenAccount openAccountLogin(Integer schoolId, String account) {
+        OpenAccount oa = openAccountDao.findFirstBySchoolIdAndAccount(schoolId, account);
+        if (oa != null) {
+            oa.refreshAccessToken();
+            return openAccountDao.save(oa);
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    @Transactional
+    public OpenAccount saveOpenAccount(Integer schoolId, String account, String name, boolean enable) {
+        OpenAccount oa = openAccountDao.findFirstBySchoolIdAndAccount(schoolId, account);
+        if (oa == null) {
+            oa = new OpenAccount();
+            oa.setSchoolId(schoolId);
+            oa.setAccount(account);
+            oa.setCreateTime(new Date());
+        }
+        oa.setName(name);
+        oa.setEnable(enable);
+        return openAccountDao.save(oa);
+    }
+
+    @Override
+    public OpenAccount findOpenAccount(Integer id) {
+        return openAccountDao.findOne(id);
+    }
+
+    @Override
+    public List<OpenAccount> listOpenAccount(Integer schoolId) {
+        return openAccountDao.findBySchoolIdAndEnable(schoolId, true);
+    }
+
+    @Override
+    public List<OpenAccount> listUnbindOpenAccount(Integer schoolId, Integer examId, String subjectCode, Integer groupNumber) {
+        return openAccountDao.findUnbind(schoolId, true, examId, subjectCode, groupNumber);
+    }
+}

+ 47 - 45
stmms-biz/src/main/resources/service-context.xml

@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <beans xmlns="http://www.springframework.org/schema/beans"
-	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
-	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jpa="http://www.springframework.org/schema/data/jpa"
-	xmlns:task="http://www.springframework.org/schema/task"
-	xsi:schemaLocation="
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
+       xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jpa="http://www.springframework.org/schema/data/jpa"
+       xmlns:task="http://www.springframework.org/schema/task"
+       xsi:schemaLocation="
 http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
 http://www.springframework.org/schema/context
@@ -15,52 +15,54 @@ http://www.springframework.org/schema/data/jpa/spring-jpa-1.2.xsd
 http://www.springframework.org/schema/task 
 http://www.springframework.org/schema/task/spring-task.xsd
 ">
-	<!-- 引入属性文件 <context:property-placeholder location="classpath:jdbc.properties" 
-		/> -->
+    <!-- 引入属性文件 <context:property-placeholder location="classpath:jdbc.properties"
+        /> -->
 
-	<!-- 自动扫描dao和service包(自动注入) -->
-	<context:component-scan base-package="cn.com.qmth.stmms.biz" />
+    <!-- 自动扫描dao和service包(自动注入) -->
+    <context:component-scan base-package="cn.com.qmth.stmms.biz"/>
 
-	<import resource="spring-jpa.xml" />
+    <import resource="spring-jpa.xml"/>
 
-	<!-- dbcp数据源 -->
-	<bean name="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
-		destroy-method="close">
-		<property name="driverClassName" value="${driverClassName}" />
-		<property name="url" value="${url}" />
-		<property name="username" value="${username}" />
-		<property name="password" value="${password}" />
-		<property name="maxActive" value="50" />  
-        <property name="initialSize" value="5" />  
-        <property name="maxWait" value="60000" />  
-        <property name="maxIdle" value="5" />  
-        <property name="minIdle" value="5" />
-		<property name="minEvictableIdleTimeMillis" value="1800000" />
-		<property name="timeBetweenEvictionRunsMillis" value="60000" />
-		<property name="testWhileIdle" value="true" />
-		<property name="testOnBorrow" value="false" />
-		<property name="validationQuery" value="SELECT 1 FROM DUAL" />
-	</bean>
+    <!-- dbcp数据源 -->
+    <bean name="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
+          destroy-method="close">
+        <property name="driverClassName" value="${jdbc.driver}"/>
+        <property name="url" value="${jdbc.url}"/>
+        <property name="username" value="${jdbc.username}"/>
+        <property name="password" value="${jdbc.password}"/>
+        <property name="maxActive" value="${jdbc.maxActive}"/>
+        <property name="initialSize" value="${jdbc.initSize}"/>
+        <property name="maxWait" value="60000"/>
+        <property name="maxIdle" value="5"/>
+        <property name="minIdle" value="5"/>
+        <property name="minEvictableIdleTimeMillis" value="1800000"/>
+        <property name="timeBetweenEvictionRunsMillis" value="60000"/>
+        <property name="testWhileIdle" value="true"/>
+        <property name="testOnBorrow" value="false"/>
+        <property name="validationQuery" value="SELECT 1 FROM DUAL"/>
+    </bean>
 
-	<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
-		<property name="entityManagerFactory" ref="entityManagerFactory" />
-	</bean>
+    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
+        <property name="entityManagerFactory" ref="entityManagerFactory"/>
+    </bean>
 
-	<!-- 指定事务声明的配置方式:使用注解的方式 -->
-	<tx:annotation-driven transaction-manager="transactionManager" />
+    <!-- 指定事务声明的配置方式:使用注解的方式 -->
+    <tx:annotation-driven transaction-manager="transactionManager"/>
 
-	<bean id="entityManagerFactory"
-		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
-		<property name="dataSource" ref="dataSource" />
-		<property name="jpaVendorAdapter">
-			<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
-				<property name="database" value="MYSQL" />
-				<property name="showSql" value="false" />
-				<property name="generateDdl" value="true" />
-			</bean>
-		</property>
-	</bean>
+    <bean id="entityManagerFactory"
+          class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
+        <property name="dataSource" ref="dataSource"/>
+        <property name="jpaVendorAdapter">
+            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
+                <property name="database" value="MYSQL"/>
+                <property name="showSql" value="false"/>
+                <property name="generateDdl" value="true"/>
+            </bean>
+        </property>
+    </bean>
 
-	<task:executor id="stmms-executor" pool-size="10" />
-	<task:annotation-driven executor="stmms-executor" />
+    <task:executor id="task-executor" pool-size="5"/>
+    <task:executor id="cron-executor" pool-size="5"/>
+    <task:scheduler id="cron-scheduler" pool-size="5"/>
+    <task:annotation-driven executor="cron-executor" scheduler="cron-scheduler"/>
 </beans>

+ 22 - 35
stmms-biz/src/test/resources/application.properties

@@ -1,36 +1,23 @@
-#jdbc config
-driverClassName=com.mysql.jdbc.Driver
-url=jdbc:mysql://mc-db1:3306/stmms_gx?useUnicode=true&characterEncoding=UTF-8
-username=root
-password=root
-
-#cookie config
-cookie.max.age=36000
-cookie.domain=
-cookie.path=/
-
-#server config
-##slice.image.server=http://gx-slice.b0.upaiyun.com
-##sheet.image.server=http://gx-sheet.b0.upaiyun.com
-##package.image.server=http://gx-package.b0.upaiyun.com
-##card.server=http://gx-card.b0.upaiyun.com
-slice.image.server=http://192.168.1.88:9000/gx-slice
-sheet.image.server=http://192.168.1.88:9000/gx-sheet
-package.image.server=http://192.168.1.88:9000/gx-package
-card.server=http://192.168.1.88:9000/gx-card
-
-use.redis=false
-
-#redis config
-redis.pool.maxTotal=1000
-redis.pool.maxIdle=200
-redis.pool.testOnBorrow=true
-redis.ip=mc-redis1
-redis.port=6379
-
-#redis cache config
-cache.redis.pool.maxTotal=500
-cache.redis.pool.maxIdle=200
-cache.redis.pool.testOnBorrow=true
-cache.redis.ip=mc-redis2
+#jdbc config
+jdbc.driver=com.mysql.jdbc.Driver
+jdbc.url=jdbc:mysql://localhost:3306/stmms_ft?useUnicode=true&characterEncoding=UTF-8
+jdbc.username=root
+jdbc.password=root
+jdbc.maxActive=50
+jdbc.initSize=5
+
+use.redis=false
+
+#redis config
+redis.pool.maxTotal=1000
+redis.pool.maxIdle=200
+redis.pool.testOnBorrow=true
+redis.ip=mc-redis1
+redis.port=6379
+
+#redis cache config
+cache.redis.pool.maxTotal=500
+cache.redis.pool.maxIdle=200
+cache.redis.pool.testOnBorrow=true
+cache.redis.ip=mc-redis2
 cache.redis.port=6379

+ 29 - 0
stmms-common/src/main/java/cn/com/qmth/stmms/common/enums/AccountType.java

@@ -0,0 +1,29 @@
+package cn.com.qmth.stmms.common.enums;
+
+/**
+ * 评卷员绑定的账号类型
+ */
+public enum AccountType {
+
+    USER("系统用户"), OPEN_ACCOUNT("外部账号");
+
+    private String name;
+
+    private AccountType(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public static AccountType findByName(String text) {
+        for (AccountType obj : AccountType.values()) {
+            if (obj.getName().equalsIgnoreCase(text)) {
+                return obj;
+            }
+        }
+        return null;
+    }
+
+}

+ 42 - 41
stmms-common/src/main/java/cn/com/qmth/stmms/common/enums/Role.java

@@ -1,41 +1,42 @@
-package cn.com.qmth.stmms.common.enums;
-
-public enum Role {
-
-    SYS_ADMIN("系统管理员", 1), SCHOOL_ADMIN("学校管理员", 2), SCANNER("扫描员", 3), SUBJECT_HEADER("科组长", 4), MARKER("评卷员", 5), SCHOOL_VIEWER("学校查询员",6);
-
-    private String name;
-
-    private int value;
-
-    private Role(String name, int value) {
-        this.name = name;
-        this.value = value;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public void setName(String name) {
-        this.name = name;
-    }
-
-    public int getValue() {
-        return value;
-    }
-
-    public void setValue(int value) {
-        this.value = value;
-    }
-
-    public static Role findByValue(int value) {
-        for (Role c : Role.values()) {
-            if (c.getValue() == value) {
-                return c;
-            }
-        }
-        return null;
-    }
-
-}
+package cn.com.qmth.stmms.common.enums;
+
+public enum Role {
+
+    SYS_ADMIN("系统管理员", 1), SCHOOL_ADMIN("学校管理员", 2), SCANNER("扫描员", 3), SUBJECT_HEADER("科组长", 4), MARKER("评卷员", 5), SCHOOL_VIEWER("学校查询员",
+            6), SCHOOL_DEV("学校接口调用", 7), OPEN_ACCOUNT("第三方账号", 8);
+
+    private String name;
+
+    private int value;
+
+    private Role(String name, int value) {
+        this.name = name;
+        this.value = value;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public int getValue() {
+        return value;
+    }
+
+    public void setValue(int value) {
+        this.value = value;
+    }
+
+    public static Role findByValue(int value) {
+        for (Role c : Role.values()) {
+            if (c.getValue() == value) {
+                return c;
+            }
+        }
+        return null;
+    }
+
+}

+ 209 - 0
stmms-common/src/main/java/cn/com/qmth/stmms/common/signature/SignatureInfo.java

@@ -0,0 +1,209 @@
+package cn.com.qmth.stmms.common.signature;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.text.MessageFormat;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 签名数据对象,包括构造、解析、验证的通用方法
+ */
+public class SignatureInfo {
+
+    private static final Charset CHARSET = StandardCharsets.UTF_8;
+
+    private static final String PATTERN = "{0} {1}{2}{3}";
+
+    private static final String FIELD_JOINER = ":";
+
+    private static final String PARAM_JOINER = "&";
+
+    private static Map<String, SignatureType> typeMap = new HashMap<>();
+
+    static {
+        for (SignatureType type : SignatureType.values()) {
+            typeMap.put(type.getName(), type);
+        }
+    }
+
+    private SignatureType type;
+
+    private String method;
+
+    private String uri;
+
+    private long timestamp;
+
+    private String ciphertext;
+
+    private String invoker;
+
+    private String secret;
+
+    private SignatureInfo() {
+    }
+
+    public SignatureType getType() {
+        return type;
+    }
+
+    private void setType(SignatureType type) {
+        this.type = type;
+    }
+
+    public String getMethod() {
+        return method;
+    }
+
+    private void setMethod(String method) {
+        this.method = method;
+    }
+
+    public String getUri() {
+        return uri;
+    }
+
+    private void setUri(String uri) {
+        this.uri = uri;
+    }
+
+    public long getTimestamp() {
+        return timestamp;
+    }
+
+    private void setTimestamp(long timestamp) {
+        this.timestamp = timestamp;
+    }
+
+    private String getCiphertext() {
+        return ciphertext;
+    }
+
+    private void setCiphertext(String ciphertext) {
+        this.ciphertext = ciphertext;
+    }
+
+    public String getInvoker() {
+        return invoker;
+    }
+
+    private void setInvoker(String invoker) {
+        this.invoker = invoker;
+    }
+
+    public String getSecret() {
+        return secret;
+    }
+
+    private void setSecret(String secret) {
+        this.secret = secret;
+    }
+
+    private static String encrypt(String... values) {
+        return base64encode(sha1(StringUtils.join(values, PARAM_JOINER)));
+    }
+
+    /**
+     * 基于解析好的签名对象,使用传入的保密信息进行签名内容验证
+     *
+     * @param secret
+     * @return
+     */
+    public boolean validate(String secret) {
+        if (method != null && uri != null && timestamp >= 0 && secret != null && ciphertext != null) {
+            return encrypt(method, uri, String.valueOf(timestamp), secret).equals(ciphertext);
+        }
+        return false;
+    }
+
+    /**
+     * 根据标准参数构造最终的签名字符串
+     *
+     * @param type
+     * @param method
+     * @param uri
+     * @param timestamp
+     * @param invoker
+     * @param secret
+     * @return
+     */
+    public static String build(SignatureType type, String method, String uri, long timestamp, String invoker, String secret) {
+        if (type == null || method == null || uri == null || timestamp <= 0 || invoker == null || secret == null) {
+            return "";
+        }
+        return MessageFormat.format(PATTERN, type.getName(), invoker, FIELD_JOINER,
+                encrypt(method.toLowerCase(), uri, String.valueOf(timestamp), secret));
+    }
+
+    /**
+     * 根据当前接口的的基本信息、header中的时间戳与签名字符串,尝试解析并构造签名数据对象
+     *
+     * @param method
+     * @param uri
+     * @param timestamp
+     * @param signature
+     * @return
+     */
+    public static SignatureInfo parse(String method, String uri, long timestamp, String signature) {
+        if (method == null || uri == null || timestamp <= 0 || signature == null) {
+            return null;
+        }
+        String[] values = StringUtils.split(signature);
+        if (values != null && values.length == 2) {
+            SignatureType type = typeMap.get(values[0]);
+            if (type != null) {
+                String[] array = StringUtils.split(values[1], FIELD_JOINER);
+                if (array != null && array.length == 2) {
+                    SignatureInfo info = new SignatureInfo();
+                    info.setType(type);
+                    info.setMethod(method.toLowerCase());
+                    info.setUri(uri);
+                    info.setTimestamp(timestamp);
+                    info.setInvoker(array[0]);
+                    info.setCiphertext(array[1]);
+                    return info;
+                }
+            }
+        }
+        return null;
+    }
+
+    public static String base64encode(byte[] input) {
+        return input != null ? new String(Base64.getEncoder().encode(input), CHARSET) : "";
+    }
+
+    public static byte[] sha1(String input) {
+        try {
+            return MessageDigest.getInstance("SHA1").digest(input.getBytes(CHARSET));
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    public static void main(String[] args) {
+        SignatureInfo source = new SignatureInfo();
+        source.setType(SignatureType.SECRET);
+        source.setMethod("POST");
+        source.setUri("/api/test");
+        source.setTimestamp(System.currentTimeMillis());
+        source.setInvoker("123456");
+        source.setSecret("abcdefg");
+        long start = System.currentTimeMillis();
+        String signature = SignatureInfo
+                .build(source.getType(), source.getMethod(), source.getUri(), source.getTimestamp(), source.getInvoker(),
+                        source.getSecret());
+        System.out.println("signature:" + signature + "\ntime cost=" + (System.currentTimeMillis() - start));
+
+        start = System.currentTimeMillis();
+        SignatureInfo parse = SignatureInfo.parse(source.getMethod(), source.getUri(), source.getTimestamp(), signature);
+        System.out.println(
+                "validate:" + (parse != null && parse.validate(source.getSecret())) + "\ntime cost=" + (System.currentTimeMillis()
+                        - start));
+    }
+
+}

+ 17 - 0
stmms-common/src/main/java/cn/com/qmth/stmms/common/signature/SignatureType.java

@@ -0,0 +1,17 @@
+package cn.com.qmth.stmms.common.signature;
+
+public enum SignatureType {
+
+    SECRET("Secret"), TOKEN("Token");
+
+    private String name;
+
+    private SignatureType(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+}

+ 38 - 0
stmms-common/src/main/java/cn/com/qmth/stmms/common/utils/AccessControlUtils.java

@@ -0,0 +1,38 @@
+package cn.com.qmth.stmms.common.utils;
+
+import org.apache.commons.lang3.RandomStringUtils;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.UUID;
+
+public class AccessControlUtils {
+
+    public static final int DEFAULT_RANDOM_STRING_LENGTH = 32;
+
+    public static final int DEFAULT_EXPIRE_SECONDS = 3600 * 10;
+
+    public static String randomString() {
+        return randomString(DEFAULT_RANDOM_STRING_LENGTH);
+    }
+
+    public static String randomString(int length) {
+        return RandomStringUtils.random(length, true, true);
+    }
+
+    public static String uuidString() {
+        return UUID.randomUUID().toString().replaceAll("-", "");
+    }
+
+    public static boolean expired(Date refreshTime, int expireSeconds) {
+        Calendar c = Calendar.getInstance();
+        c.setTime(refreshTime);
+        c.add(Calendar.SECOND, expireSeconds);
+        return new Date().after(c.getTime());
+    }
+
+    public static boolean expired(Date refreshTime) {
+        return expired(refreshTime, DEFAULT_EXPIRE_SECONDS);
+    }
+
+}

+ 11 - 17
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ArbitrateController.java

@@ -40,7 +40,7 @@ import cn.com.qmth.stmms.biz.mark.service.ArbitrateHistoryService;
 import cn.com.qmth.stmms.biz.mark.service.MarkService;
 import cn.com.qmth.stmms.biz.mark.service.TaskService;
 import cn.com.qmth.stmms.biz.user.service.UserService;
-import cn.com.qmth.stmms.common.auth.annotation.RoleRequire;
+import cn.com.qmth.stmms.common.annotation.RoleRequire;
 import cn.com.qmth.stmms.common.domain.WebUser;
 import cn.com.qmth.stmms.common.enums.HistoryStatus;
 import cn.com.qmth.stmms.common.enums.LockType;
@@ -107,8 +107,8 @@ public class ArbitrateController extends BaseExamController {
             query.setGroupNumber(groupList.get(0).getNumber());
         }
         for (MarkGroup group : groupList) {
-            group.setQuestionList(questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId,
-                    group.getSubjectCode(), false, group.getNumber()));
+            group.setQuestionList(questionService
+                    .findByExamAndSubjectAndObjectiveAndGroupNumber(examId, group.getSubjectCode(), false, group.getNumber()));
         }
         query.orderByIdDesc();
         query = arbitrateService.findByQuery(query);
@@ -198,10 +198,9 @@ public class ArbitrateController extends BaseExamController {
     @RequestMapping(value = "/history/{subjectCode}/{groupNumber}", method = RequestMethod.POST)
     @ResponseBody
     @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
-    public List<Task> getHistory(HttpServletRequest request, @PathVariable String subjectCode,
-            @PathVariable Integer groupNumber, @RequestParam(required = false) Integer pageNumber,
-            @RequestParam(required = false) Integer pageSize, @RequestParam String order, @RequestParam String sort,
-            @RequestParam(required = false, defaultValue = "false") Boolean isTag,
+    public List<Task> getHistory(HttpServletRequest request, @PathVariable String subjectCode, @PathVariable Integer groupNumber,
+            @RequestParam(required = false) Integer pageNumber, @RequestParam(required = false) Integer pageSize,
+            @RequestParam String order, @RequestParam String sort, @RequestParam(required = false, defaultValue = "false") Boolean isTag,
             @RequestParam(required = false) Integer studentId) {
         int examId = getSessionExamId(request);
         WebUser wu = RequestUtils.getWebUser(request);
@@ -252,8 +251,7 @@ public class ArbitrateController extends BaseExamController {
         WebUser wu = RequestUtils.getWebUser(request);
         ArbitrateHistory history = arbitrateService.findById(historyId);
         if (history != null && history.getExamId().equals(examId) && subjectCheck(history.getSubjectCode(), wu)) {
-            MarkGroup group = groupService.findOne(history.getExamId(), history.getSubjectCode(),
-                    history.getGroupNumber());
+            MarkGroup group = groupService.findOne(history.getExamId(), history.getSubjectCode(), history.getGroupNumber());
             return taskService.build(history, group);
         }
         Task task = new Task();
@@ -306,11 +304,9 @@ public class ArbitrateController extends BaseExamController {
         WebUser wu = RequestUtils.getWebUser(request);
         JSONObject result = new JSONObject();
         ArbitrateHistory history = arbitrateService.findById(task.getLibraryId());
-        if (history != null && subjectCheck(history.getSubjectCode(), wu) && task.getTotalScore() >= 0
-                && task.getScoreList() != null) {
+        if (history != null && subjectCheck(history.getSubjectCode(), wu) && task.getTotalScore() >= 0 && task.getScoreList() != null) {
             try {
-                lockService.watch(LockType.GROUP, history.getExamId(), history.getSubjectCode(),
-                        history.getGroupNumber());
+                lockService.watch(LockType.GROUP, history.getExamId(), history.getSubjectCode(), history.getGroupNumber());
 
                 history.setUserId(wu.getUser().getId());
                 history.setTotalScore(task.getTotalScore());
@@ -325,8 +321,7 @@ public class ArbitrateController extends BaseExamController {
                 log.error("ArbitrateController-处理仲裁卷出错", e);
                 result.accumulate("success", false);
             } finally {
-                lockService.unwatch(LockType.GROUP, history.getExamId(), history.getSubjectCode(),
-                        history.getGroupNumber());
+                lockService.unwatch(LockType.GROUP, history.getExamId(), history.getSubjectCode(), history.getGroupNumber());
             }
         } else {
             result.accumulate("success", false);
@@ -337,8 +332,7 @@ public class ArbitrateController extends BaseExamController {
     @RequestMapping("/status")
     @ResponseBody
     @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
-    public JSONObject status(HttpServletRequest request, @RequestParam String subjectCode,
-            @RequestParam Integer groupNumber) {
+    public JSONObject status(HttpServletRequest request, @RequestParam String subjectCode, @RequestParam Integer groupNumber) {
         JSONObject status = new JSONObject();
         int examId = getSessionExamId(request);
         WebUser wu = RequestUtils.getWebUser(request);

+ 9 - 9
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ExamController.java

@@ -38,7 +38,7 @@ import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
 import cn.com.qmth.stmms.biz.exam.service.MarkerService;
 import cn.com.qmth.stmms.biz.mark.model.PictureConfigItem;
 import cn.com.qmth.stmms.biz.user.model.User;
-import cn.com.qmth.stmms.common.auth.annotation.RoleRequire;
+import cn.com.qmth.stmms.common.annotation.RoleRequire;
 import cn.com.qmth.stmms.common.domain.WebUser;
 import cn.com.qmth.stmms.common.enums.ExamStatus;
 import cn.com.qmth.stmms.common.enums.Role;
@@ -79,8 +79,7 @@ public class ExamController extends BaseExamController {
         query = examService.findByQuery(query);
         if (query.getCurrentCount() > 0) {
             model.addAttribute("examList", query.getResult());
-            model.addAttribute("paginator",
-                    new Paginator(query.getPageNumber(), query.getPageSize(), (int) query.getTotalCount()));
+            model.addAttribute("paginator", new Paginator(query.getPageNumber(), query.getPageSize(), (int) query.getTotalCount()));
         }
         model.addAttribute("query", query);
         return "modules/exam/examList";
@@ -119,8 +118,8 @@ public class ExamController extends BaseExamController {
     @SuppressWarnings("unchecked")
     @RequestMapping(value = "/exam-edit", method = RequestMethod.POST)
     @RoleRequire(Role.SCHOOL_ADMIN)
-    public String examEdit(HttpServletRequest request, Exam exam, int StatusValue,
-            @RequestParam(required = false) String picList, RedirectAttributes redirectAttributes) {
+    public String examEdit(HttpServletRequest request, Exam exam, int StatusValue, @RequestParam(required = false) String picList,
+            RedirectAttributes redirectAttributes) {
         User user = RequestUtils.getWebUser(request).getUser();
         Exam oldExam = examService.findById(exam.getId());
         if (oldExam != null && oldExam.getCreatorId().intValue() == user.getId().intValue()) {
@@ -225,8 +224,8 @@ public class ExamController extends BaseExamController {
 
     @RequestMapping("/exam/getSheetConfig")
     @RoleRequire(Role.SCHOOL_ADMIN)
-    public String get(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
-            @RequestParam Integer examId, @RequestParam(required = false) String subjectCode) {
+    public String get(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes, @RequestParam Integer examId,
+            @RequestParam(required = false) String subjectCode) {
         Exam exam = examService.findById(examId);
         ExamSubject subject = examSubjectService.find(examId, subjectCode);
         ExamStudentSearchQuery query = new ExamStudentSearchQuery();
@@ -248,8 +247,9 @@ public class ExamController extends BaseExamController {
 
     private List<String> buildPicUrl(ExamStudent examStudent) {
         Campus campus = campusService.findBySchoolAndName(examStudent.getSchoolId(), examStudent.getCampusName());
-        List<String> picUrls = PictureUrlBuilder.getSheetUrls(examStudent.getExamId(), campus.getId(),
-                examStudent.getSubjectCode(), examStudent.getExamNumber(), examStudent.getSheetCount());
+        List<String> picUrls = PictureUrlBuilder
+                .getSheetUrls(examStudent.getExamId(), campus.getId(), examStudent.getSubjectCode(), examStudent.getExamNumber(),
+                        examStudent.getSheetCount());
         return picUrls;
     }
 }

+ 111 - 110
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ImageCheckController.java

@@ -1,110 +1,111 @@
-package cn.com.qmth.stmms.admin.exam;
-
-import java.util.LinkedList;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.core.task.AsyncTaskExecutor;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.servlet.ModelAndView;
-import org.springframework.web.servlet.mvc.support.RedirectAttributes;
-
-import cn.com.qmth.stmms.admin.dto.ExceptionStudentDTO;
-import cn.com.qmth.stmms.admin.thread.ImageCheckThread;
-import cn.com.qmth.stmms.admin.utils.UpyunConfig;
-import cn.com.qmth.stmms.biz.campus.service.CampusService;
-import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
-import cn.com.qmth.stmms.biz.exam.query.ExamStudentSearchQuery;
-import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
-import cn.com.qmth.stmms.common.auth.annotation.RoleRequire;
-import cn.com.qmth.stmms.common.domain.WebUser;
-import cn.com.qmth.stmms.common.enums.Role;
-import cn.com.qmth.stmms.common.utils.ExportExcel;
-import cn.com.qmth.stmms.common.utils.RequestUtils;
-
-@Controller
-@RequestMapping("/admin/exam/check/image")
-public class ImageCheckController extends BaseExamController {
-
-    @Autowired
-    private CampusService campusService;
-
-    @Autowired
-    private ExamStudentService studentService;
-
-    @Autowired
-    private UpyunConfig config;
-
-    @Autowired
-    private AsyncTaskExecutor taskExecutor;
-
-    @Value("${file.root}")
-    private String baseDir;
-
-    private AtomicBoolean running = new AtomicBoolean(false);
-
-    @RequestMapping
-    public ModelAndView list(HttpServletRequest request, ExamStudentSearchQuery query) {
-        WebUser wu = RequestUtils.getWebUser(request);
-        int examId = getSessionExamId(request);
-        ModelAndView view = new ModelAndView("modules/exam/checkImage");
-        query.setExamId(examId);
-        query.setUpload(true);
-        query.setAbsent(false);
-        query.setException(true);
-        query.orderByExamNumber();
-        subjectFilter(query, wu);
-        query = studentService.findByQuery(query);
-
-        view.addObject("query", query);
-        view.addObject("subjectList", getExamSubject(examId, wu));
-        view.addObject("campusList", studentService.findDistinctCampusName(examId));
-        view.addObject("running", running.get());
-        return view;
-    }
-
-    @RequestMapping("/run")
-    @RoleRequire(Role.SCHOOL_ADMIN)
-    public ModelAndView checkImage(HttpServletRequest request) {
-        int examId = getSessionExamId(request);
-        if (running.compareAndSet(false, true) == true) {
-            ImageCheckThread thread = new ImageCheckThread(examId, running, config, baseDir, studentService,
-                    campusService);
-            taskExecutor.submit(thread);
-        }
-        return new ModelAndView("redirect:/admin/exam/check/image");
-    }
-
-    @RequestMapping("/export")
-    public String export(ExamStudentSearchQuery query, HttpServletRequest request, HttpServletResponse response,
-            RedirectAttributes redirectAttributes) {
-        WebUser wu = RequestUtils.getWebUser(request);
-        List<ExceptionStudentDTO> list = new LinkedList<ExceptionStudentDTO>();
-        query.setExamId(getSessionExamId(request));
-        query.setUpload(true);
-        query.setAbsent(false);
-        query.setException(true);
-        query.setPageNumber(1);
-        query.setPageSize(Integer.MAX_VALUE);
-        subjectFilter(query, wu);
-        query = studentService.findByQuery(query);
-        for (ExamStudent student : query.getResult()) {
-            ExceptionStudentDTO dto = new ExceptionStudentDTO(student);
-            list.add(dto);
-        }
-        String fileName = "异常名单.xlsx";
-        try {
-            new ExportExcel("异常名单", ExceptionStudentDTO.class).setDataList(list).write(response, fileName).dispose();
-            return null;
-        } catch (Exception e) {
-            addMessage(redirectAttributes, "导出失败!" + e.getMessage());
-            return "redirect:/admin/exam/check/image";
-        }
-    }
-}
+package cn.com.qmth.stmms.admin.exam;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.task.AsyncTaskExecutor;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
+import cn.com.qmth.stmms.admin.dto.ExceptionStudentDTO;
+import cn.com.qmth.stmms.admin.thread.ImageCheckThread;
+import cn.com.qmth.stmms.admin.utils.UpyunConfig;
+import cn.com.qmth.stmms.biz.campus.service.CampusService;
+import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
+import cn.com.qmth.stmms.biz.exam.query.ExamStudentSearchQuery;
+import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
+import cn.com.qmth.stmms.common.annotation.RoleRequire;
+import cn.com.qmth.stmms.common.domain.WebUser;
+import cn.com.qmth.stmms.common.enums.Role;
+import cn.com.qmth.stmms.common.utils.ExportExcel;
+import cn.com.qmth.stmms.common.utils.RequestUtils;
+
+@Controller
+@RequestMapping("/admin/exam/check/image")
+public class ImageCheckController extends BaseExamController {
+
+    @Autowired
+    private CampusService campusService;
+
+    @Autowired
+    private ExamStudentService studentService;
+
+    @Autowired
+    private UpyunConfig config;
+
+    @Qualifier("task-executor")
+    @Autowired
+    private AsyncTaskExecutor taskExecutor;
+
+    @Value("${file.root}")
+    private String baseDir;
+
+    private AtomicBoolean running = new AtomicBoolean(false);
+
+    @RequestMapping
+    public ModelAndView list(HttpServletRequest request, ExamStudentSearchQuery query) {
+        WebUser wu = RequestUtils.getWebUser(request);
+        int examId = getSessionExamId(request);
+        ModelAndView view = new ModelAndView("modules/exam/checkImage");
+        query.setExamId(examId);
+        query.setUpload(true);
+        query.setAbsent(false);
+        query.setException(true);
+        query.orderByExamNumber();
+        subjectFilter(query, wu);
+        query = studentService.findByQuery(query);
+
+        view.addObject("query", query);
+        view.addObject("subjectList", getExamSubject(examId, wu));
+        view.addObject("campusList", studentService.findDistinctCampusName(examId));
+        view.addObject("running", running.get());
+        return view;
+    }
+
+    @RequestMapping("/run")
+    @RoleRequire(Role.SCHOOL_ADMIN)
+    public ModelAndView checkImage(HttpServletRequest request) {
+        int examId = getSessionExamId(request);
+        if (running.compareAndSet(false, true) == true) {
+            ImageCheckThread thread = new ImageCheckThread(examId, running, config, baseDir, studentService, campusService);
+            taskExecutor.submit(thread);
+        }
+        return new ModelAndView("redirect:/admin/exam/check/image");
+    }
+
+    @RequestMapping("/export")
+    public String export(ExamStudentSearchQuery query, HttpServletRequest request, HttpServletResponse response,
+            RedirectAttributes redirectAttributes) {
+        WebUser wu = RequestUtils.getWebUser(request);
+        List<ExceptionStudentDTO> list = new LinkedList<ExceptionStudentDTO>();
+        query.setExamId(getSessionExamId(request));
+        query.setUpload(true);
+        query.setAbsent(false);
+        query.setException(true);
+        query.setPageNumber(1);
+        query.setPageSize(Integer.MAX_VALUE);
+        subjectFilter(query, wu);
+        query = studentService.findByQuery(query);
+        for (ExamStudent student : query.getResult()) {
+            ExceptionStudentDTO dto = new ExceptionStudentDTO(student);
+            list.add(dto);
+        }
+        String fileName = "异常名单.xlsx";
+        try {
+            new ExportExcel("异常名单", ExceptionStudentDTO.class).setDataList(list).write(response, fileName).dispose();
+            return null;
+        } catch (Exception e) {
+            addMessage(redirectAttributes, "导出失败!" + e.getMessage());
+            return "redirect:/admin/exam/check/image";
+        }
+    }
+}

+ 10 - 11
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/InspectedController.java

@@ -19,7 +19,6 @@ import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Controller;
-import org.springframework.ui.Model;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.ResponseBody;
@@ -39,13 +38,12 @@ import cn.com.qmth.stmms.biz.exam.service.MarkerService;
 import cn.com.qmth.stmms.biz.mark.model.MarkLibrary;
 import cn.com.qmth.stmms.biz.mark.model.MarkSpecialTag;
 import cn.com.qmth.stmms.biz.mark.model.MarkTrack;
-import cn.com.qmth.stmms.biz.mark.model.Task;
 import cn.com.qmth.stmms.biz.mark.query.MarkLibrarySearchQuery;
 import cn.com.qmth.stmms.biz.mark.service.MarkLibraryService;
 import cn.com.qmth.stmms.biz.mark.service.MarkSpecialTagService;
 import cn.com.qmth.stmms.biz.mark.service.MarkTrackService;
 import cn.com.qmth.stmms.biz.utils.ScoreItem;
-import cn.com.qmth.stmms.common.auth.annotation.RoleRequire;
+import cn.com.qmth.stmms.common.annotation.RoleRequire;
 import cn.com.qmth.stmms.common.domain.WebUser;
 import cn.com.qmth.stmms.common.enums.LibraryStatus;
 import cn.com.qmth.stmms.common.enums.MarkStatus;
@@ -91,8 +89,8 @@ public class InspectedController extends BaseExamController {
 
     @RequestMapping("/start")
     @ResponseBody
-    public ModelAndView start(HttpServletRequest request, RedirectAttributes redirectAttributes,
-            @RequestParam String subjectCode, @RequestParam Integer groupNumber) {
+    public ModelAndView start(HttpServletRequest request, RedirectAttributes redirectAttributes, @RequestParam String subjectCode,
+            @RequestParam Integer groupNumber) {
         int examId = getSessionExamId(request);
         if (examId > 0) {
             MarkGroup group = groupService.findOne(examId, subjectCode, groupNumber);
@@ -152,8 +150,9 @@ public class InspectedController extends BaseExamController {
             result.accumulate("markerScore", library.getMarkerScore());
 
             JSONArray array = new JSONArray();
-            List<ExamQuestion> questions = questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(
-                    library.getExamId(), library.getSubjectCode(), false, library.getGroupNumber());
+            List<ExamQuestion> questions = questionService
+                    .findByExamAndSubjectAndObjectiveAndGroupNumber(library.getExamId(), library.getSubjectCode(), false,
+                            library.getGroupNumber());
             List<ScoreItem> scores = library.getScoreList();
             for (int i = 0; i < questions.size(); i++) {
                 ExamQuestion question = questions.get(i);
@@ -164,11 +163,11 @@ public class InspectedController extends BaseExamController {
             }
             result.accumulate("questions", array);
 
-            MarkGroup group = groupService.findOne(student.getExamId(), student.getSubjectCode(),
-                    library.getGroupNumber());
+            MarkGroup group = groupService.findOne(student.getExamId(), student.getSubjectCode(), library.getGroupNumber());
             Campus campus = campusService.findBySchoolAndName(student.getSchoolId(), student.getCampusName());
-            List<String> picUrls = PictureUrlBuilder.getSliceUrls(student.getExamId(), campus.getId(),
-                    student.getSubjectCode(), student.getExamNumber(), student.getSliceCount());
+            List<String> picUrls = PictureUrlBuilder
+                    .getSliceUrls(student.getExamId(), campus.getId(), student.getSubjectCode(), student.getExamNumber(),
+                            student.getSliceCount());
             List<MarkTrack> markTracks = markTrackService.findByLibraryId(library.getId());
             List<MarkSpecialTag> markSpecialTagList = markSpecialTagService.findByLibraryId(library.getId());
             result.accumulate("picUrls", picUrls);

+ 7 - 11
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/LibraryController.java

@@ -26,7 +26,7 @@ import cn.com.qmth.stmms.biz.mark.model.MarkLibrary;
 import cn.com.qmth.stmms.biz.mark.query.MarkLibrarySearchQuery;
 import cn.com.qmth.stmms.biz.mark.service.MarkLibraryService;
 import cn.com.qmth.stmms.biz.mark.service.MarkService;
-import cn.com.qmth.stmms.common.auth.annotation.RoleRequire;
+import cn.com.qmth.stmms.common.annotation.RoleRequire;
 import cn.com.qmth.stmms.common.domain.WebUser;
 import cn.com.qmth.stmms.common.enums.LibraryStatus;
 import cn.com.qmth.stmms.common.enums.LockType;
@@ -75,8 +75,7 @@ public class LibraryController extends BaseExamController {
             query.setSubjectCode(subjectList.get(0).getCode());
         }
         subjectFilter(query, wu);
-        List<MarkGroup> groupList = groupService.findByExamAndSubjectAndStatus(examId, query.getSubjectCode(),
-                MarkStatus.FORMAL);
+        List<MarkGroup> groupList = groupService.findByExamAndSubjectAndStatus(examId, query.getSubjectCode(), MarkStatus.FORMAL);
         // if (groupList.isEmpty()) {
         // return "redirect:/admin/exam/mark";
         // }
@@ -92,8 +91,8 @@ public class LibraryController extends BaseExamController {
                 }
             }
             for (MarkGroup group : groupList) {
-                group.setQuestionList(questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId,
-                        group.getSubjectCode(), false, group.getNumber()));
+                group.setQuestionList(questionService
+                        .findByExamAndSubjectAndObjectiveAndGroupNumber(examId, group.getSubjectCode(), false, group.getNumber()));
             }
         }
         MarkLibrarySearchQuery query2 = new MarkLibrarySearchQuery();
@@ -123,10 +122,8 @@ public class LibraryController extends BaseExamController {
         if (library != null) {
             if (subjectCheck(library.getSubjectCode(), RequestUtils.getWebUser(request))) {
                 try {
-                    lockService.watch(LockType.GROUP, library.getExamId(), library.getSubjectCode(),
-                            library.getGroupNumber());
-                    if (library.getStatus().equals(LibraryStatus.MARKED)
-                            && markService.backLibrary(library, wu.getId())) {
+                    lockService.watch(LockType.GROUP, library.getExamId(), library.getSubjectCode(), library.getGroupNumber());
+                    if (library.getStatus().equals(LibraryStatus.MARKED) && markService.backLibrary(library, wu.getId())) {
                         obj.accumulate("success", true);
                     } else {
                         obj.accumulate("success", false);
@@ -137,8 +134,7 @@ public class LibraryController extends BaseExamController {
                     obj.accumulate("message", "打回评卷任务失败");
                     log.error("back library error", e);
                 } finally {
-                    lockService.unwatch(LockType.GROUP, library.getExamId(), library.getSubjectCode(),
-                            library.getGroupNumber());
+                    lockService.unwatch(LockType.GROUP, library.getExamId(), library.getSubjectCode(), library.getGroupNumber());
                 }
             } else {
                 obj.accumulate("success", false);

+ 56 - 69
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/MarkGroupController.java

@@ -43,7 +43,7 @@ import cn.com.qmth.stmms.biz.exam.service.MarkerService;
 import cn.com.qmth.stmms.biz.lock.LockService;
 import cn.com.qmth.stmms.biz.mark.model.PictureConfigItem;
 import cn.com.qmth.stmms.biz.mark.service.MarkService;
-import cn.com.qmth.stmms.common.auth.annotation.RoleRequire;
+import cn.com.qmth.stmms.common.annotation.RoleRequire;
 import cn.com.qmth.stmms.common.domain.WebUser;
 import cn.com.qmth.stmms.common.enums.LockType;
 import cn.com.qmth.stmms.common.enums.MarkMode;
@@ -103,12 +103,11 @@ public class MarkGroupController extends BaseExamController {
         }
         List<MarkGroup> list = groupService.findByExamAndSubject(examId, subjectCode);
         for (MarkGroup group : list) {
-            group.setQuestionList(questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId, subjectCode,
-                    false, group.getNumber()));
+            group.setQuestionList(
+                    questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId, subjectCode, false, group.getNumber()));
             group.setMarkerCount(markerService.countByExamAndSubjectAndGroup(examId, subjectCode, group.getNumber()));
             group.setCurrentCount(markService.applyCount(group));
-            int percent = group.getLibraryCount() > 0 ? (int) (group.getMarkedCount() * 100.00 / group
-                    .getLibraryCount()) : 0;
+            int percent = group.getLibraryCount() > 0 ? (int) (group.getMarkedCount() * 100.00 / group.getLibraryCount()) : 0;
             group.setPercent(percent);
         }
         model.addAttribute("resultList", list);
@@ -119,17 +118,18 @@ public class MarkGroupController extends BaseExamController {
 
     @RequestMapping("/query")
     @ResponseBody
-    public JSONArray query(HttpServletRequest request, @RequestParam String subjectCode,
-            @RequestParam(required = false) Boolean withDouble, @RequestParam(required = false) MarkStatus status) {
+    public JSONArray query(HttpServletRequest request, @RequestParam String subjectCode, @RequestParam(required = false) Boolean withDouble,
+            @RequestParam(required = false) MarkStatus status) {
         int examId = getSessionExamId(request);
         JSONArray array = new JSONArray();
-        List<MarkGroup> list = withDouble != null && withDouble ? groupService.findByExamAndSubjectWithDouble(examId,
-                subjectCode) : (status != null ? groupService
-                .findByExamAndSubjectAndStatus(examId, subjectCode, status) : groupService.findByExamAndSubject(examId,
-                subjectCode));
+        List<MarkGroup> list = withDouble != null && withDouble ?
+                groupService.findByExamAndSubjectWithDouble(examId, subjectCode) :
+                (status != null ?
+                        groupService.findByExamAndSubjectAndStatus(examId, subjectCode, status) :
+                        groupService.findByExamAndSubject(examId, subjectCode));
         for (MarkGroup group : list) {
-            group.setQuestionList(questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId, subjectCode,
-                    false, group.getNumber()));
+            group.setQuestionList(
+                    questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId, subjectCode, false, group.getNumber()));
             JSONObject obj = new JSONObject();
             obj.accumulate("number", group.getNumber());
             obj.accumulate("title", group.getTitle());
@@ -156,8 +156,8 @@ public class MarkGroupController extends BaseExamController {
 
     @RequestMapping("/release")
     @RoleRequire(Role.SCHOOL_ADMIN)
-    public String release(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
-            @RequestParam String subjectCode, @RequestParam Integer number) {
+    public String release(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes, @RequestParam String subjectCode,
+            @RequestParam Integer number) {
         int examId = getSessionExamId(request);
         MarkGroup group = groupService.findOne(examId, subjectCode, number);
         if (group == null) {
@@ -175,8 +175,8 @@ public class MarkGroupController extends BaseExamController {
 
     @RequestMapping("/reset")
     @RoleRequire(Role.SCHOOL_ADMIN)
-    public String reset(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
-            @RequestParam String subjectCode, @RequestParam Integer number) {
+    public String reset(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes, @RequestParam String subjectCode,
+            @RequestParam Integer number) {
         int examId = getSessionExamId(request);
         MarkGroup group = groupService.findOne(examId, subjectCode, number);
         if (group == null) {
@@ -210,15 +210,13 @@ public class MarkGroupController extends BaseExamController {
             boolean allow = false;
             if (group.getStatus() == MarkStatus.TRIAL && status == MarkStatus.FORMAL) {
                 allow = true;
-            } else if (group.getStatus() == MarkStatus.FORMAL && status == MarkStatus.FINISH
-                    && group.getLeftCount() == 0) {
+            } else if (group.getStatus() == MarkStatus.FORMAL && status == MarkStatus.FINISH && group.getLeftCount() == 0) {
                 allow = true;
             } else if (group.getStatus() == MarkStatus.FINISH && status == MarkStatus.FORMAL) {
                 allow = true;
             }
             if (allow) {
-                if (groupService.updateStatus(examId, subjectCode, number, status, group.getStatus())
-                        && status == MarkStatus.FORMAL) {
+                if (groupService.updateStatus(examId, subjectCode, number, status, group.getStatus()) && status == MarkStatus.FORMAL) {
                     // 切换到正评成功后刷新任务数量
                     group.setStatus(status);
                     markService.updateLibraryCount(group);
@@ -260,13 +258,11 @@ public class MarkGroupController extends BaseExamController {
         if (group != null) {
             String pictureConfig = buildPictureConfig(group);
             group.setPicList(pictureConfig);
-            group.setQuestionList(questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId, subjectCode,
-                    false, group.getNumber()));
+            group.setQuestionList(
+                    questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId, subjectCode, false, group.getNumber()));
             model.addAttribute("group", group);
-            model.addAttribute(
-                    "questions",
-                    questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(group.getExamId(),
-                            group.getSubjectCode(), false, group.getNumber()));
+            model.addAttribute("questions", questionService
+                    .findByExamAndSubjectAndObjectiveAndGroupNumber(group.getExamId(), group.getSubjectCode(), false, group.getNumber()));
             model.addAttribute("pictureConfig", pictureConfig);
             model.addAttribute("markModeList", MarkMode.values());
             model.addAttribute("scorePolicyList", ScorePolicy.values());
@@ -280,20 +276,19 @@ public class MarkGroupController extends BaseExamController {
 
     @RequestMapping("/edit-full")
     @RoleRequire(Role.SCHOOL_ADMIN)
-    public String editFull(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
-            @RequestParam String subjectCode, @RequestParam Integer number) {
+    public String editFull(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes, @RequestParam String subjectCode,
+            @RequestParam Integer number) {
         int examId = getSessionExamId(request);
         MarkGroup group = groupService.findOne(examId, subjectCode, number);
         if (group != null) {
             String pictureConfig = buildPictureConfig(group);
             group.setPicList(pictureConfig);
-            group.setQuestionList(questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId, subjectCode,
-                    false, number));
+            group.setQuestionList(questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId, subjectCode, false, number));
             List<MarkGroup> questionList = new ArrayList<MarkGroup>();
             String[] mainNumbers = group.getMainNumber().split(",");
             for (String mainNumber : mainNumbers) {
-                List<ExamQuestion> list = questionService.findByExamAndSubjectAndObjectiveAndMainNumber(examId,
-                        subjectCode, false, Integer.parseInt(mainNumber));
+                List<ExamQuestion> list = questionService
+                        .findByExamAndSubjectAndObjectiveAndMainNumber(examId, subjectCode, false, Integer.parseInt(mainNumber));
                 StringBuilder score = new StringBuilder();
                 DecimalFormat format = new DecimalFormat("###.#");
                 String title = "";
@@ -325,8 +320,8 @@ public class MarkGroupController extends BaseExamController {
 
     @RequestMapping("/delete")
     @RoleRequire(Role.SCHOOL_ADMIN)
-    public String delete(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
-            @RequestParam String subjectCode, @RequestParam Integer number) {
+    public String delete(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes, @RequestParam String subjectCode,
+            @RequestParam Integer number) {
         int examId = getSessionExamId(request);
         MarkGroup group = groupService.findOne(examId, subjectCode, number);
         if (group == null) {
@@ -352,10 +347,9 @@ public class MarkGroupController extends BaseExamController {
     @RequestMapping("/save")
     @RoleRequire(Role.SCHOOL_ADMIN)
     @Transactional
-    public String save(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
-            @RequestParam String subjectCode, @RequestParam Integer number, @RequestParam Boolean reset,
-            @RequestParam(required = false) String picList, @RequestParam(required = false) Double doubleRate,
-            @RequestParam(required = false) Double arbitrateThreshold,
+    public String save(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes, @RequestParam String subjectCode,
+            @RequestParam Integer number, @RequestParam Boolean reset, @RequestParam(required = false) String picList,
+            @RequestParam(required = false) Double doubleRate, @RequestParam(required = false) Double arbitrateThreshold,
             @RequestParam(required = false) Integer thirdPolicy, @RequestParam(required = false) Integer scorePolicy,
             @RequestParam(required = false) MarkMode markMode, @RequestParam(required = false) Integer trialCount,
             @RequestParam(required = false) boolean sheetView, @RequestParam(required = false) String questionDetail,
@@ -369,10 +363,9 @@ public class MarkGroupController extends BaseExamController {
                     questionDetail = StringEscapeUtils.unescapeHtml(questionDetail);
                     JSONArray array = JSONArray.fromObject(questionDetail);
                     List<ExamQuestionDTO> detailList = JSONArray.toList(array, new ExamQuestionDTO(), new JsonConfig());
-                    List<ExamQuestion> all = questionService.findByExamAndSubjectAndObjective(examId, subjectCode,
-                            false);
-                    List<ExamQuestion> old = questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId,
-                            subjectCode, false, number);
+                    List<ExamQuestion> all = questionService.findByExamAndSubjectAndObjective(examId, subjectCode, false);
+                    List<ExamQuestion> old = questionService
+                            .findByExamAndSubjectAndObjectiveAndGroupNumber(examId, subjectCode, false, number);
                     Set<Integer> mainNumbers = new HashSet<Integer>();
                     for (ExamQuestion examQuestion : all) {
                         mainNumbers.add(examQuestion.getMainNumber());
@@ -397,25 +390,22 @@ public class MarkGroupController extends BaseExamController {
                     }
                     if (detailList != null && detailList.size() > 0) {
                         ScorePolicy policy = scorePolicy != null ? ScorePolicy.findByValue(scorePolicy) : null;
-                        ThirdPolicy third = thirdPolicy != null ? ThirdPolicy.findByValue(thirdPolicy)
-                                : ThirdPolicy.DISABLE;
+                        ThirdPolicy third = thirdPolicy != null ? ThirdPolicy.findByValue(thirdPolicy) : ThirdPolicy.DISABLE;
                         try {
-                            lockService.waitlock(LockType.GROUP, true, group.getExamId(), group.getSubjectCode(),
-                                    group.getNumber());
+                            lockService.waitlock(LockType.GROUP, true, group.getExamId(), group.getSubjectCode(), group.getNumber());
                             markService.updateGroup(group, buildQuestionList(group, detailList), policy, third);
                         } catch (Exception e) {
                             log.error("update group error", e);
                             throw new RuntimeException("重置更新大题失败", e);
                         } finally {
-                            lockService.unlock(LockType.GROUP, true, group.getExamId(), group.getSubjectCode(),
-                                    group.getNumber());
+                            lockService.unlock(LockType.GROUP, true, group.getExamId(), group.getSubjectCode(), group.getNumber());
                         }
                     }
                 } else {
                     // simple update
                     List<Double> intervalScores = buildDoubleList(intervalScoreList);
-                    List<ExamQuestion> questionList = questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(
-                            examId, subjectCode, false, number);
+                    List<ExamQuestion> questionList = questionService
+                            .findByExamAndSubjectAndObjectiveAndGroupNumber(examId, subjectCode, false, number);
                     if (intervalScores.size() == questionList.size()) {
                         for (int i = 0; i < questionList.size(); i++) {
                             ExamQuestion q = questionList.get(i);
@@ -465,10 +455,9 @@ public class MarkGroupController extends BaseExamController {
     @RequestMapping("/insert")
     @RoleRequire(Role.SCHOOL_ADMIN)
     @Transactional
-    public String insert(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
-            @RequestParam String subjectCode, @RequestParam Integer number, @RequestParam String questionDetail,
-            @RequestParam String picList, @RequestParam(required = false) Double doubleRate,
-            @RequestParam(required = false) Double arbitrateThreshold,
+    public String insert(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes, @RequestParam String subjectCode,
+            @RequestParam Integer number, @RequestParam String questionDetail, @RequestParam String picList,
+            @RequestParam(required = false) Double doubleRate, @RequestParam(required = false) Double arbitrateThreshold,
             @RequestParam(required = false) Integer thirdPolicy, @RequestParam(required = false) Integer scorePolicy,
             @RequestParam(required = false) String markMode, @RequestParam(required = false) Integer trialCount,
             @RequestParam(required = false) boolean sheetView) {
@@ -492,8 +481,7 @@ public class MarkGroupController extends BaseExamController {
                 // build picList
                 picList = StringEscapeUtils.unescapeHtml(picList);
                 JSONArray array = JSONArray.fromObject(picList);
-                List<PictureConfigItem> picConfigList = JSONArray.toList(array, new PictureConfigItem(),
-                        new JsonConfig());
+                List<PictureConfigItem> picConfigList = JSONArray.toList(array, new PictureConfigItem(), new JsonConfig());
                 // build questionDetail
                 questionDetail = StringEscapeUtils.unescapeHtml(questionDetail);
                 array = JSONArray.fromObject(questionDetail);
@@ -509,23 +497,21 @@ public class MarkGroupController extends BaseExamController {
                 }
                 if (picConfigList != null && detailList != null && picConfigList.size() > 0 && detailList.size() > 0) {
                     for (ExamQuestionDTO detail : detailList) {
-                        if (questionService.countByExamAndSubjectAndObjectiveAndMainNumber(examId, subjectCode, false,
-                                detail.getMainNumber()) > 0) {
+                        if (questionService
+                                .countByExamAndSubjectAndObjectiveAndMainNumber(examId, subjectCode, false, detail.getMainNumber()) > 0) {
                             addMessage(redirectAttributes, "大题号不能重复");
                             redirectAttributes.addAttribute("subjectCode", subjectCode);
                             return "redirect:/admin/exam/group/add";
                         }
                     }
-                    group = new MarkGroup(examId, subjectCode, number, picConfigList, 0d, doubleRate,
-                            arbitrateThreshold, scorePolicy, markMode, trialCount, sheetView, thirdPolicy);
+                    group = new MarkGroup(examId, subjectCode, number, picConfigList, 0d, doubleRate, arbitrateThreshold, scorePolicy,
+                            markMode, trialCount, sheetView, thirdPolicy);
                     // clear and replace exam_question
-                    questionService
-                            .deleteByExamAndSubjectAndObjectiveAndGroupNumber(examId, subjectCode, false, number);
+                    questionService.deleteByExamAndSubjectAndObjectiveAndGroupNumber(examId, subjectCode, false, number);
                     List<ExamQuestion> list = buildQuestionList(group, detailList);
                     questionService.save(list);
                     groupService.save(group);
-                    subjectService.updateScore(examId, subjectCode, false,
-                            groupService.sumTotalScore(examId, subjectCode));
+                    subjectService.updateScore(examId, subjectCode, false, groupService.sumTotalScore(examId, subjectCode));
                     redirectAttributes.addAttribute("subjectCode", subjectCode);
                     return "redirect:/admin/exam/group";
                 } else {
@@ -544,8 +530,8 @@ public class MarkGroupController extends BaseExamController {
 
     @RequestMapping("/getPictureConfig")
     @RoleRequire(Role.SCHOOL_ADMIN)
-    public String get(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
-            @RequestParam String subjectCode, @RequestParam Integer number) {
+    public String get(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes, @RequestParam String subjectCode,
+            @RequestParam Integer number) {
         int examId = getSessionExamId(request);
         ExamStudentSearchQuery query = new ExamStudentSearchQuery();
         query.setExamId(examId);
@@ -583,8 +569,9 @@ public class MarkGroupController extends BaseExamController {
 
     private List<String> buildPicUrl(ExamStudent examStudent) {
         Campus campus = campusService.findBySchoolAndName(examStudent.getSchoolId(), examStudent.getCampusName());
-        List<String> picUrls = PictureUrlBuilder.getSliceUrls(examStudent.getExamId(), campus.getId(),
-                examStudent.getSubjectCode(), examStudent.getExamNumber(), examStudent.getSliceCount());
+        List<String> picUrls = PictureUrlBuilder
+                .getSliceUrls(examStudent.getExamId(), campus.getId(), examStudent.getSubjectCode(), examStudent.getExamNumber(),
+                        examStudent.getSliceCount());
         return picUrls;
     }
 

+ 24 - 27
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/MarkQualityController.java

@@ -10,6 +10,7 @@ import javax.servlet.http.HttpServletRequest;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.core.task.AsyncTaskExecutor;
 import org.springframework.stereotype.Controller;
@@ -39,7 +40,7 @@ import cn.com.qmth.stmms.biz.mark.service.MarkLibraryService;
 import cn.com.qmth.stmms.biz.mark.service.TaskService;
 import cn.com.qmth.stmms.biz.mark.service.TrialService;
 import cn.com.qmth.stmms.biz.mark.thread.MarkQualityThread;
-import cn.com.qmth.stmms.common.auth.annotation.RoleRequire;
+import cn.com.qmth.stmms.common.annotation.RoleRequire;
 import cn.com.qmth.stmms.common.domain.WebUser;
 import cn.com.qmth.stmms.common.enums.LibraryStatus;
 import cn.com.qmth.stmms.common.enums.LockType;
@@ -62,6 +63,7 @@ public class MarkQualityController extends BaseExamController {
     @Autowired
     private LockService lockService;
 
+    @Qualifier("task-executor")
     @Autowired
     private AsyncTaskExecutor taskExecutor;
 
@@ -112,10 +114,8 @@ public class MarkQualityController extends BaseExamController {
         }
         if (query.getSubjectCode() != null && query.getGroupNumber() != null) {
             query = markerService.findByQuery(query);
-            model.addAttribute(
-                    "running",
-                    lockService.isLocked(LockType.BATCH_QUALITY,
-                            getLockKey(examId, query.getSubjectCode(), query.getGroupNumber())));
+            model.addAttribute("running",
+                    lockService.isLocked(LockType.BATCH_QUALITY, getLockKey(examId, query.getSubjectCode(), query.getGroupNumber())));
         }
         model.addAttribute("query", query);
         model.addAttribute("subjectList", subjectList);
@@ -124,15 +124,15 @@ public class MarkQualityController extends BaseExamController {
     }
 
     @RequestMapping("/update")
-    public String update(HttpServletRequest request, RedirectAttributes redirectAttributes,
-            @RequestParam String subjectCode, @RequestParam Integer groupNumber) {
+    public String update(HttpServletRequest request, RedirectAttributes redirectAttributes, @RequestParam String subjectCode,
+            @RequestParam Integer groupNumber) {
         int examId = getSessionExamId(request);
         MarkGroup group = groupService.findOne(examId, subjectCode, groupNumber);
         if (group != null) {
             final String lockKey = getLockKey(examId, subjectCode, groupNumber);
             if (lockService.trylock(LockType.BATCH_QUALITY, lockKey)) {
-                taskExecutor.submit(new MarkQualityThread(markerService.findByExamAndSubjectAndGroup(examId,
-                        subjectCode, groupNumber), lockKey));
+                taskExecutor.submit(new MarkQualityThread(markerService.findByExamAndSubjectAndGroup(examId, subjectCode, groupNumber),
+                        lockKey));
             }
             redirectAttributes.addAttribute("groupNumber", groupNumber);
         }
@@ -145,17 +145,16 @@ public class MarkQualityController extends BaseExamController {
     }
 
     @RequestMapping("/chart")
-    public String chart(HttpServletRequest request, Model model, @RequestParam String subjectCode,
-            @RequestParam Integer groupNumber) {
+    public String chart(HttpServletRequest request, Model model, @RequestParam String subjectCode, @RequestParam Integer groupNumber) {
         int examId = getSessionExamId(request);
         MarkGroup group = groupService.findOne(examId, subjectCode, groupNumber);
         if (group != null) {
             List<MarkerVO> list = new ArrayList<MarkerVO>();
             List<Marker> markers = markerService.findByExamAndSubjectAndGroup(examId, subjectCode, groupNumber);
-            List<Double> scores = libraryService.findScore(examId, subjectCode, groupNumber, LibraryStatus.MARKED,LibraryStatus.INSPECTED);
+            List<Double> scores = libraryService.findScore(examId, subjectCode, groupNumber, LibraryStatus.MARKED, LibraryStatus.INSPECTED);
             for (Marker marker : markers) {
-                List<Object[]> libraries = libraryService.findScoreCount(examId, subjectCode, groupNumber,
-                        marker.getId(), LibraryStatus.MARKED,LibraryStatus.INSPECTED);
+                List<Object[]> libraries = libraryService
+                        .findScoreCount(examId, subjectCode, groupNumber, marker.getId(), LibraryStatus.MARKED, LibraryStatus.INSPECTED);
                 Map<Double, Long> scoreCount = new HashMap<Double, Long>();
                 for (Object[] array : libraries) {
                     Double score = (Double) array[0];
@@ -169,8 +168,8 @@ public class MarkQualityController extends BaseExamController {
                 vo.setScoreCount(scoreCount);
                 list.add(vo);
             }
-            group.setQuestionList(questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId, subjectCode,
-                    false, group.getNumber()));
+            group.setQuestionList(
+                    questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId, subjectCode, false, group.getNumber()));
             model.addAttribute("scores", scores);
             model.addAttribute("markers", list);
             model.addAttribute("group", group);
@@ -183,14 +182,13 @@ public class MarkQualityController extends BaseExamController {
 
     @RequestMapping("/getChart")
     @ResponseBody
-    public List<MarkerVO> getChart(HttpServletRequest request, @RequestParam String subjectCode,
-            @RequestParam Integer groupNumber) {
+    public List<MarkerVO> getChart(HttpServletRequest request, @RequestParam String subjectCode, @RequestParam Integer groupNumber) {
         int examId = getSessionExamId(request);
         List<MarkerVO> list = new ArrayList<MarkerVO>();
         List<Marker> markers = markerService.findByExamAndSubjectAndGroup(examId, subjectCode, groupNumber);
         for (Marker marker : markers) {
-            List<Object[]> libraries = libraryService.findScoreCount(examId, subjectCode, groupNumber, marker.getId(),
-                    LibraryStatus.MARKED,LibraryStatus.INSPECTED);
+            List<Object[]> libraries = libraryService
+                    .findScoreCount(examId, subjectCode, groupNumber, marker.getId(), LibraryStatus.MARKED, LibraryStatus.INSPECTED);
             MarkLibrarySearchQuery query = new MarkLibrarySearchQuery();
             query.setMarkerId(marker.getId());
             query.addStatus(LibraryStatus.MARKED);
@@ -218,8 +216,7 @@ public class MarkQualityController extends BaseExamController {
 
     @RequestMapping("/batchProcess")
     @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
-    public String batchProcess(Model model, HttpServletRequest request, @RequestParam Integer markerId,
-            @RequestParam Double markerScore) {
+    public String batchProcess(Model model, HttpServletRequest request, @RequestParam Integer markerId, @RequestParam Double markerScore) {
         int examId = getSessionExamId(request);
         Marker marker = markerService.findById(markerId);
         WebUser wu = RequestUtils.getWebUser(request);
@@ -243,9 +240,8 @@ public class MarkQualityController extends BaseExamController {
     @RequestMapping(value = "/history", method = RequestMethod.POST)
     @ResponseBody
     @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
-    public List<Task> getTask(HttpServletRequest request, @RequestParam Integer markerId,
-            @RequestParam Double markerScore, @RequestParam(required = false) Integer pageNumber,
-            @RequestParam(required = false) Integer pageSize) {
+    public List<Task> getTask(HttpServletRequest request, @RequestParam Integer markerId, @RequestParam Double markerScore,
+            @RequestParam(required = false) Integer pageNumber, @RequestParam(required = false) Integer pageSize) {
         Marker marker = markerService.findById(markerId);
         List<Task> list = new ArrayList<>();
         MarkGroup group = groupService.findOne(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
@@ -276,8 +272,9 @@ public class MarkQualityController extends BaseExamController {
         } else if (group != null && group.getStatus() == MarkStatus.TRIAL) {
             // 试评查找给分历史记录
             List<TrialHistory> historyList = new ArrayList<TrialHistory>();
-            historyList = trialService.findHistory(marker.getExamId(), marker.getSubjectCode(),
-                    marker.getGroupNumber(), marker.getId(), pageNumber, pageSize, null,markerScore);
+            historyList = trialService
+                    .findHistory(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber(), marker.getId(), pageNumber, pageSize,
+                            null, markerScore);
             for (TrialHistory history : historyList) {
                 TrialLibrary library = trialService.findLibrary(history.getLibraryId());
                 if (library != null) {

+ 98 - 54
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/MarkerController.java

@@ -11,6 +11,11 @@ import java.util.Set;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import cn.com.qmth.stmms.biz.exam.model.*;
+import cn.com.qmth.stmms.biz.exam.service.*;
+import cn.com.qmth.stmms.biz.user.model.OpenAccount;
+import cn.com.qmth.stmms.biz.user.service.UserService;
+import cn.com.qmth.stmms.common.enums.AccountType;
 import net.sf.json.JSONArray;
 import net.sf.json.JSONObject;
 
@@ -30,20 +35,10 @@ import org.springframework.web.multipart.MultipartFile;
 import org.springframework.web.servlet.ModelAndView;
 import org.springframework.web.servlet.mvc.support.RedirectAttributes;
 
-import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
-import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
-import cn.com.qmth.stmms.biz.exam.model.Marker;
-import cn.com.qmth.stmms.biz.exam.model.MarkerDTO;
 import cn.com.qmth.stmms.biz.exam.query.MarkerSearchQuery;
-import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
-import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
-import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
-import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
-import cn.com.qmth.stmms.biz.exam.service.MarkerClassService;
-import cn.com.qmth.stmms.biz.exam.service.MarkerService;
 import cn.com.qmth.stmms.biz.lock.LockService;
 import cn.com.qmth.stmms.biz.mark.service.MarkService;
-import cn.com.qmth.stmms.common.auth.annotation.RoleRequire;
+import cn.com.qmth.stmms.common.annotation.RoleRequire;
 import cn.com.qmth.stmms.common.domain.WebUser;
 import cn.com.qmth.stmms.common.enums.LockType;
 import cn.com.qmth.stmms.common.enums.MarkerExcelError;
@@ -62,6 +57,12 @@ public class MarkerController extends BaseExamController {
 
     public static final String USER_PASSWORD = "123456";
 
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private ExamService examService;
+
     @Autowired
     private MarkerService markerService;
 
@@ -76,15 +77,15 @@ public class MarkerController extends BaseExamController {
 
     @Autowired
     private LockService lockService;
-    
+
     @Autowired
     private MarkerClassService classService;
-    
+
     @Autowired
     private ExamStudentService studentService;
-    
+
     @Autowired
-    private ExamQuestionService questionService; 
+    private ExamQuestionService questionService;
 
     @Value("${marker.showBtnImportAndBtnUpdateImport}")
     private String showBtnImport;
@@ -100,11 +101,17 @@ public class MarkerController extends BaseExamController {
         for (Marker marker : query.getResult()) {
             marker.setSubject(subjectService.find(marker.getExamId(), marker.getSubjectCode()));
             MarkGroup group = groupService.findOne(examId, marker.getSubjectCode(), marker.getGroupNumber());
-            group.setQuestionList(questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId, marker.getSubjectCode(),
-                    false, group.getNumber()));
+            group.setQuestionList(questionService
+                    .findByExamAndSubjectAndObjectiveAndGroupNumber(examId, marker.getSubjectCode(), false, group.getNumber()));
             marker.setGroup(group);
             marker.setMarkedCount(markService.markedCount(marker));
             marker.setCurrentCount(markService.applyCount(marker));
+            if (marker.getAccountType() != null && marker.getAccountId() != null) {
+                // 暂时只开放外部账号绑定
+                if (marker.getAccountType() == AccountType.OPEN_ACCOUNT) {
+                    marker.setOpenAccount(userService.findOpenAccount(marker.getAccountId()));
+                }
+            }
         }
         model.addAttribute("query", query);
         model.addAttribute("subjectList", getExamSubject(examId, wu));
@@ -114,8 +121,7 @@ public class MarkerController extends BaseExamController {
 
     @RequestMapping("/query")
     @ResponseBody
-    public JSONArray query(HttpServletRequest request, @RequestParam String subjectCode,
-            @RequestParam Integer groupNumber) {
+    public JSONArray query(HttpServletRequest request, @RequestParam String subjectCode, @RequestParam Integer groupNumber) {
         int examId = getSessionExamId(request);
         JSONArray array = new JSONArray();
         List<Marker> list = markerService.findByExamAndSubjectAndGroup(examId, subjectCode, groupNumber);
@@ -154,8 +160,8 @@ public class MarkerController extends BaseExamController {
 
     @RequestMapping("/batch-create")
     @RoleRequire(Role.SCHOOL_ADMIN)
-    public String createInit(Model model, HttpServletRequest request,
-            @RequestParam(required = false) String subjectCode, @RequestParam(required = false) Integer groupNumber) {
+    public String createInit(Model model, HttpServletRequest request, @RequestParam(required = false) String subjectCode,
+            @RequestParam(required = false) Integer groupNumber) {
         int examId = getSessionExamId(request);
         model.addAttribute("subjectList", getExamSubject(examId, RequestUtils.getWebUser(request)));
         if (subjectCode != null) {
@@ -172,9 +178,8 @@ public class MarkerController extends BaseExamController {
 
     @RequestMapping(value = "/batch-create", method = RequestMethod.POST)
     @RoleRequire(Role.SCHOOL_ADMIN)
-    public ModelAndView create(HttpServletRequest request, @RequestParam String subjectCode,
-            @RequestParam Integer groupNumber, @RequestParam Integer count,
-            @RequestParam(required = false) String password) {
+    public ModelAndView create(HttpServletRequest request, @RequestParam String subjectCode, @RequestParam Integer groupNumber,
+            @RequestParam Integer count, @RequestParam(required = false) String password) {
         int examId = getSessionExamId(request);
         ExamSubject subject = subjectService.find(examId, subjectCode);
         ModelAndView view = new ModelAndView("redirect:/admin/exam/marker");
@@ -194,8 +199,7 @@ public class MarkerController extends BaseExamController {
         Marker marker = markerService.findById(id);
         if (marker != null) {
             try {
-                lockService.waitlock(LockType.GROUP, marker.getExamId(), marker.getSubjectCode(),
-                        marker.getGroupNumber());
+                lockService.waitlock(LockType.GROUP, marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
                 lockService.waitlock(LockType.MARKER, marker.getId());
 
                 markService.resetMarker(marker);
@@ -204,8 +208,7 @@ public class MarkerController extends BaseExamController {
                 throw new RuntimeException("重置评卷员失败", e);
             } finally {
                 lockService.unlock(LockType.MARKER, marker.getId());
-                lockService.unlock(LockType.GROUP, marker.getExamId(), marker.getSubjectCode(),
-                        marker.getGroupNumber());
+                lockService.unlock(LockType.GROUP, marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
             }
         }
         redirectAttributes.addAttribute("subjectCode", marker.getSubjectCode());
@@ -221,8 +224,7 @@ public class MarkerController extends BaseExamController {
         JSONObject obj = new JSONObject();
         if (marker != null) {
             try {
-                lockService.waitlock(LockType.GROUP, marker.getExamId(), marker.getSubjectCode(),
-                        marker.getGroupNumber());
+                lockService.waitlock(LockType.GROUP, marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
                 lockService.waitlock(LockType.MARKER, marker.getId());
 
                 markService.resetMarker(marker);
@@ -233,8 +235,7 @@ public class MarkerController extends BaseExamController {
                 obj.accumulate("message", "重置评卷员失败");
             } finally {
                 lockService.unlock(LockType.MARKER, marker.getId());
-                lockService.unlock(LockType.GROUP, marker.getExamId(), marker.getSubjectCode(),
-                        marker.getGroupNumber());
+                lockService.unlock(LockType.GROUP, marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
             }
         } else {
             obj.accumulate("success", false);
@@ -283,8 +284,8 @@ public class MarkerController extends BaseExamController {
                 }
                 MarkGroup group = groupService.findOne(examId, marker.getSubjectCode(), marker.getGroupNumber());
                 if (group != null) {
-                    group.setQuestionList(questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId, group.getSubjectCode(),
-                    false, group.getNumber()));
+                    group.setQuestionList(questionService
+                            .findByExamAndSubjectAndObjectiveAndGroupNumber(examId, group.getSubjectCode(), false, group.getNumber()));
                     marker.setGroupName(group.getNumber() + "-" + group.getTitle());
                 } else {
                     marker.setGroupName("");
@@ -414,8 +415,7 @@ public class MarkerController extends BaseExamController {
     }
 
     @RequestMapping(value = "/importUpdate", method = RequestMethod.POST)
-    public String importUpdateFile(HttpServletRequest request, MultipartFile file,
-            RedirectAttributes redirectAttributes) {
+    public String importUpdateFile(HttpServletRequest request, MultipartFile file, RedirectAttributes redirectAttributes) {
         try {
             int successNum = 0;
             int failureNum = 0;
@@ -432,9 +432,8 @@ public class MarkerController extends BaseExamController {
                 }
             }
             for (MarkerDTO markerDTO : list) {
-                if (StringUtils.isBlank(markerDTO.getSubjectCode()) || StringUtils.isBlank(markerDTO.getLoginName())
-                        || StringUtils.isBlank(markerDTO.getNewLoginName())
-                        || StringUtils.isBlank(markerDTO.getPassword())) {
+                if (StringUtils.isBlank(markerDTO.getSubjectCode()) || StringUtils.isBlank(markerDTO.getLoginName()) || StringUtils
+                        .isBlank(markerDTO.getNewLoginName()) || StringUtils.isBlank(markerDTO.getPassword())) {
                     continue;
                 }
                 MarkerExcelError markerExcelError = checkExcelData(markerDTO, current);
@@ -449,8 +448,8 @@ public class MarkerController extends BaseExamController {
                     marker.setPassword(markerDTO.getPassword());
                     saveList.add(marker);
                 } else {
-                    failureMsg.append("<br/>科目代码(" + markerDTO.getSubjectCode() + ")原评卷员(" + markerDTO.getLoginName()
-                            + ")更换新评卷员为(" + markerDTO.getNewLoginName() + ")失败!");
+                    failureMsg.append("<br/>科目代码(" + markerDTO.getSubjectCode() + ")原评卷员(" + markerDTO.getLoginName() + ")更换新评卷员为("
+                            + markerDTO.getNewLoginName() + ")失败!");
                     failureMsg.append("---->失败原因:(" + markerExcelError.getName() + ")");
                     failureNum++;
                 }
@@ -467,15 +466,13 @@ public class MarkerController extends BaseExamController {
         return "redirect:" + "/admin/exam/marker";
     }
 
-    private MarkerExcelError checkLongNameAndPassword(Marker marker, Map<String, ExamSubject> current,
-            Map<String, Marker> saveMap) {
+    private MarkerExcelError checkLongNameAndPassword(Marker marker, Map<String, ExamSubject> current, Map<String, Marker> saveMap) {
         Marker previous = null;
 
         if (current != null && current.get(marker.getSubjectCode()) == null) {
             return MarkerExcelError.MARKERNOTCODE;
         }
-        MarkGroup group = groupService.findOne(marker.getExamId(), marker.getSubjectCode(),
-                Integer.valueOf(marker.getGroupName()));
+        MarkGroup group = groupService.findOne(marker.getExamId(), marker.getSubjectCode(), Integer.valueOf(marker.getGroupName()));
         if (group == null) {
             return MarkerExcelError.MARKERNOTGROUP;
         } else {
@@ -530,32 +527,79 @@ public class MarkerController extends BaseExamController {
             return MarkerExcelError.MARKERNOTINFO;
         }
     }
-    
+
     @RequestMapping(value = "/class/{markerId}", method = RequestMethod.GET)
-    public String classAdd(@PathVariable Integer markerId, Model model,RedirectAttributes redirectAttributes) {
+    public String classAdd(@PathVariable Integer markerId, Model model, RedirectAttributes redirectAttributes) {
         Marker marker = markerService.findById(markerId);
         if (marker != null) {
             model.addAttribute("marker", marker);
-            List<String> classList = studentService.findDistinctClassName(marker.getExamId(),marker.getSubjectCode());
-            Set<String> classes  = classService.findClassNameByMarkerId(markerId);
+            List<String> classList = studentService.findDistinctClassName(marker.getExamId(), marker.getSubjectCode());
+            Set<String> classes = classService.findClassNameByMarkerId(markerId);
             classList.removeAll(classes);
             model.addAttribute("classList", classList);
             model.addAttribute("classes", classes);
             return "modules/exam/markerClass";
-        } 
+        }
         addMessage(redirectAttributes, "评卷员不存在");
         return "redirect:/admin/exam/marker";
     }
 
     @RequestMapping(value = "/class/{markerId}", method = RequestMethod.POST)
-    public String classSave(@PathVariable Integer markerId,@RequestParam(required=false) String[] classes,Model model,RedirectAttributes redirectAttributes) {
+    public String classSave(@PathVariable Integer markerId, @RequestParam(required = false) String[] classes, Model model,
+            RedirectAttributes redirectAttributes) {
         Marker marker = markerService.findById(markerId);
         if (marker != null) {
-            classService.save(marker,classes);
+            classService.save(marker, classes);
             addMessage(redirectAttributes, "保存'" + marker.getLoginName() + "'成功");
-            return "redirect:" + "/admin/exam/marker?subjectCode=" + marker.getSubjectCode()+"&groupNumber="+marker.getGroupNumber();
-        } 
+            return "redirect:" + "/admin/exam/marker?subjectCode=" + marker.getSubjectCode() + "&groupNumber=" + marker.getGroupNumber();
+        }
+        addMessage(redirectAttributes, "评卷员不存在");
+        return "redirect:/admin/exam/marker";
+    }
+
+    @RequestMapping(value = "/account/{markerId}")
+    public String editAccount(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
+            @PathVariable Integer markerId) {
+        Exam exam = examService.findById(getSessionExamId(request));
+        Marker marker = markerService.findById(markerId);
+        if (exam != null && marker != null && exam.getId().equals(marker.getExamId())) {
+            model.addAttribute("marker", marker);
+            model.addAttribute("openAccountList", userService
+                    .listUnbindOpenAccount(exam.getSchoolId(), marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber()));
+            return "modules/exam/markerAccount";
+        }
         addMessage(redirectAttributes, "评卷员不存在");
         return "redirect:/admin/exam/marker";
     }
+
+    @RequestMapping(value = "/account/bind", method = RequestMethod.POST)
+    @ResponseBody
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
+    public boolean bindAccount(@RequestParam Integer markerId, @RequestParam AccountType accountType, @RequestParam Integer accountId) {
+        Marker marker = markerService.findById(markerId);
+        if (marker == null) {
+            return false;
+        }
+        if (accountType == AccountType.OPEN_ACCOUNT) {
+            OpenAccount oa = userService.findOpenAccount(accountId);
+            if (oa == null || !oa.isEnable()) {
+                return false;
+            }
+        }
+        if (markerService
+                .countByExamAndSubjectAndGroupAndAccount(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber(), accountType,
+                        accountId) == 0) {
+            markerService.updateAccountById(markerId, accountType, accountId);
+            return true;
+        }
+        return false;
+    }
+
+    @RequestMapping(value = "/account/unbind", method = RequestMethod.POST)
+    @ResponseBody
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
+    public boolean unbindOpenAccount(@RequestParam Integer markerId) {
+        markerService.updateAccountById(markerId, null, null);
+        return true;
+    }
 }

+ 21 - 23
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/PaperController.java

@@ -40,8 +40,7 @@ import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
 import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
 import cn.com.qmth.stmms.biz.exam.service.query.ExamQuestionSearchQuery;
 import cn.com.qmth.stmms.biz.exam.service.query.ExamSubjectSearchQuery;
-import cn.com.qmth.stmms.biz.mark.model.PictureConfigItem;
-import cn.com.qmth.stmms.common.auth.annotation.RoleRequire;
+import cn.com.qmth.stmms.common.annotation.RoleRequire;
 import cn.com.qmth.stmms.common.domain.WebUser;
 import cn.com.qmth.stmms.common.enums.ObjectivePolicy;
 import cn.com.qmth.stmms.common.enums.Role;
@@ -49,9 +48,6 @@ import cn.com.qmth.stmms.common.utils.ExportExcel;
 import cn.com.qmth.stmms.common.utils.ImportExcel;
 import cn.com.qmth.stmms.common.utils.RequestUtils;
 
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
 @Controller("examPaperController")
 @RequestMapping("/admin/exam/paper")
 public class PaperController extends BaseExamController {
@@ -122,15 +118,14 @@ public class PaperController extends BaseExamController {
     }
 
     @RequestMapping(value = "/template")
-    public String importTemplate(HttpServletResponse response, @RequestParam Boolean objective,
-            RedirectAttributes redirectAttributes) {
+    public String importTemplate(HttpServletResponse response, @RequestParam Boolean objective, RedirectAttributes redirectAttributes) {
         try {
             String fileName = objective ? "客观题导入模板.xlsx" : "主观题导入模板.xlsx";
             String title = objective ? "客观题数据" : "主观题数据";
             List<QuestionDTO> list = new LinkedList<QuestionDTO>();
             list.add(objective ? new ObjectiveQuestionDTO() : new SubjectiveQuestionDTO());
-            new ExportExcel(title, objective ? ObjectiveQuestionDTO.class : SubjectiveQuestionDTO.class, 2)
-                    .setDataList(list).write(response, fileName).dispose();
+            new ExportExcel(title, objective ? ObjectiveQuestionDTO.class : SubjectiveQuestionDTO.class, 2).setDataList(list)
+                    .write(response, fileName).dispose();
             return null;
         } catch (Exception e) {
             addMessage(redirectAttributes, "导入模板下载失败!失败信息:" + e.getMessage());
@@ -164,15 +159,16 @@ public class PaperController extends BaseExamController {
         query.setPageSize(Integer.MAX_VALUE);
         query = questionService.findByQuery(query);
         for (ExamQuestion q : query.getResult()) {
-            list.add(objective ? new ObjectiveQuestionDTO(q, subjectMap.get(q.getSubjectCode()))
-                    : new SubjectiveQuestionDTO(q, subjectMap.get(q.getSubjectCode()),
+            list.add(objective ?
+                    new ObjectiveQuestionDTO(q, subjectMap.get(q.getSubjectCode())) :
+                    new SubjectiveQuestionDTO(q, subjectMap.get(q.getSubjectCode()),
                             groupMap.get(q.getSubjectCode() + "_" + q.getGroupNumber())));
         }
         try {
             String fileName = objective ? "客观题数据.xlsx" : "主观题数据.xlsx";
             String title = objective ? "客观题数据" : "主观题数据";
-            new ExportExcel(title, objective ? ObjectiveQuestionDTO.class : SubjectiveQuestionDTO.class, 2)
-                    .setDataList(list).write(response, fileName).dispose();
+            new ExportExcel(title, objective ? ObjectiveQuestionDTO.class : SubjectiveQuestionDTO.class, 2).setDataList(list)
+                    .write(response, fileName).dispose();
             return null;
         } catch (Exception e) {
             addMessage(redirectAttributes, "导出数据失败!失败信息:" + e.getMessage());
@@ -209,13 +205,16 @@ public class PaperController extends BaseExamController {
                                 if (old == null && group.getQuestionMap() != null) {
                                     for (Entry<Integer, List<ExamQuestion>> entry : group.getQuestionMap().entrySet()) {
                                         // 已存在的大题跳过
-                                        if (questionService.countByExamAndSubjectAndObjectiveAndMainNumber(examId,
-                                                group.getSubjectCode(), objective, entry.getKey()) == 0) {
+                                        if (questionService
+                                                .countByExamAndSubjectAndObjectiveAndMainNumber(examId, group.getSubjectCode(), objective,
+                                                        entry.getKey()) == 0) {
                                             questionService.save(entry.getValue());
                                         }
                                     }
                                     //有题目的分组才保存
-                                    if(questionService.countByExamAndSubjectAndObjectiveAndGroupNumber(examId, group.getSubjectCode(), objective, group.getNumber())!=0){
+                                    if (questionService
+                                            .countByExamAndSubjectAndObjectiveAndGroupNumber(examId, group.getSubjectCode(), objective,
+                                                    group.getNumber()) != 0) {
                                         groupService.save(group);
                                     }
                                 }
@@ -241,8 +240,7 @@ public class PaperController extends BaseExamController {
         return "redirect:/admin/exam/paper";
     }
 
-    private Map<String, SubjectQuestionDTO> parseQuestion(MultipartFile file, int examId, boolean objective,
-            List<String> error) {
+    private Map<String, SubjectQuestionDTO> parseQuestion(MultipartFile file, int examId, boolean objective, List<String> error) {
         Map<String, SubjectQuestionDTO> map = new HashMap<String, SubjectQuestionDTO>();
         try {
             ImportExcel excel = new ImportExcel(file, 1, 0);
@@ -281,21 +279,21 @@ public class PaperController extends BaseExamController {
         }
         return array;
     }
-    
+
     @RequestMapping(value = "/question-edit/{questionId}", method = RequestMethod.GET)
     @RoleRequire(Role.SCHOOL_ADMIN)
-    public String edit(Model model,@PathVariable Integer questionId) {
+    public String edit(Model model, @PathVariable Integer questionId) {
         ExamQuestion examQuestion = questionService.findById(questionId);
         model.addAttribute("examQuestion", examQuestion);
         model.addAttribute("objectivePolicyList", ObjectivePolicy.values());
         return "modules/exam/questionEdit";
     }
-    
+
     @RequestMapping(value = "/question-edit", method = RequestMethod.POST)
     @RoleRequire(Role.SCHOOL_ADMIN)
     public String update(@RequestParam Integer id, @RequestParam ObjectivePolicy objectivePolicy) {
         ExamQuestion question = questionService.updateObjectivePolicy(id, objectivePolicy);
-        return "redirect:/admin/exam/paper/detail?subjectCode="+question.getSubjectCode();
+        return "redirect:/admin/exam/paper/detail?subjectCode=" + question.getSubjectCode();
     }
-    
+
 }

+ 115 - 113
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ScoreCheckController.java

@@ -1,113 +1,115 @@
-package cn.com.qmth.stmms.admin.exam;
-
-import java.util.LinkedList;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.core.task.AsyncTaskExecutor;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.servlet.ModelAndView;
-import org.springframework.web.servlet.mvc.support.RedirectAttributes;
-
-import cn.com.qmth.stmms.admin.dto.ExceptionQuestionDTO;
-import cn.com.qmth.stmms.admin.thread.ScoreCheckThread;
-import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
-import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
-import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
-import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
-import cn.com.qmth.stmms.biz.exam.service.query.ExamQuestionSearchQuery;
-import cn.com.qmth.stmms.common.auth.annotation.RoleRequire;
-import cn.com.qmth.stmms.common.domain.WebUser;
-import cn.com.qmth.stmms.common.enums.Role;
-import cn.com.qmth.stmms.common.utils.ExportExcel;
-import cn.com.qmth.stmms.common.utils.RequestUtils;
-
-@Controller
-@RequestMapping("/admin/exam/check/score")
-public class ScoreCheckController extends BaseExamController {
-
-    @Autowired
-    private ExamStudentService studentService;
-
-    @Autowired
-    private ExamQuestionService questionService;
-
-    @Autowired
-    private ExamSubjectService subjectService;
-
-    @Autowired
-    private AsyncTaskExecutor taskExecutor;
-
-    private AtomicBoolean running = new AtomicBoolean(false);
-
-    @RequestMapping
-    public ModelAndView list(HttpServletRequest request, ExamQuestionSearchQuery query) {
-        WebUser wu = RequestUtils.getWebUser(request);
-        int examId = getSessionExamId(request);
-        ModelAndView view = new ModelAndView("modules/exam/checkScore");
-        query.setExamId(examId);
-        query.setTotalScoreGt(0d);
-        query.setTotalCountGt(0);
-        query.orderBySubjectAndNumber();
-        if (query.getZeroRateGt() == null) {
-            query.setZeroRateGt(1.0);
-        }
-        subjectFilter(query, wu);
-        query = questionService.findByQuery(query);
-
-        for (ExamQuestion q : query.getResult()) {
-            q.setSubject((subjectService.find(q.getExamId(), q.getSubjectCode())));
-        }
-
-        view.addObject("query", query);
-        view.addObject("subjectList", getExamSubject(examId, wu));
-        view.addObject("running", running.get());
-        return view;
-    }
-
-    @RequestMapping("/run")
-    @RoleRequire(Role.SCHOOL_ADMIN)
-    public ModelAndView checkScore(HttpServletRequest request) {
-        int examId = getSessionExamId(request);
-        if (running.compareAndSet(false, true) == true) {
-            ScoreCheckThread thread = new ScoreCheckThread(examId, running, studentService, questionService);
-            taskExecutor.submit(thread);
-        }
-        return new ModelAndView("redirect:/admin/exam/check/score");
-    }
-
-    @RequestMapping("/export")
-    public String export(ExamQuestionSearchQuery query, HttpServletRequest request, HttpServletResponse response,
-            RedirectAttributes redirectAttributes) {
-        WebUser wu = RequestUtils.getWebUser(request);
-        int examId = getSessionExamId(request);
-        List<ExceptionQuestionDTO> list = new LinkedList<ExceptionQuestionDTO>();
-        query.setExamId(examId);
-        query.setTotalCountGt(0);
-        query.orderBySubjectAndNumber();
-        query.setPageNumber(1);
-        query.setPageSize(Integer.MAX_VALUE);
-        subjectFilter(query, wu);
-        query = questionService.findByQuery(query);
-        for (ExamQuestion question : query.getResult()) {
-            if (question.getTotalScore() > 0) {
-                ExceptionQuestionDTO dto = new ExceptionQuestionDTO(question,
-                        subjectService.find(question.getExamId(), question.getSubjectCode()));
-                list.add(dto);
-            }
-        }
-        String fileName = "异常试题.xlsx";
-        try {
-            new ExportExcel("异常试题", ExceptionQuestionDTO.class).setDataList(list).write(response, fileName).dispose();
-            return null;
-        } catch (Exception e) {
-            addMessage(redirectAttributes, "导出失败!" + e.getMessage());
-            return "redirect:/admin/exam/check/score";
-        }
-    }
-}
+package cn.com.qmth.stmms.admin.exam;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.core.task.AsyncTaskExecutor;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
+import cn.com.qmth.stmms.admin.dto.ExceptionQuestionDTO;
+import cn.com.qmth.stmms.admin.thread.ScoreCheckThread;
+import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
+import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
+import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
+import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
+import cn.com.qmth.stmms.biz.exam.service.query.ExamQuestionSearchQuery;
+import cn.com.qmth.stmms.common.annotation.RoleRequire;
+import cn.com.qmth.stmms.common.domain.WebUser;
+import cn.com.qmth.stmms.common.enums.Role;
+import cn.com.qmth.stmms.common.utils.ExportExcel;
+import cn.com.qmth.stmms.common.utils.RequestUtils;
+
+@Controller
+@RequestMapping("/admin/exam/check/score")
+public class ScoreCheckController extends BaseExamController {
+
+    @Autowired
+    private ExamStudentService studentService;
+
+    @Autowired
+    private ExamQuestionService questionService;
+
+    @Autowired
+    private ExamSubjectService subjectService;
+
+    @Qualifier("task-executor")
+    @Autowired
+    private AsyncTaskExecutor taskExecutor;
+
+    private AtomicBoolean running = new AtomicBoolean(false);
+
+    @RequestMapping
+    public ModelAndView list(HttpServletRequest request, ExamQuestionSearchQuery query) {
+        WebUser wu = RequestUtils.getWebUser(request);
+        int examId = getSessionExamId(request);
+        ModelAndView view = new ModelAndView("modules/exam/checkScore");
+        query.setExamId(examId);
+        query.setTotalScoreGt(0d);
+        query.setTotalCountGt(0);
+        query.orderBySubjectAndNumber();
+        if (query.getZeroRateGt() == null) {
+            query.setZeroRateGt(1.0);
+        }
+        subjectFilter(query, wu);
+        query = questionService.findByQuery(query);
+
+        for (ExamQuestion q : query.getResult()) {
+            q.setSubject((subjectService.find(q.getExamId(), q.getSubjectCode())));
+        }
+
+        view.addObject("query", query);
+        view.addObject("subjectList", getExamSubject(examId, wu));
+        view.addObject("running", running.get());
+        return view;
+    }
+
+    @RequestMapping("/run")
+    @RoleRequire(Role.SCHOOL_ADMIN)
+    public ModelAndView checkScore(HttpServletRequest request) {
+        int examId = getSessionExamId(request);
+        if (running.compareAndSet(false, true) == true) {
+            ScoreCheckThread thread = new ScoreCheckThread(examId, running, studentService, questionService);
+            taskExecutor.submit(thread);
+        }
+        return new ModelAndView("redirect:/admin/exam/check/score");
+    }
+
+    @RequestMapping("/export")
+    public String export(ExamQuestionSearchQuery query, HttpServletRequest request, HttpServletResponse response,
+            RedirectAttributes redirectAttributes) {
+        WebUser wu = RequestUtils.getWebUser(request);
+        int examId = getSessionExamId(request);
+        List<ExceptionQuestionDTO> list = new LinkedList<ExceptionQuestionDTO>();
+        query.setExamId(examId);
+        query.setTotalCountGt(0);
+        query.orderBySubjectAndNumber();
+        query.setPageNumber(1);
+        query.setPageSize(Integer.MAX_VALUE);
+        subjectFilter(query, wu);
+        query = questionService.findByQuery(query);
+        for (ExamQuestion question : query.getResult()) {
+            if (question.getTotalScore() > 0) {
+                ExceptionQuestionDTO dto = new ExceptionQuestionDTO(question,
+                        subjectService.find(question.getExamId(), question.getSubjectCode()));
+                list.add(dto);
+            }
+        }
+        String fileName = "异常试题.xlsx";
+        try {
+            new ExportExcel("异常试题", ExceptionQuestionDTO.class).setDataList(list).write(response, fileName).dispose();
+            return null;
+        } catch (Exception e) {
+            addMessage(redirectAttributes, "导出失败!" + e.getMessage());
+            return "redirect:/admin/exam/check/score";
+        }
+    }
+}

+ 27 - 30
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ScoreController.java

@@ -12,6 +12,7 @@ import net.sf.json.JSONObject;
 
 import org.apache.commons.lang.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.core.task.AsyncTaskExecutor;
 import org.springframework.stereotype.Controller;
@@ -43,7 +44,7 @@ import cn.com.qmth.stmms.biz.lock.LockService;
 import cn.com.qmth.stmms.biz.mark.service.MarkService;
 import cn.com.qmth.stmms.biz.report.service.ReportService;
 import cn.com.qmth.stmms.biz.utils.ScoreItem;
-import cn.com.qmth.stmms.common.auth.annotation.RoleRequire;
+import cn.com.qmth.stmms.common.annotation.RoleRequire;
 import cn.com.qmth.stmms.common.domain.WebUser;
 import cn.com.qmth.stmms.common.enums.LockType;
 import cn.com.qmth.stmms.common.enums.MarkStatus;
@@ -74,6 +75,7 @@ public class ScoreController extends BaseExamController {
     @Autowired
     private MarkGroupService groupService;
 
+    @Qualifier("task-executor")
     @Autowired
     private AsyncTaskExecutor taskExecutor;
 
@@ -99,8 +101,7 @@ public class ScoreController extends BaseExamController {
     private String cardServer;
 
     @RequestMapping
-    public ModelAndView list(HttpServletRequest request, ExamStudentSearchQuery query,
-            @RequestParam(defaultValue = "0") Integer filter) {
+    public ModelAndView list(HttpServletRequest request, ExamStudentSearchQuery query, @RequestParam(defaultValue = "0") Integer filter) {
         WebUser wu = RequestUtils.getWebUser(request);
         int examId = getSessionExamId(request);
         String subjectCode = RequestUtils.getSession(request).getParameter("subjectCode");
@@ -124,8 +125,9 @@ public class ScoreController extends BaseExamController {
             buildPackageUrl(student);
             buildAnswerUrl(student);
         }
-        String exportMessage = StringUtils.isNotBlank(query.getSubjectCode()) ? enableExport(examId,
-                query.getSubjectCode()) : enableExport(examId);
+        String exportMessage = StringUtils.isNotBlank(query.getSubjectCode()) ?
+                enableExport(examId, query.getSubjectCode()) :
+                enableExport(examId);
         if (exportMessage != null) {
             view.addObject("exportMessage", exportMessage);
             view.addObject("enableExport", false);
@@ -143,8 +145,7 @@ public class ScoreController extends BaseExamController {
     }
 
     @RequestMapping("/detail")
-    public ModelAndView detail(HttpServletRequest request, @RequestParam String examNumber,
-            @RequestParam String queryString) {
+    public ModelAndView detail(HttpServletRequest request, @RequestParam String examNumber, @RequestParam String queryString) {
         int examId = getSessionExamId(request);
         ExamStudent student = studentService.findByExamIdAndExamNumber(examId, examNumber);
         if (student != null) {
@@ -202,8 +203,8 @@ public class ScoreController extends BaseExamController {
     public ModelAndView calculate(HttpServletRequest request) {
         int examId = getSessionExamId(request);
         if (lockService.trylock(LockType.SCORE_CALCULATE, examId)) {
-            ScoreCalculateThread thread = new ScoreCalculateThread(examId, lockService, studentService,
-                    questionService, markService, reportService, examService, subjectService, groupService);
+            ScoreCalculateThread thread = new ScoreCalculateThread(examId, lockService, studentService, questionService, markService,
+                    reportService, examService, subjectService, groupService);
             taskExecutor.submit(thread);
         }
         return new ModelAndView("redirect:/admin/exam/score");
@@ -214,8 +215,9 @@ public class ScoreController extends BaseExamController {
             RedirectAttributes redirectAttributes) {
         WebUser wu = RequestUtils.getWebUser(request);
         int examId = getSessionExamId(request);
-        String exportMessage = StringUtils.isNotBlank(query.getSubjectCode()) ? enableExport(examId,
-                query.getSubjectCode()) : enableExport(examId);
+        String exportMessage = StringUtils.isNotBlank(query.getSubjectCode()) ?
+                enableExport(examId, query.getSubjectCode()) :
+                enableExport(examId);
         if (exportMessage == null && StringUtils.isNotBlank(query.getSubjectCode())) {
             exportMessage = enableExport(examId, query.getSubjectCode());
         }
@@ -258,8 +260,7 @@ public class ScoreController extends BaseExamController {
 
     private List<String> getOptionHeader(int examId, String subjectCode, String paerType) {
         List<String> headerList = new ArrayList<String>();
-        List<ExamQuestion> oQuestions = questionService.findByExamAndSubjectAndObjectiveAndPaperType(examId,
-                subjectCode, true, paerType);
+        List<ExamQuestion> oQuestions = questionService.findByExamAndSubjectAndObjectiveAndPaperType(examId, subjectCode, true, paerType);
         List<ExamQuestion> sQestions = questionService.findByExamAndSubjectAndObjective(examId, subjectCode, false);
         for (ExamQuestion examQuestion : oQuestions) {
             headerList.add(getTitle(examQuestion) + "选项");
@@ -315,8 +316,8 @@ public class ScoreController extends BaseExamController {
     private List<ScoreItem> buildScoreList(ExamStudent student) {
         List<ScoreItem> scoreList = new LinkedList<ScoreItem>();
 
-        List<ExamQuestion> oList = questionService.findByExamAndSubjectAndObjectiveAndPaperType(student.getExamId(),
-                student.getSubjectCode(), true, student.getPaperType());
+        List<ExamQuestion> oList = questionService
+                .findByExamAndSubjectAndObjectiveAndPaperType(student.getExamId(), student.getSubjectCode(), true, student.getPaperType());
         List<ScoreItem> list1 = student.getScoreList(true);
         int index = 0;
         for (ExamQuestion question : oList) {
@@ -331,8 +332,7 @@ public class ScoreController extends BaseExamController {
         }
         scoreList.addAll(list1);
 
-        List<ExamQuestion> sList = questionService.findByExamAndSubjectAndObjective(student.getExamId(),
-                student.getSubjectCode(), false);
+        List<ExamQuestion> sList = questionService.findByExamAndSubjectAndObjective(student.getExamId(), student.getSubjectCode(), false);
         List<ScoreItem> list2 = student.getScoreList(false);
         index = 0;
         for (ExamQuestion question : sList) {
@@ -352,16 +352,14 @@ public class ScoreController extends BaseExamController {
 
     private void buildSheetUrl(ExamStudent student) {
         // 改为内部原图浏览地址,直接附带标记内容
-        student.setSheetUrls(PictureUrlBuilder.getInnerSheetUrls(student.getExamId(), student.getExamNumber(),
-                student.getSheetCount()));
+        student.setSheetUrls(PictureUrlBuilder.getInnerSheetUrls(student.getExamId(), student.getExamNumber(), student.getSheetCount()));
     }
 
     private void buildPackageUrl(ExamStudent student) {
         if (StringUtils.isNotBlank(student.getPackageCode())) {
             ExamPackage ep = packageService.find(student.getExamId(), student.getPackageCode());
             if (ep != null && ep.getPicCount() > 0) {
-                student.setPackageUrls(PictureUrlBuilder.getPackageUrls(student.getExamId(), student.getPackageCode(),
-                        ep.getPicCount()));
+                student.setPackageUrls(PictureUrlBuilder.getPackageUrls(student.getExamId(), student.getPackageCode(), ep.getPicCount()));
             }
         }
     }
@@ -382,12 +380,12 @@ public class ScoreController extends BaseExamController {
             String message = "该考试需要统分";
             return message;
         }
-        
+
         if (checkStudentService.countByExamIdAndChecked(examId, false) != 0) {
             String message = "人工确认未完成";
             return message;
         }
-        
+
         ExamStudentSearchQuery query = new ExamStudentSearchQuery();
         query.setExamId(examId);
         query.setUpload(false);
@@ -397,11 +395,11 @@ public class ScoreController extends BaseExamController {
             String message = "未上传考生必须人工指定缺考";
             return message;
         }
-        
+
         List<ExamSubject> subjects = subjectService.list(examId);
         for (ExamSubject examSubject : subjects) {
-            List<MarkGroup> groups = groupService.findByExamAndSubjectAndStatus(examId, examSubject.getCode(), MarkStatus.FORMAL,
-                    MarkStatus.TRIAL);
+            List<MarkGroup> groups = groupService
+                    .findByExamAndSubjectAndStatus(examId, examSubject.getCode(), MarkStatus.FORMAL, MarkStatus.TRIAL);
             if (groups != null && !groups.isEmpty()) {
                 String message = examSubject.getCode() + " 科目未评卷完成";
                 return message;
@@ -418,8 +416,7 @@ public class ScoreController extends BaseExamController {
             return message;
         }
 
-        List<MarkGroup> groups = groupService.findByExamAndSubjectAndStatus(examId, subjectCode, MarkStatus.FORMAL,
-                MarkStatus.TRIAL);
+        List<MarkGroup> groups = groupService.findByExamAndSubjectAndStatus(examId, subjectCode, MarkStatus.FORMAL, MarkStatus.TRIAL);
         if (groups != null && !groups.isEmpty()) {
             String message = subjectCode + " 科目未评卷完成";
             return message;
@@ -429,7 +426,7 @@ public class ScoreController extends BaseExamController {
             String message = "人工确认未完成";
             return message;
         }
-        
+
         ExamStudentSearchQuery query = new ExamStudentSearchQuery();
         query.setExamId(examId);
         query.setUpload(false);
@@ -440,7 +437,7 @@ public class ScoreController extends BaseExamController {
             String message = "未上传考生必须人工指定缺考";
             return message;
         }
-        
+
         return null;
     }
 

+ 18 - 26
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/StudentController.java

@@ -37,7 +37,7 @@ import cn.com.qmth.stmms.biz.exam.service.ExamService;
 import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
 import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
 import cn.com.qmth.stmms.biz.mark.service.MarkService;
-import cn.com.qmth.stmms.common.auth.annotation.RoleRequire;
+import cn.com.qmth.stmms.common.annotation.RoleRequire;
 import cn.com.qmth.stmms.common.domain.WebUser;
 import cn.com.qmth.stmms.common.enums.Role;
 import cn.com.qmth.stmms.common.utils.ExportExcel;
@@ -128,8 +128,8 @@ public class StudentController extends BaseExamController {
 
     @RequestMapping(value = "/save")
     @RoleRequire(Role.SCHOOL_ADMIN)
-    public String save(ExamStudent student, Model model, RedirectAttributes redirectAttributes,
-            HttpServletRequest request, HttpServletResponse response) {
+    public String save(ExamStudent student, Model model, RedirectAttributes redirectAttributes, HttpServletRequest request,
+            HttpServletResponse response) {
         int examId = getSessionExamId(request);
         Exam exam = examService.findById(examId);
         ExamSubject subject = subjectService.find(examId, student.getSubjectCode());
@@ -144,8 +144,7 @@ public class StudentController extends BaseExamController {
                 if (previous != null) {
                     // String previousSubjectCode = previous.getSubjectCode();
                     student.setExamId(examId);
-                    ExamStudent old = checkExamNumber(student, new HashMap<String, ExamStudent>(),
-                            new HashMap<String, ExamStudent>());
+                    ExamStudent old = checkExamNumber(student, new HashMap<String, ExamStudent>(), new HashMap<String, ExamStudent>());
                     if (old != null && !old.getExamNumber().equals(previous.getExamNumber())) {
                         addMessage(redirectAttributes, "准考证号" + student.getExamNumber() + "已经存在");
                         return "redirect:/admin/exam/student";
@@ -196,8 +195,7 @@ public class StudentController extends BaseExamController {
                 student.setSheetCount(0);
                 student.setObjectiveScore(0d);
                 student.setSubjectiveScore(0d);
-                ExamStudent old = checkExamNumber(student, new HashMap<String, ExamStudent>(),
-                        new HashMap<String, ExamStudent>());
+                ExamStudent old = checkExamNumber(student, new HashMap<String, ExamStudent>(), new HashMap<String, ExamStudent>());
                 if (old != null) {
                     addMessage(redirectAttributes, "准考证号" + student.getExamNumber() + "已经存在");
                     return "redirect:/admin/exam/student";
@@ -217,11 +215,8 @@ public class StudentController extends BaseExamController {
         if (student != null) {
             studentService.deleteById(id);
             markService.deleteByStudent(student);
-            subjectService.updateUploadCount(
-                    student.getExamId(),
-                    student.getSubjectCode(),
-                    (int) studentService.countUploadedByExamIdAndSubjectCode(student.getExamId(),
-                            student.getSubjectCode()));
+            subjectService.updateUploadCount(student.getExamId(), student.getSubjectCode(),
+                    (int) studentService.countUploadedByExamIdAndSubjectCode(student.getExamId(), student.getSubjectCode()));
             addMessage(redirectAttributes, "删除考生成功");
         } else {
             addMessage(redirectAttributes, "找不到对应的考生");
@@ -267,12 +262,11 @@ public class StudentController extends BaseExamController {
             }
 
             for (ExamStudent student : list) {
-                if (StringUtils.isBlank(student.getExamNumber()) || StringUtils.isBlank(student.getName())
-                        || StringUtils.isBlank(student.getSubjectCode())
-                        || StringUtils.isBlank(student.getSubjectName())
-                        || StringUtils.isBlank(student.getCampusName()) || StringUtils.isBlank(student.getCollege())
-                        || StringUtils.isBlank(student.getClassName()) || StringUtils.isBlank(student.getTeacher())
-                        || StringUtils.isBlank(student.getStudentCode())) {
+                if (StringUtils.isBlank(student.getExamNumber()) || StringUtils.isBlank(student.getName()) || StringUtils
+                        .isBlank(student.getSubjectCode()) || StringUtils.isBlank(student.getSubjectName()) || StringUtils
+                        .isBlank(student.getCampusName()) || StringUtils.isBlank(student.getCollege()) || StringUtils
+                        .isBlank(student.getClassName()) || StringUtils.isBlank(student.getTeacher()) || StringUtils
+                        .isBlank(student.getStudentCode())) {
                     continue;
                 }
 
@@ -326,8 +320,7 @@ public class StudentController extends BaseExamController {
                 student.setSubjectRemark(subject != null ? subject.getRemark() : "");
             }
             String fileName = "考生数据.xlsx";
-            new ExportExcel("考生数据", ExamStudent.class).setDataList(query.getResult()).write(response, fileName)
-                    .dispose();
+            new ExportExcel("考生数据", ExamStudent.class).setDataList(query.getResult()).write(response, fileName).dispose();
             return null;
         } catch (Exception e) {
             addMessage(redirectAttributes, "导出考生数据失败!" + e.getMessage());
@@ -466,8 +459,7 @@ public class StudentController extends BaseExamController {
         return result;
     }
 
-    private ExamStudent checkExamNumber(ExamStudent student, Map<String, ExamStudent> current,
-            Map<String, ExamStudent> saveMap) {
+    private ExamStudent checkExamNumber(ExamStudent student, Map<String, ExamStudent> current, Map<String, ExamStudent> saveMap) {
         ExamStudent previous = saveMap.get(student.getExamNumber());
 
         if (previous != null) {
@@ -499,16 +491,16 @@ public class StudentController extends BaseExamController {
 
     private void buildSheetUrl(ExamStudent student) {
         Campus campus = campusService.findBySchoolAndName(student.getSchoolId(), student.getCampusName());
-        student.setSheetUrls(PictureUrlBuilder.getSheetUrls(student.getExamId(), campus.getId(),
-                student.getSubjectCode(), student.getExamNumber(), student.getSheetCount()));
+        student.setSheetUrls(PictureUrlBuilder
+                .getSheetUrls(student.getExamId(), campus.getId(), student.getSubjectCode(), student.getExamNumber(),
+                        student.getSheetCount()));
     }
 
     private void buildPackageUrl(ExamStudent student) {
         if (StringUtils.isNotBlank(student.getPackageCode())) {
             ExamPackage ep = packageService.find(student.getExamId(), student.getPackageCode());
             if (ep != null && ep.getPicCount() > 0) {
-                student.setPackageUrls(PictureUrlBuilder.getPackageUrls(student.getExamId(), student.getPackageCode(),
-                        ep.getPicCount()));
+                student.setPackageUrls(PictureUrlBuilder.getPackageUrls(student.getExamId(), student.getPackageCode(), ep.getPicCount()));
             }
         }
     }

+ 1 - 1
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/SubjectController.java

@@ -19,7 +19,7 @@ import org.springframework.web.bind.annotation.RequestParam;
 import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
 import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
 import cn.com.qmth.stmms.biz.mark.model.PictureConfigItem;
-import cn.com.qmth.stmms.common.auth.annotation.RoleRequire;
+import cn.com.qmth.stmms.common.annotation.RoleRequire;
 import cn.com.qmth.stmms.common.enums.Role;
 
 @Controller

+ 13 - 18
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/TrialController.java

@@ -30,7 +30,7 @@ import cn.com.qmth.stmms.biz.mark.model.TrialLibrary;
 import cn.com.qmth.stmms.biz.mark.query.TrialLibrarySearchQuery;
 import cn.com.qmth.stmms.biz.mark.service.MarkService;
 import cn.com.qmth.stmms.biz.mark.service.TrialService;
-import cn.com.qmth.stmms.common.auth.annotation.RoleRequire;
+import cn.com.qmth.stmms.common.annotation.RoleRequire;
 import cn.com.qmth.stmms.common.domain.WebUser;
 import cn.com.qmth.stmms.common.enums.LibraryStatus;
 import cn.com.qmth.stmms.common.enums.LockType;
@@ -65,7 +65,7 @@ public class TrialController extends BaseExamController {
 
     @Autowired
     private LockService lockService;
-    
+
     @Autowired
     private ExamQuestionService questionService;
 
@@ -86,8 +86,7 @@ public class TrialController extends BaseExamController {
             query.setSubjectCode(subjectList.get(0).getCode());
         }
         subjectFilter(query, wu);
-        List<MarkGroup> groupList = groupService.findByExamAndSubjectAndStatus(examId, query.getSubjectCode(),
-                MarkStatus.TRIAL);
+        List<MarkGroup> groupList = groupService.findByExamAndSubjectAndStatus(examId, query.getSubjectCode(), MarkStatus.TRIAL);
         if (!groupList.isEmpty()) {
             if (query.getGroupNumber() == null) {
                 query.setGroupNumber(groupList.get(0).getNumber());
@@ -98,11 +97,10 @@ public class TrialController extends BaseExamController {
                 library.setMarkCount(trialService.countHistory(library.getId()));
             }
             for (MarkGroup group : groupList) {
-                group.setQuestionList(questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId,
-                        group.getSubjectCode(), false, group.getNumber()));
+                group.setQuestionList(questionService
+                        .findByExamAndSubjectAndObjectiveAndGroupNumber(examId, group.getSubjectCode(), false, group.getNumber()));
             }
-            model.addAttribute("group",
-                    groupService.findOne(query.getExamId(), query.getSubjectCode(), query.getGroupNumber()));
+            model.addAttribute("group", groupService.findOne(query.getExamId(), query.getSubjectCode(), query.getGroupNumber()));
         }
         model.addAttribute("query", query);
         model.addAttribute("subjectList", getExamSubject(examId, wu));
@@ -118,11 +116,9 @@ public class TrialController extends BaseExamController {
         int examId = getSessionExamId(request);
         TrialLibrary library = trialService.findLibrary(libraryId);
         if (library != null) {
-            if (library.getExamId().equals(examId)
-                    && subjectCheck(library.getSubjectCode(), RequestUtils.getWebUser(request))) {
+            if (library.getExamId().equals(examId) && subjectCheck(library.getSubjectCode(), RequestUtils.getWebUser(request))) {
                 try {
-                    lockService.watch(LockType.GROUP, library.getExamId(), library.getSubjectCode(),
-                            library.getGroupNumber());
+                    lockService.watch(LockType.GROUP, library.getExamId(), library.getSubjectCode(), library.getGroupNumber());
                     lockService.waitlock(LockType.TRIAL_LIBRARY, library.getId());
                     if (markService.resetLibrary(library)) {
                         obj.accumulate("success", true);
@@ -136,8 +132,7 @@ public class TrialController extends BaseExamController {
                     obj.accumulate("message", "无法打回该评卷任务");
                 } finally {
                     lockService.unlock(LockType.TRIAL_LIBRARY, library.getId());
-                    lockService.unwatch(LockType.GROUP, library.getExamId(), library.getSubjectCode(),
-                            library.getGroupNumber());
+                    lockService.unwatch(LockType.GROUP, library.getExamId(), library.getSubjectCode(), library.getGroupNumber());
                 }
             } else {
                 obj.accumulate("success", false);
@@ -158,14 +153,14 @@ public class TrialController extends BaseExamController {
         TrialLibrary library = trialService.findLibrary(libraryId);
         if (library != null) {
             ExamStudent student = studentService.findById(library.getStudentId());
-            MarkGroup group = groupService.findOne(library.getExamId(), library.getSubjectCode(),
-                    library.getGroupNumber());
+            MarkGroup group = groupService.findOne(library.getExamId(), library.getSubjectCode(), library.getGroupNumber());
             if (group != null && student != null) {
                 obj.accumulate("success", true);
                 // 裁切图配置
                 obj.accumulate("imageServer", sliceServer);
-                obj.accumulate("urls", PictureUrlBuilder.getSliceUrls(library.getExamId(), library.getCampusId(),
-                        library.getSubjectCode(), library.getExamNumber(), student.getSliceCount()));
+                obj.accumulate("urls", PictureUrlBuilder
+                        .getSliceUrls(library.getExamId(), library.getCampusId(), library.getSubjectCode(), library.getExamNumber(),
+                                student.getSliceCount()));
                 obj.accumulate("pictureConfig", group.getPictureConfigList());
                 // 评卷记录集合
                 JSONArray array = new JSONArray();

+ 43 - 44
stmms-web/src/main/java/cn/com/qmth/stmms/admin/interceptor/AdminExamInterceptor.java

@@ -1,44 +1,43 @@
-package cn.com.qmth.stmms.admin.interceptor;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.springframework.beans.factory.annotation.Autowired;
-
-import cn.com.qmth.stmms.admin.utils.SessionExamUtils;
-import cn.com.qmth.stmms.biz.exam.model.Exam;
-import cn.com.qmth.stmms.biz.exam.service.ExamService;
-import cn.com.qmth.stmms.biz.user.model.User;
-import cn.com.qmth.stmms.common.interceptor.CommonInterceptor;
-import cn.com.qmth.stmms.common.utils.RequestUtils;
-
-/**
- * Admin Exam模块操作拦截器
- * 
- * @author LS
- * 
- */
-public class AdminExamInterceptor extends CommonInterceptor {
-
-    @Autowired
-    private ExamService examService;
-
-    @Override
-    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
-            throws Exception {
-        int examId = SessionExamUtils.getExamId(request);
-        if (examId > 0) {
-            User user = RequestUtils.getWebUser(request).getUser();
-            Exam exam = examService.findById(examId);
-            if (exam == null || !exam.getSchoolId().equals(user.getSchoolId())) {
-                response.sendRedirect(request.getContextPath() + "/admin/home");
-                return false;
-            }
-            return true;
-        } else {
-            response.sendRedirect(request.getContextPath() + "/admin/exam-list");
-            return false;
-        }
-    }
-
-}
+package cn.com.qmth.stmms.admin.interceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import cn.com.qmth.stmms.common.domain.WebUser;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import cn.com.qmth.stmms.admin.utils.SessionExamUtils;
+import cn.com.qmth.stmms.biz.exam.model.Exam;
+import cn.com.qmth.stmms.biz.exam.service.ExamService;
+import cn.com.qmth.stmms.biz.user.model.User;
+import cn.com.qmth.stmms.common.interceptor.SessionInterceptor;
+import cn.com.qmth.stmms.common.utils.RequestUtils;
+
+/**
+ * Admin Exam模块操作拦截器
+ *
+ * @author LS
+ */
+public class AdminExamInterceptor extends SessionInterceptor {
+
+    @Autowired
+    private ExamService examService;
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        int examId = SessionExamUtils.getExamId(request);
+        if (examId > 0) {
+            WebUser wu = RequestUtils.getWebUser(request);
+            Exam exam = examService.findById(examId);
+            if (exam == null || wu == null || !exam.getSchoolId().equals(wu.getUser().getSchoolId())) {
+                response.sendRedirect(request.getContextPath() + "/admin/home");
+                return false;
+            }
+            return true;
+        } else {
+            response.sendRedirect(request.getContextPath() + "/admin/exam-list");
+            return false;
+        }
+    }
+
+}

+ 57 - 60
stmms-web/src/main/java/cn/com/qmth/stmms/admin/interceptor/AdminInterceptor.java

@@ -1,60 +1,57 @@
-package cn.com.qmth.stmms.admin.interceptor;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.springframework.beans.factory.annotation.Autowired;
-
-import cn.com.qmth.stmms.biz.user.model.User;
-import cn.com.qmth.stmms.biz.user.service.UserService;
-import cn.com.qmth.stmms.common.domain.WebUser;
-import cn.com.qmth.stmms.common.enums.Role;
-import cn.com.qmth.stmms.common.interceptor.CommonInterceptor;
-import cn.com.qmth.stmms.common.utils.RequestUtils;
-
-/**
- * Admin模块操作拦截器
- * 
- * @author LS
- * 
- */
-public class AdminInterceptor extends CommonInterceptor {
-
-    @Autowired
-    private UserService userService;
-
-    @Override
-    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
-        return validateAdminUser(request, response);
-    }
-
-    private boolean validateAdminUser(HttpServletRequest request, HttpServletResponse response) throws Exception {
-        User user = buildUser(RequestUtils.getWebUser(request));
-        if (user != null
-                && (user.getRole() == Role.SYS_ADMIN || user.getRole() == Role.SCHOOL_ADMIN
-                        || user.getRole() == Role.SUBJECT_HEADER || user.getRole() == Role.SCHOOL_VIEWER)) {
-            return true;
-        }
-        return sessionExpire(request, response, "/login");
-    }
-
-    protected User buildUser(WebUser wu) {
-        User user = null;
-        if (wu != null) {
-            User local = userService.findById(wu.getId());
-            if (!wu.getWebToken().equals(local.getWebToken())) {
-                return null;
-            }
-            user = wu.getUser();
-            if (user == null) {
-                user = local;
-                if (user != null) {
-                    wu.setDataObject(user);
-                    wu.setName(user.getName());
-                    wu.setRole(user.getRole());
-                }
-            }
-        }
-        return user;
-    }
-}
+package cn.com.qmth.stmms.admin.interceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import cn.com.qmth.stmms.common.session.model.StmmsSession;
+import cn.com.qmth.stmms.common.utils.AccessControlUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import cn.com.qmth.stmms.biz.user.model.User;
+import cn.com.qmth.stmms.biz.user.service.UserService;
+import cn.com.qmth.stmms.common.domain.WebUser;
+import cn.com.qmth.stmms.common.enums.Role;
+import cn.com.qmth.stmms.common.interceptor.SessionInterceptor;
+import cn.com.qmth.stmms.common.utils.RequestUtils;
+
+/**
+ * Admin模块操作拦截器
+ *
+ * @author LS
+ */
+public class AdminInterceptor extends SessionInterceptor {
+
+    @Autowired
+    private UserService userService;
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        return validateAdminUser(buildWebUser(request), request, response);
+    }
+
+    private boolean validateAdminUser(WebUser wu, HttpServletRequest request, HttpServletResponse response) throws Exception {
+        if (wu != null && (wu.getRole() == Role.SYS_ADMIN || wu.getRole() == Role.SCHOOL_ADMIN || wu.getRole() == Role.SUBJECT_HEADER
+                || wu.getRole() == Role.SCHOOL_VIEWER)) {
+            return true;
+        }
+        return sessionExpire(request, response, "/login");
+    }
+
+    private WebUser buildWebUser(HttpServletRequest request) {
+        StmmsSession session = RequestUtils.getSession(request);
+        Integer userId = session.getWebUserId();
+        Role role = session.getWebUserRole();
+        if (userId != null && role != null) {
+            User user = userService.findById(userId);
+            if (user != null && user.getRole() == role && user.getAccessTokenRefreshTime() != null && StringUtils
+                    .equals(session.getWebUserToken(), user.getAccessToken()) && !AccessControlUtils
+                    .expired(user.getAccessTokenRefreshTime())) {
+                WebUser wu = new WebUser(user);
+                RequestUtils.setWebUser(request, wu);
+                return wu;
+            }
+        }
+        return null;
+    }
+}

+ 31 - 33
stmms-web/src/main/java/cn/com/qmth/stmms/admin/interceptor/SysAdminInterceptor.java

@@ -1,33 +1,31 @@
-package cn.com.qmth.stmms.admin.interceptor;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import cn.com.qmth.stmms.common.domain.WebUser;
-import cn.com.qmth.stmms.common.enums.Role;
-import cn.com.qmth.stmms.common.interceptor.CommonInterceptor;
-import cn.com.qmth.stmms.common.utils.RequestUtils;
-
-/**
- * Admin模块操作拦截器
- * 
- * @author LS
- * 
- */
-public class SysAdminInterceptor extends CommonInterceptor {
-
-    @Override
-    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
-            throws Exception {
-        return validateSysAdminUser(request, response);
-    }
-
-    private boolean validateSysAdminUser(HttpServletRequest request, HttpServletResponse response) throws Exception {
-        WebUser wu = RequestUtils.getWebUser(request);
-        if (wu != null && wu.getRole() == Role.SYS_ADMIN) {
-            return true;
-        }
-        return sessionExpire(request, response, "/login");
-    }
-
-}
+package cn.com.qmth.stmms.admin.interceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import cn.com.qmth.stmms.common.domain.WebUser;
+import cn.com.qmth.stmms.common.enums.Role;
+import cn.com.qmth.stmms.common.interceptor.SessionInterceptor;
+import cn.com.qmth.stmms.common.utils.RequestUtils;
+
+/**
+ * Admin模块操作拦截器
+ *
+ * @author LS
+ */
+public class SysAdminInterceptor extends SessionInterceptor {
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        return validateSysAdminUser(request, response);
+    }
+
+    private boolean validateSysAdminUser(HttpServletRequest request, HttpServletResponse response) throws Exception {
+        WebUser wu = RequestUtils.getWebUser(request);
+        if (wu != null && wu.getRole() == Role.SYS_ADMIN) {
+            return true;
+        }
+        return sessionExpire(request, response, "/login");
+    }
+
+}

+ 190 - 188
stmms-web/src/main/java/cn/com/qmth/stmms/admin/school/SchoolController.java

@@ -1,188 +1,190 @@
-package cn.com.qmth.stmms.admin.school;
-
-import javax.servlet.http.HttpServletRequest;
-
-import org.apache.commons.lang.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.servlet.ModelAndView;
-
-import cn.com.qmth.stmms.biz.school.model.School;
-import cn.com.qmth.stmms.biz.school.query.SchoolSearchQuery;
-import cn.com.qmth.stmms.biz.school.service.SchoolService;
-import cn.com.qmth.stmms.biz.user.model.User;
-import cn.com.qmth.stmms.biz.user.service.UserService;
-import cn.com.qmth.stmms.common.controller.BaseController;
-import cn.com.qmth.stmms.common.enums.Role;
-import cn.com.qmth.stmms.common.utils.Md5EncryptUtils;
-
-@Controller
-@RequestMapping("/admin/sys/school")
-public class SchoolController extends BaseController {
-
-    @Autowired
-    private SchoolService schoolService;
-
-    @Autowired
-    private UserService userService;
-
-    @RequestMapping
-    public ModelAndView list(HttpServletRequest request, SchoolSearchQuery query) {
-        ModelAndView view = new ModelAndView("modules/sys/schoolList");
-        query = schoolService.findByQuery(query);
-        view.addObject("query", query);
-        return view;
-    }
-
-    @RequestMapping(value = "/add", method = RequestMethod.GET)
-    public ModelAndView addInit(HttpServletRequest request) {
-        ModelAndView view = new ModelAndView("modules/sys/schoolEdit");
-        view.addObject("school", new School());
-        return view;
-    }
-
-    @RequestMapping(value = "/edit", method = RequestMethod.GET)
-    public ModelAndView editInit(HttpServletRequest request, @RequestParam Integer id) {
-        School school = schoolService.findById(id);
-        if (school != null) {
-            ModelAndView view = new ModelAndView("modules/sys/schoolEdit");
-            view.addObject("school", school);
-            return view;
-        } else {
-            return new ModelAndView("redirect:/admin/sys/school");
-        }
-    }
-
-    @RequestMapping(value = "/save", method = RequestMethod.POST)
-    public String save(HttpServletRequest request, School school) {
-        School previous = null;
-        if (school.getId() != null) {
-            previous = schoolService.findById(school.getId());
-        }
-        if (previous == null) {
-            school.setEnable(true);
-            school = schoolService.save(school);
-            return "redirect:/admin/sys/school/admin?id=" + school.getId();
-        } else {
-            previous.setName(school.getName());
-            previous.setProvince(school.getProvince());
-            previous.setCity(school.getCity());
-            previous.setAddress(school.getAddress());
-            previous.setDescription(school.getDescription());
-            schoolService.save(previous);
-        }
-        return "redirect:/admin/sys/school";
-    }
-
-    @RequestMapping(value = "/admin", method = RequestMethod.GET)
-    public ModelAndView adminInit(HttpServletRequest request, @RequestParam Integer id,
-            @RequestParam(required = false) String message) {
-        School school = schoolService.findById(id);
-        if (school != null) {
-            User user = userService.findSchoolAdmin(id);
-            if (user == null) {
-                user = new User();
-                user.setSchoolId(id);
-            }
-            ModelAndView view = new ModelAndView("modules/sys/schoolAdminEdit");
-            view.addObject("school", school);
-            view.addObject("user", user);
-            view.addObject("message", message);
-            return view;
-        } else {
-            return new ModelAndView("redirect:/admin/sys/school");
-        }
-    }
-
-    @RequestMapping(value = "/admin/save", method = RequestMethod.POST)
-    public ModelAndView adminSave(HttpServletRequest request, User user) {
-        School school = schoolService.findById(user.getSchoolId());
-        if (school == null) {
-            return new ModelAndView("redirect:/admin/sys/school");
-        }
-        User previous = userService.findSchoolAdmin(user.getSchoolId());
-        String message = "";
-        if (previous != null) {
-            previous.setLoginName(user.getLoginName());
-            previous.setName(user.getName());
-            if (StringUtils.isNotBlank(user.getPassword())) {
-                previous.setPassword(Md5EncryptUtils.md5(user.getPassword()));
-            }
-            if (checkLoginName(previous)) {
-                userService.save(previous);
-                return new ModelAndView("redirect:/admin/sys/school");
-            } else {
-                message = "登录名重复";
-            }
-        } else if (StringUtils.isBlank(user.getPassword())) {
-            message = "密码不能为空";
-        } else if (checkLoginName(user)) {
-            user.setEnable(true);
-            user.setRole(Role.SCHOOL_ADMIN);
-            user.setPassword(Md5EncryptUtils.md5(user.getPassword()));
-            userService.save(user);
-            return new ModelAndView("redirect:/admin/sys/school");
-        } else {
-            message = "登录名重复";
-        }
-        return adminInit(request, user.getSchoolId(), message);
-    }
-    
-    @RequestMapping(value = "/viewer", method = RequestMethod.GET)
-    public ModelAndView viewerInit(HttpServletRequest request, @RequestParam Integer id,
-            @RequestParam(required = false) String message) {
-        School school = schoolService.findById(id);
-        if (school != null) {
-            User user = userService.findSchoolViewer(id);
-            if (user == null) {
-                user = new User();
-                user.setSchoolId(id);
-            }
-            ModelAndView view = new ModelAndView("modules/sys/schoolViewerEdit");
-            view.addObject("school", school);
-            view.addObject("user", user);
-            view.addObject("message", message);
-            return view;
-        } else {
-            return new ModelAndView("redirect:/admin/sys/school");
-        }
-    }
-
-    @RequestMapping(value = "/viewer/save", method = RequestMethod.POST)
-    public ModelAndView viewerSave(HttpServletRequest request, User user) {
-        School school = schoolService.findById(user.getSchoolId());
-        if (school == null) {
-            return new ModelAndView("redirect:/admin/sys/school");
-        }
-        User previous = userService.findSchoolViewer(user.getSchoolId());
-        String message = "";
-        if (previous != null) {
-            previous.setLoginName(user.getLoginName());
-            previous.setName(user.getName());
-            if (StringUtils.isNotBlank(user.getPassword())) {
-                previous.setPassword(Md5EncryptUtils.md5(user.getPassword()));
-            }
-            if (checkLoginName(previous)) {
-                userService.save(previous);
-                return new ModelAndView("redirect:/admin/sys/school");
-            } else {
-                message = "登录名重复";
-            }
-        } else if (StringUtils.isBlank(user.getPassword())) {
-            message = "密码不能为空";
-        } else if (checkLoginName(user)) {
-        	user.setEnable(true);
-            user.setRole(Role.SCHOOL_VIEWER);
-            user.setPassword(Md5EncryptUtils.md5(user.getPassword()));
-            userService.save(user);
-            return new ModelAndView("redirect:/admin/sys/school");
-        } else {
-            message = "登录名重复";
-        }
-        return adminInit(request, user.getSchoolId(), message);
-    }
-
-}
+package cn.com.qmth.stmms.admin.school;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.servlet.ModelAndView;
+
+import cn.com.qmth.stmms.biz.school.model.School;
+import cn.com.qmth.stmms.biz.school.query.SchoolSearchQuery;
+import cn.com.qmth.stmms.biz.school.service.SchoolService;
+import cn.com.qmth.stmms.biz.user.model.User;
+import cn.com.qmth.stmms.biz.user.service.UserService;
+import cn.com.qmth.stmms.common.controller.BaseController;
+import cn.com.qmth.stmms.common.enums.Role;
+import cn.com.qmth.stmms.common.utils.Md5EncryptUtils;
+
+@Controller
+@RequestMapping("/admin/sys/school")
+public class SchoolController extends BaseController {
+
+    @Autowired
+    private SchoolService schoolService;
+
+    @Autowired
+    private UserService userService;
+
+    @RequestMapping
+    public ModelAndView list(HttpServletRequest request, SchoolSearchQuery query) {
+        ModelAndView view = new ModelAndView("modules/sys/schoolList");
+        query = schoolService.findByQuery(query);
+        view.addObject("query", query);
+        return view;
+    }
+
+    @RequestMapping(value = "/add", method = RequestMethod.GET)
+    public ModelAndView addInit(HttpServletRequest request) {
+        ModelAndView view = new ModelAndView("modules/sys/schoolEdit");
+        view.addObject("school", new School());
+        return view;
+    }
+
+    @RequestMapping(value = "/edit", method = RequestMethod.GET)
+    public ModelAndView editInit(HttpServletRequest request, @RequestParam Integer id) {
+        School school = schoolService.findById(id);
+        if (school != null) {
+            ModelAndView view = new ModelAndView("modules/sys/schoolEdit");
+            view.addObject("school", school);
+            return view;
+        } else {
+            return new ModelAndView("redirect:/admin/sys/school");
+        }
+    }
+
+    @RequestMapping(value = "/save", method = RequestMethod.POST)
+    public String save(HttpServletRequest request, School school) {
+        School previous = null;
+        if (school.getId() != null) {
+            previous = schoolService.findById(school.getId());
+        }
+        if (previous == null) {
+            school.setEnable(true);
+            school.resetAccessKeyAndSecret();
+            school = schoolService.save(school);
+            return "redirect:/admin/sys/school/admin?id=" + school.getId();
+        } else {
+            previous.setName(school.getName());
+            previous.setProvince(school.getProvince());
+            previous.setCity(school.getCity());
+            previous.setAddress(school.getAddress());
+            previous.setDescription(school.getDescription());
+            if (StringUtils.isBlank(previous.getAccessKey()) || StringUtils.isBlank(previous.getAccessSecret())) {
+                previous.resetAccessKeyAndSecret();
+            }
+            schoolService.save(previous);
+        }
+        return "redirect:/admin/sys/school";
+    }
+
+    @RequestMapping(value = "/admin", method = RequestMethod.GET)
+    public ModelAndView adminInit(HttpServletRequest request, @RequestParam Integer id, @RequestParam(required = false) String message) {
+        School school = schoolService.findById(id);
+        if (school != null) {
+            User user = userService.findSchoolAdmin(id);
+            if (user == null) {
+                user = new User();
+                user.setSchoolId(id);
+            }
+            ModelAndView view = new ModelAndView("modules/sys/schoolAdminEdit");
+            view.addObject("school", school);
+            view.addObject("user", user);
+            view.addObject("message", message);
+            return view;
+        } else {
+            return new ModelAndView("redirect:/admin/sys/school");
+        }
+    }
+
+    @RequestMapping(value = "/admin/save", method = RequestMethod.POST)
+    public ModelAndView adminSave(HttpServletRequest request, User user) {
+        School school = schoolService.findById(user.getSchoolId());
+        if (school == null) {
+            return new ModelAndView("redirect:/admin/sys/school");
+        }
+        User previous = userService.findSchoolAdmin(user.getSchoolId());
+        String message = "";
+        if (previous != null) {
+            previous.setLoginName(user.getLoginName());
+            previous.setName(user.getName());
+            if (StringUtils.isNotBlank(user.getPassword())) {
+                previous.setPassword(Md5EncryptUtils.md5(user.getPassword()));
+            }
+            if (checkLoginName(previous)) {
+                userService.save(previous);
+                return new ModelAndView("redirect:/admin/sys/school");
+            } else {
+                message = "登录名重复";
+            }
+        } else if (StringUtils.isBlank(user.getPassword())) {
+            message = "密码不能为空";
+        } else if (checkLoginName(user)) {
+            user.setEnable(true);
+            user.setRole(Role.SCHOOL_ADMIN);
+            user.setPassword(Md5EncryptUtils.md5(user.getPassword()));
+            userService.save(user);
+            return new ModelAndView("redirect:/admin/sys/school");
+        } else {
+            message = "登录名重复";
+        }
+        return adminInit(request, user.getSchoolId(), message);
+    }
+
+    @RequestMapping(value = "/viewer", method = RequestMethod.GET)
+    public ModelAndView viewerInit(HttpServletRequest request, @RequestParam Integer id, @RequestParam(required = false) String message) {
+        School school = schoolService.findById(id);
+        if (school != null) {
+            User user = userService.findSchoolViewer(id);
+            if (user == null) {
+                user = new User();
+                user.setSchoolId(id);
+            }
+            ModelAndView view = new ModelAndView("modules/sys/schoolViewerEdit");
+            view.addObject("school", school);
+            view.addObject("user", user);
+            view.addObject("message", message);
+            return view;
+        } else {
+            return new ModelAndView("redirect:/admin/sys/school");
+        }
+    }
+
+    @RequestMapping(value = "/viewer/save", method = RequestMethod.POST)
+    public ModelAndView viewerSave(HttpServletRequest request, User user) {
+        School school = schoolService.findById(user.getSchoolId());
+        if (school == null) {
+            return new ModelAndView("redirect:/admin/sys/school");
+        }
+        User previous = userService.findSchoolViewer(user.getSchoolId());
+        String message = "";
+        if (previous != null) {
+            previous.setLoginName(user.getLoginName());
+            previous.setName(user.getName());
+            if (StringUtils.isNotBlank(user.getPassword())) {
+                previous.setPassword(Md5EncryptUtils.md5(user.getPassword()));
+            }
+            if (checkLoginName(previous)) {
+                userService.save(previous);
+                return new ModelAndView("redirect:/admin/sys/school");
+            } else {
+                message = "登录名重复";
+            }
+        } else if (StringUtils.isBlank(user.getPassword())) {
+            message = "密码不能为空";
+        } else if (checkLoginName(user)) {
+            user.setEnable(true);
+            user.setRole(Role.SCHOOL_VIEWER);
+            user.setPassword(Md5EncryptUtils.md5(user.getPassword()));
+            userService.save(user);
+            return new ModelAndView("redirect:/admin/sys/school");
+        } else {
+            message = "登录名重复";
+        }
+        return adminInit(request, user.getSchoolId(), message);
+    }
+
+}

+ 156 - 156
stmms-web/src/main/java/cn/com/qmth/stmms/admin/user/UserController.java

@@ -1,156 +1,156 @@
-package cn.com.qmth.stmms.admin.user;
-
-import java.util.Date;
-
-import javax.servlet.http.HttpServletRequest;
-
-import org.apache.commons.lang.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Controller;
-import org.springframework.ui.Model;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.servlet.ModelAndView;
-import org.springframework.web.servlet.mvc.support.RedirectAttributes;
-
-import cn.com.qmth.stmms.biz.user.model.User;
-import cn.com.qmth.stmms.biz.user.service.UserService;
-import cn.com.qmth.stmms.biz.user.service.query.UserSearchQuery;
-import cn.com.qmth.stmms.common.auth.annotation.RoleRequire;
-import cn.com.qmth.stmms.common.controller.BaseController;
-import cn.com.qmth.stmms.common.enums.Role;
-import cn.com.qmth.stmms.common.utils.Md5EncryptUtils;
-import cn.com.qmth.stmms.common.utils.RequestUtils;
-
-@Controller
-@RequestMapping("/admin/user")
-public class UserController extends BaseController {
-
-    public static Role[] roleList = { Role.SCANNER, Role.SUBJECT_HEADER };
-
-    @Autowired
-    private UserService userService;
-
-    @RequestMapping("/list")
-    @RoleRequire(Role.SCHOOL_ADMIN)
-    public ModelAndView list(HttpServletRequest request, UserSearchQuery query) {
-        User user = RequestUtils.getWebUser(request).getUser();
-        query.setSchoolId(user.getSchoolId());
-        query = userService.findByQuery(query);
-
-        ModelAndView view = new ModelAndView("modules/user/userList");
-        view.addObject("roleList", roleList);
-        view.addObject("query", query);
-        return view;
-    }
-
-    @RequestMapping("/delete")
-    @RoleRequire(Role.SCHOOL_ADMIN)
-    public String delete(HttpServletRequest request, RedirectAttributes redirectAttributes, @RequestParam Integer id) {
-        User current = RequestUtils.getWebUser(request).getUser();
-        User user = userService.findById(id);
-        String message = null;
-        if (user == null) {
-            message = "找不到指定用户";
-        } else if (!user.getSchoolId().equals(current.getSchoolId()) || user.getId().equals(current.getId())) {
-            message = "不能删除指定用户";
-        } else {
-            userService.delete(user);
-            message = "删除成功";
-        }
-        redirectAttributes.addFlashAttribute("message", message);
-        return "redirect:/admin/user/list";
-    }
-
-    @RequestMapping(value = "/add", method = RequestMethod.GET)
-    @RoleRequire(Role.SCHOOL_ADMIN)
-    public ModelAndView addInit(HttpServletRequest request) {
-        ModelAndView view = new ModelAndView("modules/user/userEdit");
-        User user = new User();
-        user.setEnable(true);
-        view.addObject("user", user);
-        view.addObject("roleList", roleList);
-        return view;
-    }
-
-    @RequestMapping(value = "/edit", method = RequestMethod.GET)
-    @RoleRequire(Role.SCHOOL_ADMIN)
-    public ModelAndView editInit(HttpServletRequest request, @RequestParam Integer id) {
-        User current = RequestUtils.getWebUser(request).getUser();
-        User user = userService.findById(id);
-        if (user != null && user.getSchoolId().equals(current.getSchoolId())) {
-            ModelAndView view = new ModelAndView("modules/user/userEdit");
-            view.addObject("user", user);
-            view.addObject("roleList", roleList);
-            return view;
-        } else {
-            return new ModelAndView("redirect:/admin/user/list");
-        }
-    }
-
-    @RequestMapping(value = "/save", method = RequestMethod.POST)
-    @RoleRequire(Role.SCHOOL_ADMIN)
-    public String save(HttpServletRequest request, Model model, User user) {
-        User current = RequestUtils.getWebUser(request).getUser();
-        User previous = null;
-        if (user.getId() != null) {
-            previous = userService.findById(user.getId());
-        }
-        if (previous == null) {
-            String message = validate(user);
-            if (message == null) {
-                user.setPassword(Md5EncryptUtils.md5(user.getPassword()));
-                user.setSchoolId(current.getSchoolId());
-                user.setEnable(true);
-                user.setCreatedTime(new Date());
-                userService.save(user);
-            } else {
-                model.addAttribute("user", user);
-                model.addAttribute("roleList", roleList);
-                model.addAttribute("message", message);
-                return "modules/user/userEdit";
-            }
-        } else if (previous.getSchoolId().equals(current.getSchoolId())) {
-            previous.setLoginName(user.getLoginName());
-            previous.setName(user.getName());
-            previous.setRole(user.getRole());
-            if (StringUtils.isNotBlank(user.getPassword())) {
-                previous.setPassword(Md5EncryptUtils.md5(user.getPassword()));
-            }
-            if (previous.getRole() != Role.SUBJECT_HEADER) {
-                previous.setSubjectCode(null);
-            } else {
-                previous.setSubjectCode(user.getSubjectCode());
-            }
-            previous.setEnable(user.isEnable());
-            String message = validate(previous);
-            if (message == null) {
-                previous.setUpdatedTime(new Date());
-                userService.save(previous);
-            } else {
-                model.addAttribute("user", previous);
-                model.addAttribute("roleList", roleList);
-                model.addAttribute("message", message);
-                return "modules/user/userEdit";
-            }
-        }
-        return "redirect:/admin/user/list";
-    }
-
-    private String validate(User user) {
-        String message = null;
-        if (StringUtils.isBlank(user.getLoginName())) {
-            message = "登录名不能为空";
-        } else if (!checkLoginName(user)) {
-            message = "登录名不能重复";
-        } else if (StringUtils.isBlank(user.getName())) {
-            message = "名称不能为空";
-        } else if (StringUtils.isBlank(user.getPassword())) {
-            message = "密码不能为空";
-        } else if (user.getRole() == Role.SUBJECT_HEADER && StringUtils.isBlank(user.getSubjectCode())) {
-            message = "科组长必须绑定科目代码";
-        }
-        return message;
-    }
-}
+package cn.com.qmth.stmms.admin.user;
+
+import java.util.Date;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.lang.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
+import cn.com.qmth.stmms.biz.user.model.User;
+import cn.com.qmth.stmms.biz.user.service.UserService;
+import cn.com.qmth.stmms.biz.user.service.query.UserSearchQuery;
+import cn.com.qmth.stmms.common.annotation.RoleRequire;
+import cn.com.qmth.stmms.common.controller.BaseController;
+import cn.com.qmth.stmms.common.enums.Role;
+import cn.com.qmth.stmms.common.utils.Md5EncryptUtils;
+import cn.com.qmth.stmms.common.utils.RequestUtils;
+
+@Controller
+@RequestMapping("/admin/user")
+public class UserController extends BaseController {
+
+    public static Role[] roleList = { Role.SCANNER, Role.SUBJECT_HEADER };
+
+    @Autowired
+    private UserService userService;
+
+    @RequestMapping("/list")
+    @RoleRequire(Role.SCHOOL_ADMIN)
+    public ModelAndView list(HttpServletRequest request, UserSearchQuery query) {
+        User user = RequestUtils.getWebUser(request).getUser();
+        query.setSchoolId(user.getSchoolId());
+        query = userService.findByQuery(query);
+
+        ModelAndView view = new ModelAndView("modules/user/userList");
+        view.addObject("roleList", roleList);
+        view.addObject("query", query);
+        return view;
+    }
+
+    @RequestMapping("/delete")
+    @RoleRequire(Role.SCHOOL_ADMIN)
+    public String delete(HttpServletRequest request, RedirectAttributes redirectAttributes, @RequestParam Integer id) {
+        User current = RequestUtils.getWebUser(request).getUser();
+        User user = userService.findById(id);
+        String message = null;
+        if (user == null) {
+            message = "找不到指定用户";
+        } else if (!user.getSchoolId().equals(current.getSchoolId()) || user.getId().equals(current.getId())) {
+            message = "不能删除指定用户";
+        } else {
+            userService.delete(user);
+            message = "删除成功";
+        }
+        redirectAttributes.addFlashAttribute("message", message);
+        return "redirect:/admin/user/list";
+    }
+
+    @RequestMapping(value = "/add", method = RequestMethod.GET)
+    @RoleRequire(Role.SCHOOL_ADMIN)
+    public ModelAndView addInit(HttpServletRequest request) {
+        ModelAndView view = new ModelAndView("modules/user/userEdit");
+        User user = new User();
+        user.setEnable(true);
+        view.addObject("user", user);
+        view.addObject("roleList", roleList);
+        return view;
+    }
+
+    @RequestMapping(value = "/edit", method = RequestMethod.GET)
+    @RoleRequire(Role.SCHOOL_ADMIN)
+    public ModelAndView editInit(HttpServletRequest request, @RequestParam Integer id) {
+        User current = RequestUtils.getWebUser(request).getUser();
+        User user = userService.findById(id);
+        if (user != null && user.getSchoolId().equals(current.getSchoolId())) {
+            ModelAndView view = new ModelAndView("modules/user/userEdit");
+            view.addObject("user", user);
+            view.addObject("roleList", roleList);
+            return view;
+        } else {
+            return new ModelAndView("redirect:/admin/user/list");
+        }
+    }
+
+    @RequestMapping(value = "/save", method = RequestMethod.POST)
+    @RoleRequire(Role.SCHOOL_ADMIN)
+    public String save(HttpServletRequest request, Model model, User user) {
+        User current = RequestUtils.getWebUser(request).getUser();
+        User previous = null;
+        if (user.getId() != null) {
+            previous = userService.findById(user.getId());
+        }
+        if (previous == null) {
+            String message = validate(user);
+            if (message == null) {
+                user.setPassword(Md5EncryptUtils.md5(user.getPassword()));
+                user.setSchoolId(current.getSchoolId());
+                user.setEnable(true);
+                user.setCreatedTime(new Date());
+                userService.save(user);
+            } else {
+                model.addAttribute("user", user);
+                model.addAttribute("roleList", roleList);
+                model.addAttribute("message", message);
+                return "modules/user/userEdit";
+            }
+        } else if (previous.getSchoolId().equals(current.getSchoolId())) {
+            previous.setLoginName(user.getLoginName());
+            previous.setName(user.getName());
+            previous.setRole(user.getRole());
+            if (StringUtils.isNotBlank(user.getPassword())) {
+                previous.setPassword(Md5EncryptUtils.md5(user.getPassword()));
+            }
+            if (previous.getRole() != Role.SUBJECT_HEADER) {
+                previous.setSubjectCode(null);
+            } else {
+                previous.setSubjectCode(user.getSubjectCode());
+            }
+            previous.setEnable(user.isEnable());
+            String message = validate(previous);
+            if (message == null) {
+                previous.setUpdatedTime(new Date());
+                userService.save(previous);
+            } else {
+                model.addAttribute("user", previous);
+                model.addAttribute("roleList", roleList);
+                model.addAttribute("message", message);
+                return "modules/user/userEdit";
+            }
+        }
+        return "redirect:/admin/user/list";
+    }
+
+    private String validate(User user) {
+        String message = null;
+        if (StringUtils.isBlank(user.getLoginName())) {
+            message = "登录名不能为空";
+        } else if (!checkLoginName(user)) {
+            message = "登录名不能重复";
+        } else if (StringUtils.isBlank(user.getName())) {
+            message = "名称不能为空";
+        } else if (StringUtils.isBlank(user.getPassword())) {
+            message = "密码不能为空";
+        } else if (user.getRole() == Role.SUBJECT_HEADER && StringUtils.isBlank(user.getSubjectCode())) {
+            message = "科组长必须绑定科目代码";
+        }
+        return message;
+    }
+}

+ 4 - 3
stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/BaseApiController.java

@@ -2,24 +2,25 @@ package cn.com.qmth.stmms.api.controller;
 
 import javax.servlet.http.HttpServletResponse;
 
+import cn.com.qmth.stmms.api.exception.ApiException;
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.http.HttpStatus;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 
-import cn.com.qmth.stmms.api.utils.AuthInfoUtils;
-import cn.com.qmth.stmms.biz.api.auth.exception.ApiException;
 import cn.com.qmth.stmms.common.controller.BaseController;
 
 public class BaseApiController extends BaseController {
 
     protected static final Logger log = LoggerFactory.getLogger(BaseApiController.class);
 
+    private static final String ERROR_MESSAGE_HEADER_KEY = "error-info";
+
     @ExceptionHandler
     public void exception(HttpServletResponse response, Exception ex) {
         log.error("api execute error", ex);
-        response.addHeader(AuthInfoUtils.ERROR_MESSAGE_HEADER_KEY, StringUtils.trimToEmpty(ex.getMessage()));
+        response.addHeader(ERROR_MESSAGE_HEADER_KEY, StringUtils.trimToEmpty(ex.getMessage()));
         try {
             if (ex instanceof ApiException) {
                 ApiException e = (ApiException) ex;

+ 32 - 36
stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/ExamInfoController.java

@@ -5,6 +5,10 @@ import java.util.List;
 
 import javax.servlet.http.HttpServletRequest;
 
+import cn.com.qmth.stmms.api.exception.ApiException;
+import cn.com.qmth.stmms.common.annotation.RoleRequire;
+import cn.com.qmth.stmms.common.domain.ApiUser;
+import cn.com.qmth.stmms.common.enums.Role;
 import net.sf.json.JSONArray;
 import net.sf.json.JSONObject;
 
@@ -17,8 +21,6 @@ import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.annotation.ResponseBody;
 
 import cn.com.qmth.stmms.admin.vo.ExamSubjectVO;
-import cn.com.qmth.stmms.biz.api.auth.annotation.AuthValidate;
-import cn.com.qmth.stmms.biz.api.auth.exception.ApiException;
 import cn.com.qmth.stmms.biz.campus.model.Campus;
 import cn.com.qmth.stmms.biz.campus.service.CampusService;
 import cn.com.qmth.stmms.biz.exam.model.Exam;
@@ -27,7 +29,6 @@ import cn.com.qmth.stmms.biz.exam.query.ExamSearchQuery;
 import cn.com.qmth.stmms.biz.exam.service.ExamService;
 import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
 import cn.com.qmth.stmms.biz.mark.model.PictureConfigItem;
-import cn.com.qmth.stmms.biz.user.model.User;
 import cn.com.qmth.stmms.common.enums.ExamStatus;
 import cn.com.qmth.stmms.common.utils.DateUtils;
 import cn.com.qmth.stmms.common.utils.RequestUtils;
@@ -51,36 +52,34 @@ public class ExamInfoController extends BaseApiController {
         return "ft";
     }
 
-    @AuthValidate({ "adminUser", "scanner" })
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SCANNER })
     @RequestMapping("/exams")
     @ResponseBody
     public JSONArray getExamInfos(HttpServletRequest request, ExamSearchQuery query) {
-        User user = RequestUtils.getApiUser(request);
+        ApiUser user = RequestUtils.getApiUser(request);
         JSONArray array = new JSONArray();
-        if (user.getSchoolId() != null) {
-            query.setSchoolId(user.getSchoolId());
-            query.addStatus(ExamStatus.START);
-            if (query.getPageSize() < 1) {
-                query.setPageSize(20);
-            }
-            query.orderByIdDesc();
-            query = examService.findByQuery(query);
-            for (Exam exam : query.getResult()) {
-                JSONObject obj = new JSONObject();
-                obj.accumulate("id", exam.getId());
-                obj.accumulate("name", exam.getName());
-                obj.accumulate("examTime", DateUtils.formatDate(exam.getExamTime()));
-                array.add(obj);
-            }
+        query.setSchoolId(user.getSchoolId());
+        query.addStatus(ExamStatus.START);
+        if (query.getPageSize() < 1) {
+            query.setPageSize(20);
+        }
+        query.orderByIdDesc();
+        query = examService.findByQuery(query);
+        for (Exam exam : query.getResult()) {
+            JSONObject obj = new JSONObject();
+            obj.accumulate("id", exam.getId());
+            obj.accumulate("name", exam.getName());
+            obj.accumulate("examTime", DateUtils.formatDate(exam.getExamTime()));
+            array.add(obj);
         }
         return array;
     }
 
-    @AuthValidate({ "adminUser", "scanner" })
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SCANNER })
     @RequestMapping(value = "/campus", method = RequestMethod.GET)
     @ResponseBody
     public JSONArray getCampus(HttpServletRequest request) {
-        User user = RequestUtils.getApiUser(request);
+        ApiUser user = RequestUtils.getApiUser(request);
         List<Campus> list = campusService.findBySchoolId(user.getSchoolId());
         JSONArray array = new JSONArray();
         for (Campus c : list) {
@@ -106,21 +105,21 @@ public class ExamInfoController extends BaseApiController {
         return array;
     }
 
-    @AuthValidate({ "adminUser", "scanner" })
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SCANNER })
     @RequestMapping(value = "/subject/update", method = RequestMethod.POST)
     @ResponseBody
     public JSONObject updateSubject(HttpServletRequest request, @RequestBody ExamSubjectVO subject) {
-        User user = RequestUtils.getApiUser(request);
+        ApiUser user = RequestUtils.getApiUser(request);
         Exam exam = examService.findById(subject.getExamId());
         JSONObject result = new JSONObject();
         if (exam != null && exam.getSchoolId().equals(user.getSchoolId())) {
             ExamSubject es = subjectService.find(subject.getExamId(), subject.getCode());
             if (es != null) {
                 if (subject.getHasAnswer() != null) {
-                    es.setHasAnswer(subject.getHasAnswer().booleanValue());
+                    es.setHasAnswer(subject.getHasAnswer());
                 }
                 if (subject.getHasPaper() != null) {
-                    es.setHasPaper(subject.getHasPaper().booleanValue());
+                    es.setHasPaper(subject.getHasPaper());
                 }
                 subjectService.save(es);
                 result.accumulate("code", subject.getCode());
@@ -134,12 +133,11 @@ public class ExamInfoController extends BaseApiController {
         return result;
     }
 
-    @AuthValidate({ "adminUser", "scanner" })
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SCANNER })
     @RequestMapping(value = "/sliceConfig/{examId}", method = RequestMethod.POST)
     @ResponseBody
-    public boolean updateSliceConfig(HttpServletRequest request, @PathVariable Integer examId,
-            @RequestBody PictureConfigItem[] configs) {
-        User user = RequestUtils.getApiUser(request);
+    public boolean updateSliceConfig(HttpServletRequest request, @PathVariable Integer examId, @RequestBody PictureConfigItem[] configs) {
+        ApiUser user = RequestUtils.getApiUser(request);
         Exam exam = examService.findById(examId);
         boolean success = false;
         if (exam != null && exam.getSchoolId().equals(user.getSchoolId())) {
@@ -155,12 +153,12 @@ public class ExamInfoController extends BaseApiController {
         return success;
     }
 
-    @AuthValidate({ "adminUser", "scanner" })
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SCANNER })
     @RequestMapping(value = "/sliceConfig/{examId}/{subjectCode}", method = RequestMethod.POST)
     @ResponseBody
-    public boolean updateSubjectSliceConfig(HttpServletRequest request, @PathVariable Integer examId,
-            @PathVariable String subjectCode, @RequestBody PictureConfigItem[] configs) {
-        User user = RequestUtils.getApiUser(request);
+    public boolean updateSubjectSliceConfig(HttpServletRequest request, @PathVariable Integer examId, @PathVariable String subjectCode,
+            @RequestBody PictureConfigItem[] configs) {
+        ApiUser user = RequestUtils.getApiUser(request);
         Exam exam = examService.findById(examId);
         boolean success = false;
         if (exam != null && exam.getSchoolId().equals(user.getSchoolId())) {
@@ -168,8 +166,6 @@ public class ExamInfoController extends BaseApiController {
             if (configs != null && configs.length > 0 && es != null) {
                 subjectService.updateSliceConfig(examId, subjectCode, Arrays.asList(configs));
                 success = true;
-            } else {
-                success = false;
             }
         } else {
             throw ApiException.EXAM_NOT_ACCESSIBLED;

+ 12 - 12
stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/ExamPackageController.java

@@ -5,6 +5,10 @@ import java.util.List;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import cn.com.qmth.stmms.api.exception.ApiException;
+import cn.com.qmth.stmms.common.annotation.RoleRequire;
+import cn.com.qmth.stmms.common.domain.ApiUser;
+import cn.com.qmth.stmms.common.enums.Role;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -16,14 +20,11 @@ import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.ResponseBody;
 
-import cn.com.qmth.stmms.biz.api.auth.annotation.AuthValidate;
-import cn.com.qmth.stmms.biz.api.auth.exception.ApiException;
 import cn.com.qmth.stmms.biz.exam.model.Exam;
 import cn.com.qmth.stmms.biz.exam.model.ExamPackage;
 import cn.com.qmth.stmms.biz.exam.service.ExamPackageService;
 import cn.com.qmth.stmms.biz.exam.service.ExamService;
 import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
-import cn.com.qmth.stmms.biz.user.model.User;
 import cn.com.qmth.stmms.common.utils.RequestUtils;
 import net.sf.json.JSONArray;
 import net.sf.json.JSONObject;
@@ -43,11 +44,11 @@ public class ExamPackageController extends BaseApiController {
     @Autowired
     private ExamPackageService packageService;
 
-    @AuthValidate({ "adminUser", "scanner" })
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SCANNER })
     @RequestMapping(value = "/package/{examId}", method = RequestMethod.GET)
     @ResponseBody
     public JSONArray getPackageCode(HttpServletRequest request, @PathVariable Integer examId) {
-        User user = RequestUtils.getApiUser(request);
+        ApiUser user = RequestUtils.getApiUser(request);
         JSONArray array = new JSONArray();
         Exam exam = examService.findById(examId);
         if (exam != null && exam.getSchoolId().equals(user.getSchoolId())) {
@@ -63,11 +64,10 @@ public class ExamPackageController extends BaseApiController {
         return array;
     }
 
-    @AuthValidate({ "adminUser", "scanner" })
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SCANNER })
     @RequestMapping(value = "/package/{examId}", method = RequestMethod.POST)
     @ResponseBody
-    public int updatePackage(HttpServletRequest request, @PathVariable Integer examId,
-            @RequestBody ExamPackage examPackage) {
+    public int updatePackage(HttpServletRequest request, @PathVariable Integer examId, @RequestBody ExamPackage examPackage) {
         Exam exam = examService.findById(examId);
         if (exam != null) {
             ExamPackage obj = packageService.find(examId, examPackage.getCode());
@@ -82,12 +82,12 @@ public class ExamPackageController extends BaseApiController {
         return -1;
     }
 
-    @AuthValidate("adminUser")
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SCANNER })
     @RequestMapping(value = "/package/count/{examId}", method = RequestMethod.GET)
     @ResponseBody
-    public JSONArray getPackageCount(HttpServletRequest request, HttpServletResponse response,
-            @PathVariable Integer examId, @RequestParam(required = false) Boolean upload) {
-        User user = RequestUtils.getApiUser(request);
+    public JSONArray getPackageCount(HttpServletRequest request, HttpServletResponse response, @PathVariable Integer examId,
+            @RequestParam(required = false) Boolean upload) {
+        ApiUser user = RequestUtils.getApiUser(request);
         JSONArray array = new JSONArray();
         Exam exam = examService.findById(examId);
         if (exam != null && exam.getSchoolId().equals(user.getSchoolId())) {

+ 36 - 44
stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/ExamStudentController.java

@@ -1,13 +1,15 @@
 package cn.com.qmth.stmms.api.controller;
 
 import java.text.DecimalFormat;
-import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 
 import javax.servlet.http.HttpServletRequest;
 
+import cn.com.qmth.stmms.api.exception.ApiException;
+import cn.com.qmth.stmms.common.annotation.RoleRequire;
+import cn.com.qmth.stmms.common.domain.ApiUser;
+import cn.com.qmth.stmms.common.enums.Role;
 import net.sf.json.JSONArray;
 import net.sf.json.JSONObject;
 
@@ -25,8 +27,6 @@ import org.springframework.web.bind.annotation.ResponseBody;
 
 import cn.com.qmth.stmms.api.utils.AESUtil;
 import cn.com.qmth.stmms.api.utils.MenualAbsentDTO;
-import cn.com.qmth.stmms.biz.api.auth.annotation.AuthValidate;
-import cn.com.qmth.stmms.biz.api.auth.exception.ApiException;
 import cn.com.qmth.stmms.biz.campus.model.Campus;
 import cn.com.qmth.stmms.biz.campus.service.CampusService;
 import cn.com.qmth.stmms.biz.exam.model.Exam;
@@ -74,12 +74,11 @@ public class ExamStudentController extends BaseApiController {
     @Autowired
     private UserService userService;
 
-    @AuthValidate("adminUser")
+    @RoleRequire({ Role.SCHOOL_ADMIN })
     @RequestMapping(value = "/student/manualAbsent/{examId}", method = RequestMethod.POST)
     @ResponseBody
-    public Object updateManualAbsent(HttpServletRequest request, @PathVariable Integer examId,
-            @RequestBody MenualAbsentDTO[] datas) {
-        User user = RequestUtils.getApiUser(request);
+    public Object updateManualAbsent(HttpServletRequest request, @PathVariable Integer examId, @RequestBody MenualAbsentDTO[] datas) {
+        ApiUser user = RequestUtils.getApiUser(request);
         Exam exam = examService.findById(examId);
         if (exam != null && exam.getSchoolId().equals(user.getSchoolId())) {
             for (MenualAbsentDTO dto : datas) {
@@ -91,11 +90,11 @@ public class ExamStudentController extends BaseApiController {
         }
     }
 
-    @AuthValidate("adminUser")
+    @RoleRequire({ Role.SCHOOL_ADMIN })
     @RequestMapping(value = "/student/manualAbsent/clear", method = RequestMethod.POST)
     @ResponseBody
     public Object clearManualAbsent(HttpServletRequest request, @RequestParam Integer examId) {
-        User user = RequestUtils.getApiUser(request);
+        ApiUser user = RequestUtils.getApiUser(request);
         Exam exam = examService.findById(examId);
         if (exam != null && exam.getSchoolId().equals(user.getSchoolId())) {
             studentService.clearManualAbsent(examId);
@@ -105,7 +104,7 @@ public class ExamStudentController extends BaseApiController {
         }
     }
 
-    @AuthValidate({ "adminUser", "scanner" })
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SCANNER })
     @RequestMapping(value = "/exam/students/{examId}", method = RequestMethod.GET)
     @ResponseBody
     public JSONArray getExamStudents(HttpServletRequest request, @PathVariable Integer examId) {
@@ -132,12 +131,12 @@ public class ExamStudentController extends BaseApiController {
         return array;
     }
 
-    @AuthValidate({ "adminUser", "scanner" })
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SCANNER })
     @RequestMapping(value = "/students/{examId}", method = RequestMethod.GET)
     @ResponseBody
-    public JSONArray getStudent(HttpServletRequest request, @PathVariable Integer examId,
-            @RequestParam(required = false) Boolean upload, @RequestParam(required = false) Boolean absent,
-            @RequestParam(required = false) Integer pageNumber, @RequestParam(required = false) Integer pageSize) {
+    public JSONArray getStudent(HttpServletRequest request, @PathVariable Integer examId, @RequestParam(required = false) Boolean upload,
+            @RequestParam(required = false) Boolean absent, @RequestParam(required = false) Integer pageNumber,
+            @RequestParam(required = false) Integer pageSize) {
         JSONArray array = new JSONArray();
         Exam exam = examService.findById(examId);
         if (exam == null) {
@@ -173,14 +172,13 @@ public class ExamStudentController extends BaseApiController {
         return array;
     }
 
-    @AuthValidate({ "adminUser", "scanner" })
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SCANNER })
     @RequestMapping(value = "/student/check", method = RequestMethod.POST)
     @ResponseBody
     public JSONObject checkStudent(HttpServletRequest request, @RequestBody ExamStudent examStudent) {
         JSONObject obj = new JSONObject();
         Exam exam = examService.findById(examStudent.getExamId());
-        ExamStudent student = studentService.findByExamIdAndExamNumber(examStudent.getExamId(),
-                examStudent.getExamNumber());
+        ExamStudent student = studentService.findByExamIdAndExamNumber(examStudent.getExamId(), examStudent.getExamNumber());
         if (student != null) {
             obj.accumulate("examId", examStudent.getExamId());
             obj.accumulate("campusName", student.getCampusName());
@@ -205,12 +203,12 @@ public class ExamStudentController extends BaseApiController {
         return obj;
     }
 
-    @AuthValidate("adminUser")
+    @RoleRequire({ Role.SCHOOL_ADMIN })
     @RequestMapping("/students/count/{examId}")
     @ResponseBody
-    public long getStudentCount(HttpServletRequest request, @PathVariable Integer examId,
-            @RequestParam(required = false) Boolean upload, @RequestParam(required = false) Boolean absent) {
-        User user = RequestUtils.getApiUser(request);
+    public long getStudentCount(HttpServletRequest request, @PathVariable Integer examId, @RequestParam(required = false) Boolean upload,
+            @RequestParam(required = false) Boolean absent) {
+        ApiUser user = RequestUtils.getApiUser(request);
         Exam exam = examService.findById(examId);
         if (exam != null && exam.getSchoolId().equals(user.getSchoolId())) {
             ExamStudentSearchQuery query = new ExamStudentSearchQuery();
@@ -222,13 +220,12 @@ public class ExamStudentController extends BaseApiController {
         return 0;
     }
 
-    @AuthValidate("adminUser")
+    @RoleRequire({ Role.SCHOOL_ADMIN })
     @RequestMapping("/exam/students")
     @ResponseBody
     public JSONArray getStudent(HttpServletRequest request, ExamStudentSearchQuery query,
-            @RequestParam(required = false) Boolean withScoreDetail,
-            @RequestParam(required = false) Boolean withMarkTrack) {
-        User user = RequestUtils.getApiUser(request);
+            @RequestParam(required = false) Boolean withScoreDetail, @RequestParam(required = false) Boolean withMarkTrack) {
+        ApiUser user = RequestUtils.getApiUser(request);
         JSONArray array = new JSONArray();
         if (query.getExamId() == null) {
             return array;
@@ -256,10 +253,8 @@ public class ExamStudentController extends BaseApiController {
                 obj.accumulate("absent", student.isAbsent());
                 obj.accumulate("manualAbsent", student.isManualAbsent());
                 obj.accumulate("breach", student.isBreach());
-                obj.accumulate("objectiveScore",
-                        student.getObjectiveScore() == null ? "" : format.format(student.getObjectiveScore()));
-                obj.accumulate("subjectiveScore",
-                        student.getSubjectiveScore() == null ? "" : format.format(student.getSubjectiveScore()));
+                obj.accumulate("objectiveScore", student.getObjectiveScore() == null ? "" : format.format(student.getObjectiveScore()));
+                obj.accumulate("subjectiveScore", student.getSubjectiveScore() == null ? "" : format.format(student.getSubjectiveScore()));
                 obj.accumulate("examSite", StringUtils.trimToEmpty(student.getExamSite()));
                 obj.accumulate("examRoom", StringUtils.trimToEmpty(student.getExamRoom()));
                 obj.accumulate("remark", StringUtils.trimToEmpty(student.getRemark()));
@@ -271,8 +266,9 @@ public class ExamStudentController extends BaseApiController {
                         // 构造客观题得分明细
                         JSONArray objective = new JSONArray();
                         List<ScoreItem> scoreList = student.getScoreList(true);
-                        List<ExamQuestion> questionList = questionService.findByExamAndSubjectAndObjectiveAndPaperType(
-                                student.getExamId(), student.getSubjectCode(), true, student.getPaperType());
+                        List<ExamQuestion> questionList = questionService
+                                .findByExamAndSubjectAndObjectiveAndPaperType(student.getExamId(), student.getSubjectCode(), true,
+                                        student.getPaperType());
                         int i = 0;
                         for (ScoreItem item : scoreList) {
                             i++;
@@ -295,8 +291,8 @@ public class ExamStudentController extends BaseApiController {
                         // 构造主观题得分明细
                         JSONArray subjective = new JSONArray();
                         scoreList = student.getScoreList(false);
-                        questionList = questionService.findByExamAndSubjectAndObjective(student.getExamId(),
-                                student.getSubjectCode(), false);
+                        questionList = questionService
+                                .findByExamAndSubjectAndObjective(student.getExamId(), student.getSubjectCode(), false);
                         i = 0;
                         for (ScoreItem item : scoreList) {
                             i++;
@@ -361,11 +357,8 @@ public class ExamStudentController extends BaseApiController {
     }
 
     /**
-     * 
-     * @param s
-     *            需要转换的字符串
-     * @param convert
-     *            是否正向转换
+     * @param s       需要转换的字符串
+     * @param convert 是否正向转换
      * @return
      */
     @SuppressWarnings("unused")
@@ -416,17 +409,16 @@ public class ExamStudentController extends BaseApiController {
 
     @RequestMapping(value = "/score/school/{schoolId}", method = RequestMethod.POST)
     @ResponseBody
-    public String getScore(@PathVariable Integer schoolId, @RequestParam String studentCode,
-            @RequestParam String subjectCode, @RequestParam(required = false, defaultValue = "true") boolean encrypt,
-            @RequestParam(required = false) String examSeqCode) {
+    public String getScore(@PathVariable Integer schoolId, @RequestParam String studentCode, @RequestParam String subjectCode,
+            @RequestParam(required = false, defaultValue = "true") boolean encrypt, @RequestParam(required = false) String examSeqCode) {
         JSONObject obj = new JSONObject();
         ExamStudent student = null;
         try {
             if (Strings.isNullOrEmpty(examSeqCode)) {
                 student = studentService.findBySchoolIdAndSubjectCodeAndStudentCode(schoolId, subjectCode, studentCode);
             } else {
-                student = studentService.findBySchoolIdAndSubjectCodeAndStudentCodeAndRemark(schoolId, subjectCode,
-                        studentCode, examSeqCode);
+                student = studentService
+                        .findBySchoolIdAndSubjectCodeAndStudentCodeAndRemark(schoolId, subjectCode, studentCode, examSeqCode);
             }
             if (student != null) {
                 DecimalFormat df = new DecimalFormat("###.#");

+ 41 - 34
stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/LoginController.java

@@ -1,34 +1,41 @@
-package cn.com.qmth.stmms.api.controller;
-
-import java.io.IOException;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.ResponseBody;
-
-import cn.com.qmth.stmms.biz.api.auth.annotation.AuthValidate;
-import cn.com.qmth.stmms.biz.user.model.User;
-import cn.com.qmth.stmms.common.utils.RequestUtils;
-import net.sf.json.JSONObject;
-
-@Controller("loginApiController")
-@RequestMapping("/api")
-public class LoginController extends BaseApiController {
-
-    @AuthValidate({ "adminUser", "scanner" })
-    @RequestMapping(value = "/user/login", method = RequestMethod.GET)
-    @ResponseBody
-    public JSONObject login(HttpServletRequest request, HttpServletResponse response) throws IOException {
-        User user = RequestUtils.getApiUser(request);
-        JSONObject obj = new JSONObject();
-        obj.accumulate("userId", user.getId());
-        obj.accumulate("userName", user.getName());
-        obj.accumulate("campusId", 0);
-        obj.accumulate("schoolId", user.getSchoolId());
-        return obj;
-    }
-}
+package cn.com.qmth.stmms.api.controller;
+
+import javax.servlet.http.HttpServletRequest;
+
+import cn.com.qmth.stmms.api.dto.UserDTO;
+import cn.com.qmth.stmms.api.exception.ApiException;
+import cn.com.qmth.stmms.biz.user.service.UserService;
+import cn.com.qmth.stmms.common.annotation.RoleRequire;
+import cn.com.qmth.stmms.common.domain.ApiUser;
+import cn.com.qmth.stmms.common.enums.Role;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import cn.com.qmth.stmms.biz.user.model.User;
+import cn.com.qmth.stmms.common.utils.RequestUtils;
+import net.sf.json.JSONObject;
+
+@Controller("loginApiController")
+@RequestMapping("/api")
+public class LoginController extends BaseApiController {
+
+    @Autowired
+    private UserService userService;
+
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SCANNER })
+    @RequestMapping(value = "/user/login", method = RequestMethod.GET)
+    @ResponseBody
+    public JSONObject login(HttpServletRequest request) {
+        ApiUser user = RequestUtils.getApiUser(request);
+        JSONObject obj = new JSONObject();
+        obj.accumulate("userId", user.getUserData() != null ? user.getUserData().getId() : 0);
+        obj.accumulate("userName", user.getUserData() != null ? user.getUserData().getName() : "");
+        obj.accumulate("campusId", 0);
+        obj.accumulate("schoolId", user.getSchoolId());
+        return obj;
+    }
+}

+ 7 - 6
stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/PictureController.java

@@ -15,6 +15,7 @@ import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.core.task.AsyncTaskExecutor;
 import org.springframework.http.HttpStatus;
@@ -61,6 +62,7 @@ public class PictureController {
     @Autowired
     private LockService lockService;
 
+    @Qualifier("task-executor")
     @Autowired
     private AsyncTaskExecutor taskExecutor;
 
@@ -75,8 +77,7 @@ public class PictureController {
 
     @RequestMapping("/sheet/{examId}/{examNumber}-{index}")
     public void getSheet(HttpServletResponse response, @PathVariable Integer examId, @PathVariable String examNumber,
-            @PathVariable Integer index, @RequestParam(required = false, defaultValue = "true") Boolean withTag)
-            throws IOException {
+            @PathVariable Integer index, @RequestParam(required = false, defaultValue = "true") Boolean withTag) throws IOException {
         ExamStudent student = studentService.findByExamIdAndExamNumber(examId, examNumber);
         if (student == null || !student.isUpload() || index < 1 || index > student.getSheetCount()) {
             response.sendError(HttpStatus.NOT_FOUND.value());
@@ -131,8 +132,8 @@ public class PictureController {
 
     public BufferedImage getSheetImage(ExamStudent student, int index) throws FileNotFoundException, IOException {
         Campus campus = campusService.findBySchoolAndName(student.getSchoolId(), student.getCampusName());
-        String url = PictureUrlBuilder.getSheetUrl(student.getExamId(), campus.getId(), student.getSubjectCode(),
-                student.getExamNumber(), index);
+        String url = PictureUrlBuilder
+                .getSheetUrl(student.getExamId(), campus.getId(), student.getSubjectCode(), student.getExamNumber(), index);
         if (StringUtils.isNotBlank(baseDir)) {
             return ImageIO.read(new File(new File(baseDir, config.getSheetBucket()), url));
         } else {
@@ -145,8 +146,8 @@ public class PictureController {
 
     public BufferedImage getSliceImage(ExamStudent student, int index) throws FileNotFoundException, IOException {
         Campus campus = campusService.findBySchoolAndName(student.getSchoolId(), student.getCampusName());
-        String url = PictureUrlBuilder.getSliceUrl(student.getExamId(), campus.getId(), student.getSubjectCode(),
-                student.getExamNumber(), index);
+        String url = PictureUrlBuilder
+                .getSliceUrl(student.getExamId(), campus.getId(), student.getSubjectCode(), student.getExamNumber(), index);
         if (StringUtils.isNotBlank(baseDir)) {
             return ImageIO.read(new File(new File(baseDir, config.getSliceBucket()), url));
         } else {

+ 7 - 7
stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/ScanController.java

@@ -2,6 +2,10 @@ package cn.com.qmth.stmms.api.controller;
 
 import javax.servlet.http.HttpServletRequest;
 
+import cn.com.qmth.stmms.api.exception.ApiException;
+import cn.com.qmth.stmms.common.annotation.RoleRequire;
+import cn.com.qmth.stmms.common.domain.ApiUser;
+import cn.com.qmth.stmms.common.enums.Role;
 import net.sf.json.JSONArray;
 import net.sf.json.JSONObject;
 
@@ -17,15 +21,12 @@ import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.annotation.ResponseBody;
 
 import cn.com.qmth.stmms.api.utils.ScanStudentParameter;
-import cn.com.qmth.stmms.biz.api.auth.annotation.AuthValidate;
-import cn.com.qmth.stmms.biz.api.auth.exception.ApiException;
 import cn.com.qmth.stmms.biz.exam.model.CheckStudent;
 import cn.com.qmth.stmms.biz.exam.model.Exam;
 import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
 import cn.com.qmth.stmms.biz.exam.service.CheckStudentService;
 import cn.com.qmth.stmms.biz.exam.service.ExamService;
 import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
-import cn.com.qmth.stmms.biz.user.model.User;
 import cn.com.qmth.stmms.common.enums.CheckType;
 import cn.com.qmth.stmms.common.utils.DateUtils;
 import cn.com.qmth.stmms.common.utils.RequestUtils;
@@ -47,19 +48,18 @@ public class ScanController extends BaseApiController {
 
     /**
      * 上传考生识别结果
-     * 
+     *
      * @param request
      * @param examId
-     * @param subjectId
      * @param scStudentParameter
      * @return
      */
-    @AuthValidate({ "adminUser", "scanner" })
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SCANNER })
     @RequestMapping(value = "/student/{examId}", method = RequestMethod.POST)
     @ResponseBody
     public JSONArray saveStudent(HttpServletRequest request, @PathVariable Integer examId,
             @RequestBody ScanStudentParameter[] scStudentParameter) {
-        User user = RequestUtils.getApiUser(request);
+        ApiUser user = RequestUtils.getApiUser(request);
         Exam exam = examService.findById(examId);
         JSONArray array = new JSONArray();
         // 判断上传权限

+ 53 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/api/dto/UserDTO.java

@@ -0,0 +1,53 @@
+package cn.com.qmth.stmms.api.dto;
+
+import cn.com.qmth.stmms.biz.user.model.User;
+
+public class UserDTO {
+
+    private Integer id;
+
+    private String loginName;
+
+    private String name;
+
+    private String token;
+
+    public UserDTO(User user) {
+        setId(user.getId());
+        setLoginName(user.getLoginName());
+        setName(user.getName());
+        setToken(user.getAccessToken());
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getLoginName() {
+        return loginName;
+    }
+
+    public void setLoginName(String loginName) {
+        this.loginName = loginName;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getToken() {
+        return token;
+    }
+
+    public void setToken(String token) {
+        this.token = token;
+    }
+}

+ 29 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/api/exception/ApiException.java

@@ -0,0 +1,29 @@
+package cn.com.qmth.stmms.api.exception;
+
+public class ApiException extends RuntimeException {
+
+    private static final long serialVersionUID = -4952288587291887490L;
+
+    private int code;
+
+    private ApiException(int code, String message) {
+        super(message);
+        this.code = code;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public static final ApiException AUTHORIZATION_UNEXIST = new ApiException(401, "authorization unexists");
+
+    public static final ApiException SIGNATURE_INVALID = new ApiException(401, "signature invalid");
+
+    public static final ApiException USER_DISABLED = new ApiException(401, "user disabled");
+
+    public static final ApiException USER_ROLE_INVALID = new ApiException(401, "user's role invalid");
+
+    public static final ApiException EXAM_NOT_ACCESSIBLED = new ApiException(401, "user cannot access specified exam");
+
+    public static final ApiException USER_LOGIN_FAILE = new ApiException(500, "user login faile");
+}

+ 135 - 92
stmms-web/src/main/java/cn/com/qmth/stmms/api/interceptor/ApiInterceptor.java

@@ -1,92 +1,135 @@
-package cn.com.qmth.stmms.api.interceptor;
-
-import java.util.List;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.lang.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.HttpStatus;
-import org.springframework.web.bind.annotation.ResponseStatus;
-import org.springframework.web.method.HandlerMethod;
-import org.springframework.web.servlet.ModelAndView;
-import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
-
-import cn.com.qmth.stmms.api.utils.AuthInfoUtils;
-import cn.com.qmth.stmms.biz.api.auth.annotation.AuthValidate;
-import cn.com.qmth.stmms.biz.api.auth.interfaces.AuthValidator;
-import cn.com.qmth.stmms.biz.api.auth.model.AuthInfo;
-import cn.com.qmth.stmms.biz.api.auth.service.AuthInfoService;
-import cn.com.qmth.stmms.biz.api.auth.service.validator.AuthValidateService;
-import cn.com.qmth.stmms.common.utils.RequestUtils;
-
-/**
- * 远程服务接口子模块操作拦截器
- * 
- * @author LS
- * 
- */
-public class ApiInterceptor extends HandlerInterceptorAdapter {
-
-    @Autowired
-    private AuthInfoService infoService;
-
-    @Autowired
-    private AuthValidateService validateService;
-
-    @Override
-    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
-            ModelAndView modelAndView) throws Exception {
-    }
-
-    @Override
-    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
-            throws Exception {
-        HandlerMethod method = (HandlerMethod) handler;
-        AuthValidate validateConfig = method.getMethodAnnotation(AuthValidate.class);
-        if (validateConfig != null) {
-            AuthInfo auth = infoService.getAuthInfo(AuthInfoUtils.getAuthInfoValue(request));
-            List<AuthValidator> validators = validateService.getValidators(validateConfig.value());
-            if (validators != null) {
-                boolean flag = true;
-                HttpStatus status = null;
-                String reason = null;
-                for (AuthValidator validator : validators) {
-                    try {
-                        // 执行验证
-                        flag = validator.validate(auth);
-                    } catch (RuntimeException e) {
-                        // 处理程序异常
-                        flag = false;
-                        ResponseStatus rs = e.getClass().getAnnotation(ResponseStatus.class);
-                        if (rs != null) {
-                            status = rs.value();
-                            reason = rs.reason();
-                        }
-                    }
-                    if (validateConfig.nand() && flag == false) {
-                        // 与的情况,一个验证失败即退出
-                        if (status == null) {
-                            status = HttpStatus.UNAUTHORIZED;
-                        }
-                        break;
-                    } else if (validateConfig.nand() == false && flag == true) {
-                        // 或的情况,一个验证成功即退出
-                        break;
-                    }
-                }
-                if (!flag) {
-                    // 验证失败返回错误码
-                    if (status == null) {
-                        status = HttpStatus.UNAUTHORIZED;
-                    }
-                    response.sendError(status.value(), StringUtils.trimToEmpty(reason));
-                }
-                RequestUtils.setApiUser(request, auth.getLoginUser());
-                return flag;
-            }
-        }
-        return true;
-    }
-}
+package cn.com.qmth.stmms.api.interceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import cn.com.qmth.stmms.api.exception.ApiException;
+import cn.com.qmth.stmms.common.domain.AuthInfo;
+import cn.com.qmth.stmms.biz.school.model.School;
+import cn.com.qmth.stmms.biz.school.service.SchoolService;
+import cn.com.qmth.stmms.biz.user.model.User;
+import cn.com.qmth.stmms.biz.user.service.UserService;
+import cn.com.qmth.stmms.common.authorization.AuthorizationService;
+import cn.com.qmth.stmms.common.annotation.RoleRequire;
+import cn.com.qmth.stmms.common.domain.ApiUser;
+import cn.com.qmth.stmms.common.enums.Role;
+import cn.com.qmth.stmms.common.signature.SignatureInfo;
+import cn.com.qmth.stmms.common.signature.SignatureType;
+import cn.com.qmth.stmms.common.utils.AccessControlUtils;
+import cn.com.qmth.stmms.common.utils.Md5EncryptUtils;
+import cn.com.qmth.stmms.common.utils.RequestUtils;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+/**
+ * API接口访问拦截器
+ *
+ * @author LS
+ */
+public class ApiInterceptor extends HandlerInterceptorAdapter {
+
+    @Autowired
+    private AuthorizationService authorizationService;
+
+    @Autowired
+    private SchoolService schoolService;
+
+    @Autowired
+    private UserService userService;
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        HandlerMethod method = (HandlerMethod) handler;
+        try {
+            return validate(request, method.getMethodAnnotation(RoleRequire.class));
+        } catch (ApiException e) {
+            response.sendError(e.getCode(), StringUtils.trimToEmpty(e.getMessage()));
+            return false;
+        } catch (Exception ee) {
+            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, StringUtils.trimToEmpty(ee.getMessage()));
+            return false;
+        }
+    }
+
+    private boolean validate(HttpServletRequest request, RoleRequire authConfig) {
+        if (authConfig != null && authConfig.value().length > 0) {
+            AuthInfo simple;
+            // 优先按签名模式解析
+            SignatureInfo info = authorizationService.buildSignature(request);
+            if (info != null) {
+                if (info.getType() == SignatureType.SECRET) {
+                    School school = schoolService.findByAccessKey(info.getInvoker());
+                    if (school == null || !info.validate(school.getAccessSecret())) {
+                        throw ApiException.SIGNATURE_INVALID;
+                    }
+                    if (!school.isEnable()) {
+                        throw ApiException.USER_DISABLED;
+                    }
+                    if (!matchRole(authConfig, Role.SCHOOL_DEV)) {
+                        throw ApiException.USER_ROLE_INVALID;
+                    }
+                    return buildApiUser(request, school);
+                } else if (info.getType() == SignatureType.TOKEN) {
+                    User user = userService.findByLoginName(info.getInvoker());
+                    if (user == null || user.getAccessTokenRefreshTime() == null || AccessControlUtils
+                            .expired(user.getAccessTokenRefreshTime()) || !info.validate(user.getAccessToken())) {
+                        throw ApiException.SIGNATURE_INVALID;
+                    }
+                    if (!user.isEnable()) {
+                        throw ApiException.USER_DISABLED;
+                    }
+                    if (matchRole(authConfig, user.getRole())) {
+                        throw ApiException.USER_ROLE_INVALID;
+                    }
+                    return buildApiUser(request, user);
+                } else {
+                    throw ApiException.SIGNATURE_INVALID;
+                }
+            }
+            // 再尝试按简单模式解析
+            else if ((simple = authorizationService.buildAuthInfo(request)) != null) {
+                User user = userService.findByLoginName(simple.getLoginname());
+                if (user == null || !Md5EncryptUtils.md5(simple.getPassword()).equals(user.getPassword())) {
+                    throw ApiException.SIGNATURE_INVALID;
+                }
+                if (!user.isEnable()) {
+                    throw ApiException.USER_DISABLED;
+                }
+                if (!matchRole(authConfig, user.getRole())) {
+                    throw ApiException.USER_ROLE_INVALID;
+                }
+                return buildApiUser(request, user);
+            } else {
+                throw ApiException.AUTHORIZATION_UNEXIST;
+            }
+        }
+        return true;
+    }
+
+    private boolean matchRole(RoleRequire authConfig, Role role) {
+        int count = 0;
+        for (Role r : authConfig.value()) {
+            if (r == role) {
+                count++;
+            }
+        }
+        if (authConfig.nand()) {
+            return count > 0;
+        } else {
+            return count == authConfig.value().length;
+        }
+    }
+
+    private boolean buildApiUser(HttpServletRequest request, School school) {
+        RequestUtils.setApiUser(request, new ApiUser(school));
+        return true;
+    }
+
+    private boolean buildApiUser(HttpServletRequest request, User user) {
+        RequestUtils.setApiUser(request, new ApiUser(user));
+        return true;
+    }
+
+}

+ 0 - 16
stmms-web/src/main/java/cn/com/qmth/stmms/api/utils/AuthInfoUtils.java

@@ -1,16 +0,0 @@
-package cn.com.qmth.stmms.api.utils;
-
-import javax.servlet.http.HttpServletRequest;
-
-import org.apache.commons.lang.StringUtils;
-
-public class AuthInfoUtils {
-
-    public static final String AUTH_INFO_HEADER_KEY = "auth-info";
-    
-    public static final String ERROR_MESSAGE_HEADER_KEY = "error-info";
-
-    public static String getAuthInfoValue(HttpServletRequest request) {
-        return StringUtils.trimToNull(request.getHeader(AUTH_INFO_HEADER_KEY));
-    }
-}

+ 38 - 39
stmms-web/src/main/java/cn/com/qmth/stmms/common/auth/annotation/RoleRequire.java → stmms-web/src/main/java/cn/com/qmth/stmms/common/annotation/RoleRequire.java

@@ -1,39 +1,38 @@
-package cn.com.qmth.stmms.common.auth.annotation;
-
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-import cn.com.qmth.stmms.common.enums.Role;
-
-/**
- * 通用方法角色验证定义
- * 
- * @author LS
- * 
- */
-@Target({ METHOD })
-@Retention(RUNTIME)
-public @interface RoleRequire {
-
-    /**
-     * 指定需要的权限列表
-     */
-    Role[] value() default {};
-
-    /**
-     * 多个权限之间是否只需满足任意一个即可,默认为false
-     * 
-     * @return
-     */
-    boolean nand() default false;
-
-    /**
-     * 验证失败后的跳转URI
-     * 
-     * @return
-     */
-    String redirect() default "";
-}
+package cn.com.qmth.stmms.common.annotation;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import cn.com.qmth.stmms.common.enums.Role;
+
+/**
+ * 通用方法角色验证定义
+ *
+ * @author LS
+ */
+@Target({ METHOD })
+@Retention(RUNTIME)
+public @interface RoleRequire {
+
+    /**
+     * 指定需要的权限列表
+     */
+    Role[] value() default {};
+
+    /**
+     * 多个权限之间是否只需满足任意一个即可,默认为true
+     *
+     * @return
+     */
+    boolean nand() default true;
+
+    /**
+     * 验证失败后的跳转URI
+     *
+     * @return
+     */
+    String redirect() default "";
+}

+ 45 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/common/authorization/AuthorizationService.java

@@ -0,0 +1,45 @@
+package cn.com.qmth.stmms.common.authorization;
+
+import cn.com.qmth.stmms.common.domain.AuthInfo;
+import cn.com.qmth.stmms.common.signature.SignatureInfo;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpServletRequest;
+
+@Service("authorizationService")
+public class AuthorizationService {
+
+    private static final String HEADER_AUTHORIZATION_KEY = "authorization";
+
+    private static final String HEADER_DATE_KEY = "date";
+
+    private static final String HEADER_AUTHINFO_KEY = "auth-info";
+
+    private static final long TIME_AHEAD_SECOND = 5;
+
+    private static final long TIME_EXPIRE_SECOND = 15;
+
+    public SignatureInfo buildSignature(HttpServletRequest request) {
+        long timestamp = 0;
+        try {
+            timestamp = Long.parseLong(StringUtils.trimToNull(request.getHeader(HEADER_DATE_KEY)));
+        } catch (Exception e) {
+        }
+        if (!expired(timestamp)) {
+            return SignatureInfo.parse(request.getMethod(), request.getServletPath(), timestamp,
+                    StringUtils.trimToNull(request.getHeader(HEADER_AUTHORIZATION_KEY)));
+        }
+        return null;
+    }
+
+    public AuthInfo buildAuthInfo(HttpServletRequest request) {
+        return AuthInfo.parse(StringUtils.trimToEmpty(request.getHeader(HEADER_AUTHINFO_KEY)));
+    }
+
+    private boolean expired(long timestamp) {
+        long diff = (System.currentTimeMillis() - timestamp) / 1000;
+        return diff < (-1 * TIME_AHEAD_SECOND) || diff > TIME_EXPIRE_SECOND;
+    }
+
+}

+ 28 - 20
stmms-web/src/main/java/cn/com/qmth/stmms/common/controller/BaseController.java

@@ -4,6 +4,7 @@ import java.beans.PropertyEditorSupport;
 import java.util.Date;
 import java.util.List;
 
+import cn.com.qmth.stmms.common.enums.*;
 import org.apache.commons.lang3.StringEscapeUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.propertyeditors.CustomBooleanEditor;
@@ -20,14 +21,6 @@ import cn.com.qmth.stmms.biz.user.model.User;
 import cn.com.qmth.stmms.biz.user.service.UserService;
 import cn.com.qmth.stmms.biz.utils.ScoreCalculateUtil;
 import cn.com.qmth.stmms.biz.utils.ScoreInfo;
-import cn.com.qmth.stmms.common.enums.CheckType;
-import cn.com.qmth.stmms.common.enums.ExamSubjectStatus;
-import cn.com.qmth.stmms.common.enums.HistoryStatus;
-import cn.com.qmth.stmms.common.enums.LibraryStatus;
-import cn.com.qmth.stmms.common.enums.MarkMode;
-import cn.com.qmth.stmms.common.enums.MarkStatus;
-import cn.com.qmth.stmms.common.enums.ObjectivePolicy;
-import cn.com.qmth.stmms.common.enums.Role;
 import cn.com.qmth.stmms.common.utils.DateUtils;
 
 public class BaseController {
@@ -43,7 +36,7 @@ public class BaseController {
 
     @Autowired
     private ExamQuestionService questionService;
-    
+
     @Autowired
     private MarkGroupService groupService;
 
@@ -143,7 +136,19 @@ public class BaseController {
                 }
             }
         });
-        
+        // AccountType 类型转换
+        binder.registerCustomEditor(AccountType.class, new PropertyEditorSupport() {
+
+            @Override
+            public void setAsText(String text) {
+                try {
+                    setValue(AccountType.findByName(text));
+                } catch (Exception e) {
+                    setValue(null);
+                }
+            }
+        });
+
         // HistoryStatus 类型转换
         binder.registerCustomEditor(HistoryStatus.class, new PropertyEditorSupport() {
 
@@ -181,20 +186,22 @@ public class BaseController {
                 }
             }
         });
-        
+
         binder.registerCustomEditor(Boolean.class, new CustomBooleanEditor(true));
     }
 
     protected boolean saveUploadStudent(ExamStudent student) {
         ExamStudent old = studentService.findById(student.getId());
-        if(!student.isAbsent()){//正考
-            List<MarkGroup> groupList = groupService.findByExamAndSubjectAndStatus(student.getExamId(), student.getSubjectCode(), MarkStatus.FINISH);
+        if (!student.isAbsent()) {//正考
+            List<MarkGroup> groupList = groupService
+                    .findByExamAndSubjectAndStatus(student.getExamId(), student.getSubjectCode(), MarkStatus.FINISH);
             for (MarkGroup markGroup : groupList) {
-                groupService.updateStatus(student.getExamId(), student.getSubjectCode(), markGroup.getNumber(), MarkStatus.FORMAL, MarkStatus.FINISH);
+                groupService.updateStatus(student.getExamId(), student.getSubjectCode(), markGroup.getNumber(), MarkStatus.FORMAL,
+                        MarkStatus.FINISH);
             }
         }
         calculateObjectiveScore(student);
-        if(!old.isAbsent() && student.isAbsent()){//正考转缺考
+        if (!old.isAbsent() && student.isAbsent()) {//正考转缺考
             student.setObjectiveScore(0d);
             student.setObjectiveScoreList(null);
             student.setSubjectiveScore(0d);
@@ -202,9 +209,9 @@ public class BaseController {
             studentService.save(student);
         }
         boolean success = studentService.updateScanInfo(student);
-        if(success){
-        	subjectService.updateUploadCount(student.getExamId(), student.getSubjectCode(), (int) studentService
-        			.countUploadedByExamIdAndSubjectCode(student.getExamId(), student.getSubjectCode()));
+        if (success) {
+            subjectService.updateUploadCount(student.getExamId(), student.getSubjectCode(),
+                    (int) studentService.countUploadedByExamIdAndSubjectCode(student.getExamId(), student.getSubjectCode()));
         }
         return success;
     }
@@ -212,8 +219,9 @@ public class BaseController {
     private void calculateObjectiveScore(ExamStudent student) {
         ScoreCalculateUtil util = ScoreCalculateUtil.instance(student);
 
-        ScoreInfo info = util.calculate(questionService.findByExamAndSubjectAndObjectiveAndPaperType(
-                student.getExamId(), student.getSubjectCode(), true, student.getPaperType()), null);
+        ScoreInfo info = util.calculate(questionService
+                        .findByExamAndSubjectAndObjectiveAndPaperType(student.getExamId(), student.getSubjectCode(), true, student.getPaperType()),
+                null);
 
         student.setObjectiveScore(info.getObjectiveScore());
         student.setScoreList(info.getScoreList(), true);

+ 11 - 14
stmms-web/src/main/java/cn/com/qmth/stmms/common/controller/LoginController.java

@@ -70,14 +70,13 @@ public class LoginController {
                 if (u.getPassword().equals(Md5EncryptUtils.md5(user.getPassword()))) {
                     u.setLastLoginTime(new Date());
                     u.setLastLoginIp(request.getRemoteAddr());
-                    String webToken = UUID.randomUUID().toString();
-                    u.setWebToken(webToken);
-                    userService.save(u);
+                    u.refreshAccessToken();
+                    u = userService.save(u);
 
-                    new WebUser(u.getId(), u.getRole(), webToken).writeToSession(session);
+                    session.saveWebUser(new WebUser(u));
 
-                    if (u.getRole() == Role.SYS_ADMIN || u.getRole() == Role.SCHOOL_ADMIN
-                            || u.getRole() == Role.SUBJECT_HEADER || u.getRole() == Role.SCHOOL_VIEWER) {
+                    if (u.getRole() == Role.SYS_ADMIN || u.getRole() == Role.SCHOOL_ADMIN || u.getRole() == Role.SUBJECT_HEADER
+                            || u.getRole() == Role.SCHOOL_VIEWER) {
                         ModelAndView modelAndView = new ModelAndView("redirect:admin/home");
                         return modelAndView;
                     } else {
@@ -117,8 +116,7 @@ public class LoginController {
                     modelAndView.addObject("indexLogo", indexLogo);
                     return modelAndView;
                 }
-                MarkGroup group = groupService.findOne(marker.getExamId(), marker.getSubjectCode(),
-                        marker.getGroupNumber());
+                MarkGroup group = groupService.findOne(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
                 if (group == null) {
                     modelAndView.addObject("message", "大题不存在");
                     modelAndView.addObject("showType", showType);
@@ -131,17 +129,17 @@ public class LoginController {
                     modelAndView.addObject("indexLogo", indexLogo);
                     return modelAndView;
                 }
-                String webToken = UUID.randomUUID().toString();
-                new WebUser(marker.getId(), Role.MARKER, webToken).writeToSession(RequestUtils.getSession(request));
-                marker.setWebToken(webToken);
+                marker.refreshAccessToken();
                 marker.setLastLoginIp(request.getRemoteAddr());
+                RequestUtils.getSession(request).saveWebUser(new WebUser(marker));
+
                 if (marker.getLastLoginTime() == null) {
                     marker.setLastLoginTime(new Date());
                     markerService.save(marker);
                     return new ModelAndView("redirect:/mark/reset");
                 }
                 marker.setLastLoginTime(new Date());
-                markerService.save(marker);
+                marker = markerService.save(marker);
                 return new ModelAndView("redirect:/mark/index");
             }
 
@@ -167,8 +165,7 @@ public class LoginController {
 
     /**
      * 登出
-     * 
-     * @param user
+     *
      * @param request
      * @param response
      * @return

+ 45 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/common/domain/ApiUser.java

@@ -0,0 +1,45 @@
+package cn.com.qmth.stmms.common.domain;
+
+import cn.com.qmth.stmms.biz.school.model.School;
+import cn.com.qmth.stmms.biz.user.model.User;
+import cn.com.qmth.stmms.common.enums.Role;
+
+public class ApiUser {
+
+    private Integer schoolId;
+
+    private User userData;
+
+    private School schoolData;
+
+    private Role role;
+
+    public ApiUser(School school) {
+        schoolData = school;
+        schoolId = school.getId();
+        role = Role.SCHOOL_DEV;
+    }
+
+    public ApiUser(User user) {
+        userData = user;
+        schoolId = user.getSchoolId();
+        role = user.getRole();
+    }
+
+    public Integer getSchoolId() {
+        return schoolId;
+    }
+
+    public Role getRole() {
+        return role;
+    }
+
+    public User getUserData() {
+        return userData;
+    }
+
+    public School getSchoolData() {
+        return schoolData;
+    }
+
+}

+ 55 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/common/domain/AuthInfo.java

@@ -0,0 +1,55 @@
+package cn.com.qmth.stmms.common.domain;
+
+import org.apache.commons.lang.StringUtils;
+
+public class AuthInfo {
+
+    private static final String FIELD_JOINER = ";";
+
+    private static final String VALUE_JOINER = "=";
+
+    private static final String LOGINNAME_KEY = "loginname";
+
+    private static final String PASSWORD_KEY = "password";
+
+    private String loginname;
+
+    private String password;
+
+    public String getLoginname() {
+        return loginname;
+    }
+
+    private void setLoginname(String loginname) {
+        this.loginname = loginname;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    private void setPassword(String password) {
+        this.password = password;
+    }
+
+    private AuthInfo() {
+    }
+
+    public static AuthInfo parse(String input) {
+        AuthInfo info = new AuthInfo();
+        String[] fields = StringUtils.split(input, FIELD_JOINER);
+        if (fields != null && fields.length > 0) {
+            for (String field : fields) {
+                String[] values = StringUtils.split(field, VALUE_JOINER);
+                if (values != null && values.length == 2) {
+                    if (values[0].equals(LOGINNAME_KEY)) {
+                        info.setLoginname(values[1]);
+                    } else if (values[0].equals(PASSWORD_KEY)) {
+                        info.setPassword(values[1]);
+                    }
+                }
+            }
+        }
+        return info.getLoginname() != null && info.getPassword() != null ? info : null;
+    }
+}

+ 63 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/common/domain/OpenUser.java

@@ -0,0 +1,63 @@
+package cn.com.qmth.stmms.common.domain;
+
+import cn.com.qmth.stmms.biz.exam.model.Marker;
+import cn.com.qmth.stmms.biz.user.model.OpenAccount;
+import cn.com.qmth.stmms.biz.user.model.User;
+import cn.com.qmth.stmms.common.enums.Role;
+
+/**
+ * 第三方账号访问用户抽象描述类
+ *
+ * @author luoshi
+ */
+public class OpenUser {
+
+    private Integer id;
+
+    private String name;
+
+    private Role role;
+
+    private String returnUrl;
+
+    private String token;
+
+    private OpenAccount userData;
+
+    private OpenUser(Integer id, String name, Role role, String token, String returnUrl, OpenAccount userData) {
+        this.id = id;
+        this.name = name;
+        this.role = role;
+        this.token = token;
+        this.returnUrl = returnUrl;
+        this.userData = userData;
+    }
+
+    public OpenUser(OpenAccount account, String returnUrl) {
+        this(account.getId(), account.getName(), Role.OPEN_ACCOUNT, account.getAccessToken(), returnUrl, account);
+    }
+
+    public OpenAccount getOpenAccount() {
+        return userData;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Role getRole() {
+        return role;
+    }
+
+    public String getToken() {
+        return token;
+    }
+
+    public String getReturnUrl() {
+        return returnUrl;
+    }
+}

+ 28 - 52
stmms-web/src/main/java/cn/com/qmth/stmms/common/domain/WebUser.java

@@ -1,5 +1,7 @@
 package cn.com.qmth.stmms.common.domain;
 
+import cn.com.qmth.stmms.biz.school.model.School;
+import cn.com.qmth.stmms.biz.user.model.OpenAccount;
 import org.apache.commons.lang.StringUtils;
 
 import cn.com.qmth.stmms.biz.exam.model.Marker;
@@ -8,19 +10,12 @@ import cn.com.qmth.stmms.common.enums.Role;
 import cn.com.qmth.stmms.common.session.model.StmmsSession;
 
 /**
- * 云阅卷前台已登录用户抽象描述类
- * 
+ * web端访问用户抽象描述类
+ *
  * @author luoshi
- * 
  */
 public class WebUser {
 
-    private static final String SESSION_USER_ID_KEY = "_web_user_id_";
-
-    private static final String SESSION_USER_ROLE_KEY = "_web_user_role_";
-
-    private static final String SESSION_USER_TOKEN_KEY = "_web_user_token_";
-
     private Integer id;
 
     private String name;
@@ -29,40 +24,29 @@ public class WebUser {
 
     private Object userData;
 
-    private String webToken;
+    private String token;
+
+    private String logoutUrl;
 
-    public WebUser(Integer id, Role role, String webToken) {
+    private WebUser(Integer id, String name, Role role, String token, Object userData, String logoutUrl) {
         this.id = id;
+        this.name = name;
         this.role = role;
-        this.webToken = webToken;
-
-        if (id == null || role == null) {
-            throw new IllegalArgumentException("webUser init error!");
-        }
+        this.token = token;
+        this.userData = userData;
+        this.logoutUrl = logoutUrl;
     }
 
-    public void writeToSession(StmmsSession session) {
-        session.setParameter(SESSION_USER_ID_KEY, String.valueOf(id));
-        session.setParameter(SESSION_USER_ROLE_KEY, String.valueOf(role.getValue()));
-        session.setParameter(SESSION_USER_TOKEN_KEY, webToken);
+    public WebUser(User user) {
+        this(user.getId(), user.getName(), user.getRole(), user.getAccessToken(), user, null);
     }
 
-    public static WebUser buildFromSession(StmmsSession session) {
-        WebUser wu = null;
-        String userId = StringUtils.trimToNull(session.getParameter(SESSION_USER_ID_KEY));
-        String userRole = StringUtils.trimToNull(session.getParameter(SESSION_USER_ROLE_KEY));
-        String webToken = StringUtils.trimToNull(session.getParameter(SESSION_USER_TOKEN_KEY));
-        if (StringUtils.isNumeric(userId) && StringUtils.isNumeric(userRole)) {
-            try {
-                wu = new WebUser(Integer.valueOf(userId), Role.findByValue(Integer.valueOf(userRole)), webToken);
-            } catch (Exception e) {
-            }
-        }
-        return wu;
+    public WebUser(Marker marker, String logoutUrl) {
+        this(marker.getId(), marker.getName(), Role.MARKER, marker.getAccessToken(), marker, logoutUrl);
     }
 
-    public void setDataObject(Object userData) {
-        this.userData = userData;
+    public WebUser(Marker marker) {
+        this(marker, null);
     }
 
     public User getUser() {
@@ -73,24 +57,24 @@ public class WebUser {
         return userData != null && userData instanceof Marker ? (Marker) userData : null;
     }
 
-    public String getName() {
-        return name;
-    }
-
-    public void setName(String name) {
-        this.name = name;
-    }
-
     public Integer getId() {
         return id;
     }
 
+    public String getName() {
+        return name;
+    }
+
     public Role getRole() {
         return role;
     }
 
-    public void setRole(Role role) {
-        this.role = role;
+    public String getToken() {
+        return token;
+    }
+
+    public String getLogoutUrl() {
+        return logoutUrl;
     }
 
     public boolean isSubjectHeader() {
@@ -105,12 +89,4 @@ public class WebUser {
         return role == Role.SCHOOL_VIEWER;
     }
 
-    public String getWebToken() {
-        return webToken;
-    }
-
-    public void setWebToken(String webToken) {
-        this.webToken = webToken;
-    }
-
 }

+ 0 - 97
stmms-web/src/main/java/cn/com/qmth/stmms/common/filter/SessionFilter.java

@@ -1,97 +0,0 @@
-package cn.com.qmth.stmms.common.filter;
-
-import java.io.IOException;
-import java.util.HashSet;
-import java.util.Map.Entry;
-import java.util.Set;
-
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.lang.StringUtils;
-import org.springframework.web.context.WebApplicationContext;
-import org.springframework.web.context.support.WebApplicationContextUtils;
-
-import cn.com.qmth.stmms.common.session.model.StmmsSession;
-import cn.com.qmth.stmms.common.session.service.SessionService;
-import cn.com.qmth.stmms.common.utils.RequestUtils;
-
-/**
- * 对每次动态页面的请求时,更新会话cookie的有效期
- */
-public class SessionFilter extends HttpServlet implements Filter {
-
-    private Set<String> excludeURIs;
-
-    private static final long serialVersionUID = -6516046520244652987L;
-
-    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
-            throws IOException, ServletException {
-        HttpServletRequest request = (HttpServletRequest) servletRequest;
-        HttpServletResponse response = (HttpServletResponse) servletResponse;
-
-        String contextPath = request.getContextPath();
-        String uri = request.getRequestURI();
-        // 去掉contextpath
-        if (!contextPath.equals("/") && StringUtils.isNotEmpty(contextPath)) {
-            int i = uri.indexOf('/', 2);
-            uri = uri.substring(i, uri.length());
-        }
-        RequestUtils.setURI(request, uri);
-
-        if (!isExcludeURIPrefix(uri)) {
-            WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(request
-                    .getSession().getServletContext());
-            SessionService sessionService = (SessionService) webApplicationContext.getBean("sessionService");
-            StmmsSession session = sessionService.get(request);
-            RequestUtils.setSession(request, sessionService.get(request));
-            for (Entry<String, String> entry : session.getParameters()) {
-                request.setAttribute(entry.getKey(), entry.getValue());
-            }
-            filterChain.doFilter(request, response);
-            // try {
-            // filterChain.doFilter(request, response);
-            // } catch (Exception e) {
-            // } finally {
-            // sessionService.put(request, response,
-            // RequestUtils.getSession(request));
-            // }
-        } else {
-            filterChain.doFilter(request, response);
-        }
-    }
-
-    @Override
-    public void init(FilterConfig filterConfig) throws ServletException {
-        excludeURIs = new HashSet<String>();
-        String value = filterConfig.getInitParameter("excludeURI");
-        if (StringUtils.isNotBlank(value)) {
-            String[] values = StringUtils.split(value, ",");
-            if (values != null && values.length > 0) {
-                for (String uri : values) {
-                    excludeURIs.add(uri);
-                }
-            }
-        }
-    }
-
-    public boolean isExcludeURIPrefix(String uri) {
-        for (String value : excludeURIs) {
-            if (value.equals("/")) {
-                if (uri.equals(value)) {
-                    return true;
-                }
-            } else if (uri.startsWith(value)) {
-                return true;
-            }
-        }
-        return false;
-    }
-}

+ 0 - 116
stmms-web/src/main/java/cn/com/qmth/stmms/common/interceptor/CommonInterceptor.java

@@ -1,116 +0,0 @@
-package cn.com.qmth.stmms.common.interceptor;
-
-import java.io.IOException;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.servlet.ModelAndView;
-import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
-
-import cn.com.qmth.stmms.common.domain.WebUser;
-import cn.com.qmth.stmms.common.session.model.StmmsSession;
-import cn.com.qmth.stmms.common.session.service.SessionService;
-import cn.com.qmth.stmms.common.utils.RequestUtils;
-
-/**
- * 基础拦截器,用于解析session构造基础WebUser对象
- * 
- * @author LS
- * 
- */
-public class CommonInterceptor extends HandlerInterceptorAdapter {
-
-    private static Logger log = LoggerFactory.getLogger(CommonInterceptor.class);
-
-    @Autowired
-    private SessionService sessionService;
-
-    @Override
-    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
-        buildWebUser(request, response);
-        return true;
-    }
-
-    @Override
-    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
-            ModelAndView modelAndView) throws Exception {
-        String uri = request.getRequestURI();
-        if (!uri.startsWith("/api")) {
-            try {
-                sessionService.put(request, response, RequestUtils.getSession(request));
-            } catch (Exception e) {
-                log.error("stmms session save faile!", e);
-            }
-        }
-
-    }
-
-    /**
-     * 强制失效session,执行请求跳转
-     * 
-     * @param request
-     * @param response
-     * @param redirectURI
-     * @return
-     * @throws IOException
-     */
-    protected boolean sessionExpire(HttpServletRequest request, HttpServletResponse response, String redirectURI)
-            throws IOException {
-        StmmsSession session = RequestUtils.getSession(request);
-        session.setInvalid(true);
-        sessionService.put(request, response, session);
-        response.sendRedirect(request.getContextPath() + redirectURI);
-        return false;
-    }
-
-    /**
-     * 强制执行请求跳转
-     * 
-     * @param request
-     * @param response
-     * @param redirectURI
-     * @return
-     * @throws IOException
-     */
-    protected boolean redirect(HttpServletRequest request, HttpServletResponse response, String redirectURI)
-            throws IOException {
-        response.sendRedirect(request.getContextPath() + redirectURI);
-        return false;
-    }
-
-    /**
-     * 强制失效session,执行请求跳转
-     * 
-     * @param request
-     * @param response
-     * @param redirectURI
-     * @return
-     * @throws IOException
-     */
-    protected boolean sessionExpireStatus(HttpServletRequest request, HttpServletResponse response, String redirectURI)
-            throws IOException {
-        StmmsSession session = RequestUtils.getSession(request);
-        session.setInvalid(true);
-        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, redirectURI);
-        sessionService.put(request, response, session);
-        return false;
-    }
-
-    /**
-     * 从session中构造WebUser对象
-     * 
-     * @param request
-     * @param response
-     * @throws Exception
-     */
-    private void buildWebUser(HttpServletRequest request, HttpServletResponse response) throws Exception {
-        WebUser wu = WebUser.buildFromSession(RequestUtils.getSession(request));
-        if (wu != null) {
-            RequestUtils.setWebUser(request, wu);
-        }
-    }
-}

+ 62 - 65
stmms-web/src/main/java/cn/com/qmth/stmms/common/interceptor/MethodInterceptor.java

@@ -1,65 +1,62 @@
-package cn.com.qmth.stmms.common.interceptor;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.lang.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.web.method.HandlerMethod;
-import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
-
-import cn.com.qmth.stmms.common.auth.annotation.RoleRequire;
-import cn.com.qmth.stmms.common.domain.WebUser;
-import cn.com.qmth.stmms.common.enums.Role;
-import cn.com.qmth.stmms.common.utils.RequestUtils;
-
-/**
- * 方法操作拦截器
- * 
- * @author LS
- * 
- */
-public class MethodInterceptor extends HandlerInterceptorAdapter {
-
-    private static Logger log = LoggerFactory.getLogger(MethodInterceptor.class);
-
-    @Override
-    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
-            throws Exception {
-        return (handler instanceof HandlerMethod) && validateAuth((HandlerMethod) handler, request, response);
-    }
-
-    private boolean validateAuth(HandlerMethod method, HttpServletRequest request, HttpServletResponse response)
-            throws Exception {
-        boolean result = true;
-        WebUser wu = RequestUtils.getWebUser(request);
-        RoleRequire roleRequire = method.getMethodAnnotation(RoleRequire.class);
-        if (wu != null && roleRequire != null) {
-            Role[] roles = roleRequire.value();
-            if (roles != null && roles.length > 0) {
-                int count = 0;
-                for (Role role : roles) {
-                    if (wu.getRole() == role) {
-                        count++;
-                    }
-                }
-                if (roleRequire.nand() && count < roles.length) {
-                    result = false;
-                } else if (!roleRequire.nand() && count == 0) {
-                    result = false;
-                }
-            }
-        }
-        if (!result) {
-            log.info("method auth vaildate faile!");
-            String redirect = StringUtils.trimToNull(roleRequire.redirect());
-            if (redirect != null) {
-                response.sendRedirect(request.getContextPath() + redirect);
-            } else {
-                response.sendError(401, "没有权限进行此操作");
-            }
-        }
-        return result;
-    }
-}
+package cn.com.qmth.stmms.common.interceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+import cn.com.qmth.stmms.common.annotation.RoleRequire;
+import cn.com.qmth.stmms.common.domain.WebUser;
+import cn.com.qmth.stmms.common.enums.Role;
+import cn.com.qmth.stmms.common.utils.RequestUtils;
+
+/**
+ * 方法操作拦截器
+ *
+ * @author LS
+ */
+public class MethodInterceptor extends HandlerInterceptorAdapter {
+
+    private static Logger log = LoggerFactory.getLogger(MethodInterceptor.class);
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        return (handler instanceof HandlerMethod) && validateAuth((HandlerMethod) handler, request, response);
+    }
+
+    private boolean validateAuth(HandlerMethod method, HttpServletRequest request, HttpServletResponse response) throws Exception {
+        boolean result = true;
+        WebUser wu = RequestUtils.getWebUser(request);
+        RoleRequire roleRequire = method.getMethodAnnotation(RoleRequire.class);
+        if (wu != null && roleRequire != null) {
+            Role[] roles = roleRequire.value();
+            if (roles != null && roles.length > 0) {
+                int count = 0;
+                for (Role role : roles) {
+                    if (wu.getRole() == role) {
+                        count++;
+                    }
+                }
+                if (!roleRequire.nand() && count < roles.length) {
+                    result = false;
+                } else if (roleRequire.nand() && count == 0) {
+                    result = false;
+                }
+            }
+        }
+        if (!result) {
+            log.info("method auth vaildate faile!");
+            String redirect = StringUtils.trimToNull(roleRequire.redirect());
+            if (redirect != null) {
+                response.sendRedirect(request.getContextPath() + redirect);
+            } else {
+                response.sendError(401, "没有权限进行此操作");
+            }
+        }
+        return result;
+    }
+}

+ 90 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/common/interceptor/SessionInterceptor.java

@@ -0,0 +1,90 @@
+package cn.com.qmth.stmms.common.interceptor;
+
+import java.io.IOException;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+import cn.com.qmth.stmms.common.session.model.StmmsSession;
+import cn.com.qmth.stmms.common.session.service.SessionService;
+import cn.com.qmth.stmms.common.utils.RequestUtils;
+
+/**
+ * web访问基础拦截器,用于构造基础session对象
+ *
+ * @author LS
+ */
+public class SessionInterceptor extends HandlerInterceptorAdapter {
+
+    private static Logger log = LoggerFactory.getLogger(SessionInterceptor.class);
+
+    @Autowired
+    private SessionService sessionService;
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        buildSession(request);
+        return true;
+    }
+
+    @Override
+    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
+        try {
+            sessionService.put(request, response, RequestUtils.getSession(request));
+        } catch (Exception e) {
+            log.error("stmms session save faile!", e);
+        }
+    }
+
+    /**
+     * 强制失效session,执行请求跳转
+     *
+     * @param request
+     * @param response
+     * @param redirectURI
+     * @return
+     * @throws IOException
+     */
+    protected boolean sessionExpire(HttpServletRequest request, HttpServletResponse response, String redirectURI) throws IOException {
+        StmmsSession session = RequestUtils.getSession(request);
+        session.setInvalid(true);
+        sessionService.put(request, response, session);
+        response.sendRedirect(request.getContextPath() + redirectURI);
+        return false;
+    }
+
+    /**
+     * 强制执行请求跳转
+     *
+     * @param request
+     * @param response
+     * @param redirectURI
+     * @return
+     * @throws IOException
+     */
+    protected boolean redirect(HttpServletRequest request, HttpServletResponse response, String redirectURI) throws IOException {
+        response.sendRedirect(request.getContextPath() + redirectURI);
+        return false;
+    }
+
+    /**
+     * 初始化session对象
+     *
+     * @param request
+     */
+    protected void buildSession(HttpServletRequest request) {
+        StmmsSession session = sessionService.get(request);
+        RequestUtils.setSession(request, session);
+        for (Map.Entry<String, String> entry : session.getParameters()) {
+            request.setAttribute(entry.getKey(), entry.getValue());
+        }
+    }
+
+}

+ 137 - 61
stmms-web/src/main/java/cn/com/qmth/stmms/common/session/model/StmmsSession.java

@@ -1,61 +1,137 @@
-package cn.com.qmth.stmms.common.session.model;
-
-import java.io.Serializable;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
-import javax.persistence.Transient;
-
-public class StmmsSession implements Serializable {
-
-    private static final long serialVersionUID = -1004172926154352747L;
-
-    private String id;
-
-    private Map<String, String> map;
-
-    @Transient
-    private boolean invalid;
-
-    public StmmsSession() {
-        this.map = new HashMap<String, String>();
-    }
-
-    public String getParameter(String key) {
-        return map.get(key);
-    }
-
-    public void setParameter(String key, String value) {
-        map.put(key, value);
-    }
-
-    public Set<Entry<String, String>> getParameters() {
-        return map.entrySet();
-    }
-
-    public String getId() {
-        return id;
-    }
-
-    public void setId(String id) {
-        this.id = id;
-    }
-
-    public void clear() {
-        this.id = null;
-        this.map.clear();
-    }
-
-    public boolean isInvalid() {
-        return invalid;
-    }
-
-    public void setInvalid(boolean invalid) {
-        this.invalid = invalid;
-        if (isInvalid()) {
-            clear();
-        }
-    }
-}
+package cn.com.qmth.stmms.common.session.model;
+
+import cn.com.qmth.stmms.common.domain.OpenUser;
+import cn.com.qmth.stmms.common.domain.WebUser;
+import cn.com.qmth.stmms.common.enums.Role;
+import org.apache.commons.lang.StringUtils;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import javax.persistence.Transient;
+
+public class StmmsSession implements Serializable {
+
+    private static final long serialVersionUID = -1004172926154352747L;
+
+    private static final String WEB_USER_ID_KEY = "_web_user_id_";
+
+    private static final String WEB_USER_ROLE_KEY = "_web_user_role_";
+
+    private static final String WEB_USER_TOKEN_KEY = "_web_user_token_";
+
+    private static final String WEB_USER_LOGOUT_URL = "_web_user_logout_url";
+
+    private static final String OPEN_USER_ID_KEY = "_open_user_id_";
+
+    private static final String OPEN_USER_TOKEN_KEY = "_open_user_token_";
+
+    private static final String OPEN_USER_RETURN_URL_KEY = "_open_user_url_";
+
+    private String id;
+
+    private Map<String, String> map;
+
+    @Transient
+    private boolean invalid;
+
+    public StmmsSession() {
+        this.map = new HashMap<String, String>();
+    }
+
+    public String getParameter(String key) {
+        return map.get(key);
+    }
+
+    public void setParameter(String key, String value) {
+        map.put(key, value);
+    }
+
+    public Set<Entry<String, String>> getParameters() {
+        return map.entrySet();
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public void clear() {
+        this.id = null;
+        this.map.clear();
+    }
+
+    public boolean isInvalid() {
+        return invalid;
+    }
+
+    public void setInvalid(boolean invalid) {
+        this.invalid = invalid;
+        if (isInvalid()) {
+            clear();
+        }
+    }
+
+    public Integer getOpenUserId() {
+        String value = getParameter(OPEN_USER_ID_KEY);
+        try {
+            return Integer.parseInt(value);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    public String getOpenUserToken() {
+        return getParameter(OPEN_USER_TOKEN_KEY);
+    }
+
+    public String getOpenUserReturnUrl() {
+        return getParameter(OPEN_USER_RETURN_URL_KEY);
+    }
+
+    public Integer getWebUserId() {
+        String value = getParameter(WEB_USER_ID_KEY);
+        try {
+            return Integer.parseInt(value);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    public Role getWebUserRole() {
+        String value = getParameter(WEB_USER_ROLE_KEY);
+        try {
+            return Role.findByValue(Integer.parseInt(value));
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    public String getWebUserToken() {
+        return getParameter(WEB_USER_TOKEN_KEY);
+    }
+
+    public String getWebUserLogoutUrl() {
+        return getParameter(WEB_USER_LOGOUT_URL);
+    }
+
+    public void saveWebUser(WebUser webUser) {
+        setParameter(WEB_USER_ID_KEY, String.valueOf(webUser.getId()));
+        setParameter(WEB_USER_ROLE_KEY, String.valueOf(webUser.getRole()));
+        setParameter(WEB_USER_TOKEN_KEY, StringUtils.trimToEmpty(webUser.getToken()));
+        setParameter(WEB_USER_LOGOUT_URL, StringUtils.trimToEmpty(webUser.getLogoutUrl()));
+    }
+
+    public void saveOpenUser(OpenUser openUser) {
+        setParameter(OPEN_USER_ID_KEY, String.valueOf(openUser.getId()));
+        setParameter(OPEN_USER_TOKEN_KEY, StringUtils.trimToEmpty(openUser.getToken()));
+        setParameter(OPEN_USER_RETURN_URL_KEY, StringUtils.trimToEmpty(openUser.getReturnUrl()));
+    }
+
+}

+ 35 - 37
stmms-web/src/main/java/cn/com/qmth/stmms/common/session/service/SessionService.java

@@ -1,37 +1,35 @@
-package cn.com.qmth.stmms.common.session.service;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import cn.com.qmth.stmms.common.session.model.StmmsSession;
-
-public interface SessionService {
-
-    /**
-     * 保存session
-     * 
-     * @param request
-     * @param reponse
-     * @param key
-     * @param value
-     */
-    void put(HttpServletRequest request, HttpServletResponse response, StmmsSession ss);
-
-    /**
-     * 获取session
-     * 
-     * @param request
-     * @param key
-     * @return
-     */
-    StmmsSession get(HttpServletRequest request);
-
-    /**
-     * 删除session
-     * 
-     * @param reponse
-     * @param sessionId
-     */
-    void remove(HttpServletRequest request, HttpServletResponse response);
-
-}
+package cn.com.qmth.stmms.common.session.service;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import cn.com.qmth.stmms.common.session.model.StmmsSession;
+
+public interface SessionService {
+
+    /**
+     * 保存session
+     *
+     * @param request
+     * @param response
+     * @param ss
+     */
+    void put(HttpServletRequest request, HttpServletResponse response, StmmsSession ss);
+
+    /**
+     * 获取session
+     *
+     * @param request
+     * @return
+     */
+    StmmsSession get(HttpServletRequest request);
+
+    /**
+     * 删除session
+     *
+     * @param request
+     * @param reponse
+     */
+    void remove(HttpServletRequest request, HttpServletResponse response);
+
+}

+ 101 - 114
stmms-web/src/main/java/cn/com/qmth/stmms/common/session/service/impl/CookieSessionServiceImpl.java

@@ -1,114 +1,101 @@
-package cn.com.qmth.stmms.common.session.service.impl;
-
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.lang.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.InitializingBean;
-import org.springframework.beans.factory.annotation.Value;
-
-import cn.com.qmth.stmms.common.session.model.StmmsSession;
-import cn.com.qmth.stmms.common.session.service.SessionService;
-import cn.com.qmth.stmms.common.utils.AesUtils;
-import cn.com.qmth.stmms.common.utils.HessianUtils;
-
-public class CookieSessionServiceImpl implements SessionService, InitializingBean {
-
-    private static Logger log = LoggerFactory.getLogger(CookieSessionServiceImpl.class);
-
-    @Value("${cookie.max.age}")
-    private Integer cookieMaxAge;
-
-    @Value("${cookie.domain}")
-    private String cookieDomain;
-
-    @Value("${cookie.path}")
-    private String cookiePath;
-
-    private static final String COOKIE_NAME = "stmms_cookie";
-
-    @Override
-    public void put(HttpServletRequest request, HttpServletResponse response, StmmsSession ss) {
-        if (ss.isInvalid()) {
-            remove(request, response);
-        } else {
-            try {
-                ss.setId(request.getSession().getId());
-                setCookie(response, COOKIE_NAME, AesUtils.encrypt(HessianUtils.serialize(ss)), cookieMaxAge);
-            } catch (Exception e) {
-                log.error("CookieSessionServiceImpl-put", e);
-                remove(request, response);
-            }
-        }
-    }
-
-    @Override
-    public StmmsSession get(HttpServletRequest request) {
-        StmmsSession session = new StmmsSession();
-        Cookie cookie = getCookie(request, COOKIE_NAME);
-        try {
-            session = (StmmsSession) HessianUtils.deserialize(AesUtils.decrypt(cookie.getValue()));
-            if (!session.getId().equals(request.getSession().getId())) {
-                session.clear();
-            }
-        } catch (Exception e) {
-            if (session == null) {
-                session = new StmmsSession();
-            }
-        }
-        return session;
-    }
-
-    @Override
-    public void remove(HttpServletRequest request, HttpServletResponse response) {
-        setCookie(response, COOKIE_NAME, "", -1);
-    }
-
-    /**
-     * 获取COOKIE
-     * 
-     * @param request
-     * @param name
-     */
-    private Cookie getCookie(HttpServletRequest request, String name) {
-        Cookie[] cookies = request.getCookies();
-        if (cookies == null)
-            return null;
-        for (int i = 0; i < cookies.length; i++) {
-            if (name.equals(cookies[i].getName())) {
-                return cookies[i];
-            }
-        }
-        return null;
-    }
-
-    /**
-     * 设置COOKIE
-     * 
-     * @param request
-     * @param response
-     * @param name
-     * @param value
-     * @param maxAge
-     */
-    private void setCookie(HttpServletResponse response, String name, String value, int maxAge) {
-        Cookie cookie = new Cookie(name, value);
-        cookie.setMaxAge(maxAge);
-        if (cookieDomain != null && cookieDomain.indexOf('.') != -1) {
-            cookie.setDomain('.' + cookieDomain);
-        }
-        cookie.setPath(cookiePath);
-        response.addCookie(cookie);
-    }
-
-    @Override
-    public void afterPropertiesSet() throws Exception {
-        if (StringUtils.isBlank(cookiePath)) {
-            cookiePath = "/";
-        }
-    }
-
-}
+package cn.com.qmth.stmms.common.session.service.impl;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Value;
+
+import cn.com.qmth.stmms.common.session.model.StmmsSession;
+import cn.com.qmth.stmms.common.session.service.SessionService;
+import cn.com.qmth.stmms.common.utils.AesUtils;
+import cn.com.qmth.stmms.common.utils.HessianUtils;
+
+public class CookieSessionServiceImpl implements SessionService {
+
+    private static Logger log = LoggerFactory.getLogger(CookieSessionServiceImpl.class);
+
+    private static final int COOKIE_MAX_AGE = 3600;
+
+    private static final String COOKIE_DOMAIN = "";
+
+    private static final String COOKIE_PATH = "/";
+
+    private static final String COOKIE_NAME = "stmms_cookie";
+
+    @Override
+    public void put(HttpServletRequest request, HttpServletResponse response, StmmsSession ss) {
+        if (ss.isInvalid()) {
+            remove(request, response);
+        } else {
+            try {
+                ss.setId(request.getSession().getId());
+                setCookie(response, COOKIE_NAME, AesUtils.encrypt(HessianUtils.serialize(ss)), COOKIE_MAX_AGE);
+            } catch (Exception e) {
+                log.error("CookieSessionServiceImpl-put", e);
+                remove(request, response);
+            }
+        }
+    }
+
+    @Override
+    public StmmsSession get(HttpServletRequest request) {
+        StmmsSession session = new StmmsSession();
+        Cookie cookie = getCookie(request, COOKIE_NAME);
+        try {
+            session = (StmmsSession) HessianUtils.deserialize(AesUtils.decrypt(cookie.getValue()));
+            if (!session.getId().equals(request.getSession().getId())) {
+                session.clear();
+            }
+        } catch (Exception e) {
+            if (session == null) {
+                session = new StmmsSession();
+            }
+        }
+        return session;
+    }
+
+    @Override
+    public void remove(HttpServletRequest request, HttpServletResponse response) {
+        setCookie(response, COOKIE_NAME, "", -1);
+    }
+
+    /**
+     * 获取COOKIE
+     *
+     * @param request
+     * @param name
+     */
+    private Cookie getCookie(HttpServletRequest request, String name) {
+        Cookie[] cookies = request.getCookies();
+        if (cookies == null)
+            return null;
+        for (int i = 0; i < cookies.length; i++) {
+            if (name.equals(cookies[i].getName())) {
+                return cookies[i];
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 设置COOKIE
+     *
+     * @param response
+     * @param name
+     * @param value
+     * @param maxAge
+     */
+    private void setCookie(HttpServletResponse response, String name, String value, int maxAge) {
+        Cookie cookie = new Cookie(name, value);
+        cookie.setMaxAge(maxAge);
+        cookie.setDomain('.' + COOKIE_DOMAIN);
+        cookie.setPath(COOKIE_PATH);
+        response.addCookie(cookie);
+    }
+
+}

+ 152 - 159
stmms-web/src/main/java/cn/com/qmth/stmms/common/utils/RequestUtils.java

@@ -1,160 +1,153 @@
-package cn.com.qmth.stmms.common.utils;
-
-import java.io.UnsupportedEncodingException;
-import java.net.URLDecoder;
-import java.net.URLEncoder;
-
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import cn.com.qmth.stmms.biz.user.model.User;
-import cn.com.qmth.stmms.common.domain.WebUser;
-import cn.com.qmth.stmms.common.session.model.StmmsSession;
-
-/**
- * request临时参数存取
- */
-public class RequestUtils {
-
-    private static final String REQUEST_SESSION_KEY = "_stmms_session_key_";
-
-    private static final String REQUEST_URI_KEY = "request_uri";
-
-    private static final String REQUEST_WEB_USER_KEY = "web_user";
-
-    private static final String REQUEST_API_USER_KEY = "api_user";
-
-    public static StmmsSession getSession(HttpServletRequest request) {
-        Object obj = request.getAttribute(REQUEST_SESSION_KEY);
-        if (obj != null && obj instanceof StmmsSession) {
-            return (StmmsSession) obj;
-        } else {
-            StmmsSession session = new StmmsSession();
-            setSession(request, session);
-            return session;
-        }
-    }
-
-    public static void setSession(HttpServletRequest request, StmmsSession session) {
-        request.setAttribute(REQUEST_SESSION_KEY, session);
-    }
-
-    public static String getURI(HttpServletRequest request) {
-        Object obj = request.getAttribute(REQUEST_URI_KEY);
-        if (obj != null && obj instanceof String) {
-            return (String) obj;
-        } else {
-            return "";
-        }
-    }
-
-    public static void setURI(HttpServletRequest request, String uri) {
-        request.setAttribute(REQUEST_URI_KEY, uri);
-    }
-
-    public static WebUser getWebUser(HttpServletRequest request) {
-        Object obj = request.getAttribute(REQUEST_WEB_USER_KEY);
-        if (obj != null && obj instanceof WebUser) {
-            return (WebUser) obj;
-        } else {
-            return null;
-        }
-    }
-
-    public static void setWebUser(HttpServletRequest request, WebUser user) {
-        request.setAttribute(REQUEST_WEB_USER_KEY, user);
-    }
-
-    public static User getApiUser(HttpServletRequest request) {
-        Object obj = request.getAttribute(REQUEST_API_USER_KEY);
-        if (obj != null && obj instanceof User) {
-            return (User) obj;
-        } else {
-            return null;
-        }
-    }
-
-    public static void setApiUser(HttpServletRequest request, User user) {
-        request.setAttribute(REQUEST_API_USER_KEY, user);
-    }
-
-    /**
-     * 设置 Cookie
-     * 
-     * @param name
-     *            名称
-     * @param value
-     *            值
-     * @param maxAge
-     *            生存时间(单位秒)
-     */
-    public static void setCookie(HttpServletResponse response, String name, String value, int maxAge) {
-        Cookie cookie = new Cookie(name, null);
-        cookie.setPath("/");
-        cookie.setMaxAge(maxAge);
-        try {
-            cookie.setValue(URLEncoder.encode(value, "utf-8"));
-        } catch (UnsupportedEncodingException e) {
-            e.printStackTrace();
-        }
-        response.addCookie(cookie);
-    }
-
-    /**
-     * 获得指定Cookie的值
-     * 
-     * @param name
-     *            名称
-     * @return 值
-     */
-    public static String getCookie(HttpServletRequest request, String name) {
-        return getCookie(request, null, name, false);
-    }
-
-    /**
-     * 获得指定Cookie的值,并删除。
-     * 
-     * @param name
-     *            名称
-     * @return 值
-     */
-    public static String getCookie(HttpServletRequest request, HttpServletResponse response, String name) {
-        return getCookie(request, response, name, true);
-    }
-
-    /**
-     * 获得指定Cookie的值
-     * 
-     * @param request
-     *            请求对象
-     * @param response
-     *            响应对象
-     * @param name
-     *            名字
-     * @param isRemove
-     *            是否移除
-     * @return 值
-     */
-    public static String getCookie(HttpServletRequest request, HttpServletResponse response, String name,
-            boolean isRemove) {
-        String value = null;
-        Cookie[] cookies = request.getCookies();
-        if (cookies != null) {
-            for (Cookie cookie : cookies) {
-                if (cookie.getName().equals(name)) {
-                    try {
-                        value = URLDecoder.decode(cookie.getValue(), "utf-8");
-                    } catch (UnsupportedEncodingException e) {
-                        e.printStackTrace();
-                    }
-                    if (isRemove) {
-                        cookie.setMaxAge(0);
-                        response.addCookie(cookie);
-                    }
-                }
-            }
-        }
-        return value;
-    }
+package cn.com.qmth.stmms.common.utils;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import cn.com.qmth.stmms.api.exception.ApiException;
+import cn.com.qmth.stmms.biz.user.model.User;
+import cn.com.qmth.stmms.common.domain.ApiUser;
+import cn.com.qmth.stmms.common.domain.OpenUser;
+import cn.com.qmth.stmms.common.domain.WebUser;
+import cn.com.qmth.stmms.common.session.model.StmmsSession;
+
+/**
+ * request临时参数存取
+ */
+public class RequestUtils {
+
+    private static final String REQUEST_SESSION_KEY = "_stmms_session_key_";
+
+    private static final String REQUEST_WEB_USER_KEY = "web_user";
+
+    private static final String REQUEST_API_USER_KEY = "api_user";
+
+    private static final String REQUEST_OPEN_USER_KEY = "open_user";
+
+    public static StmmsSession getSession(HttpServletRequest request) {
+        Object obj = request.getAttribute(REQUEST_SESSION_KEY);
+        if (obj != null && obj instanceof StmmsSession) {
+            return (StmmsSession) obj;
+        } else {
+            StmmsSession session = new StmmsSession();
+            setSession(request, session);
+            return session;
+        }
+    }
+
+    public static void setSession(HttpServletRequest request, StmmsSession session) {
+        request.setAttribute(REQUEST_SESSION_KEY, session);
+    }
+
+    public static WebUser getWebUser(HttpServletRequest request) {
+        Object obj = request.getAttribute(REQUEST_WEB_USER_KEY);
+        if (obj != null && obj instanceof WebUser) {
+            return (WebUser) obj;
+        } else {
+            return null;
+        }
+    }
+
+    public static void setWebUser(HttpServletRequest request, WebUser user) {
+        request.setAttribute(REQUEST_WEB_USER_KEY, user);
+    }
+
+    public static ApiUser getApiUser(HttpServletRequest request) {
+        Object obj = request.getAttribute(REQUEST_API_USER_KEY);
+        if (obj != null && obj instanceof ApiUser) {
+            return (ApiUser) obj;
+        } else {
+            return null;
+        }
+    }
+
+    public static void setApiUser(HttpServletRequest request, ApiUser user) {
+        request.setAttribute(REQUEST_API_USER_KEY, user);
+    }
+
+    public static OpenUser getOpenUser(HttpServletRequest request) {
+        Object obj = request.getAttribute(REQUEST_OPEN_USER_KEY);
+        if (obj != null && obj instanceof OpenUser) {
+            return (OpenUser) obj;
+        } else {
+            return null;
+        }
+    }
+
+    public static void setOpenUser(HttpServletRequest request, OpenUser user) {
+        request.setAttribute(REQUEST_OPEN_USER_KEY, user);
+    }
+
+    /**
+     * 设置 Cookie
+     *
+     * @param name   名称
+     * @param value  值
+     * @param maxAge 生存时间(单位秒)
+     */
+    public static void setCookie(HttpServletResponse response, String name, String value, int maxAge) {
+        Cookie cookie = new Cookie(name, null);
+        cookie.setPath("/");
+        cookie.setMaxAge(maxAge);
+        try {
+            cookie.setValue(URLEncoder.encode(value, "utf-8"));
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        response.addCookie(cookie);
+    }
+
+    /**
+     * 获得指定Cookie的值
+     *
+     * @param name 名称
+     * @return 值
+     */
+    public static String getCookie(HttpServletRequest request, String name) {
+        return getCookie(request, null, name, false);
+    }
+
+    /**
+     * 获得指定Cookie的值,并删除。
+     *
+     * @param name 名称
+     * @return 值
+     */
+    public static String getCookie(HttpServletRequest request, HttpServletResponse response, String name) {
+        return getCookie(request, response, name, true);
+    }
+
+    /**
+     * 获得指定Cookie的值
+     *
+     * @param request  请求对象
+     * @param response 响应对象
+     * @param name     名字
+     * @param isRemove 是否移除
+     * @return 值
+     */
+    public static String getCookie(HttpServletRequest request, HttpServletResponse response, String name, boolean isRemove) {
+        String value = null;
+        Cookie[] cookies = request.getCookies();
+        if (cookies != null) {
+            for (Cookie cookie : cookies) {
+                if (cookie.getName().equals(name)) {
+                    try {
+                        value = URLDecoder.decode(cookie.getValue(), "utf-8");
+                    } catch (UnsupportedEncodingException e) {
+                        e.printStackTrace();
+                    }
+                    if (isRemove) {
+                        cookie.setMaxAge(0);
+                        response.addCookie(cookie);
+                    }
+                }
+            }
+        }
+        return value;
+    }
 }

+ 28 - 26
stmms-web/src/main/java/cn/com/qmth/stmms/mark/MarkController.java

@@ -6,6 +6,7 @@ import java.util.List;
 
 import javax.servlet.http.HttpServletRequest;
 
+import cn.com.qmth.stmms.common.domain.WebUser;
 import net.sf.json.JSONObject;
 
 import org.apache.commons.lang.StringEscapeUtils;
@@ -166,16 +167,19 @@ public class MarkController extends BaseController {
 
     @RequestMapping("/logout")
     public ModelAndView logout(HttpServletRequest request) {
-        releaseMarker(RequestUtils.getWebUser(request).getMarker());
-        return new ModelAndView("redirect:/logout");
+        WebUser wu = RequestUtils.getWebUser(request);
+        releaseMarker(wu.getMarker());
+        if (StringUtils.isNotBlank(wu.getLogoutUrl())) {
+            return new ModelAndView("redirect:" + wu.getLogoutUrl());
+        } else {
+            return new ModelAndView("redirect:/logout");
+        }
     }
 
     /**
      * 进入评卷界面后的通用预处理
-     * 
+     *
      * @param marker
-     * @param session
-     * @param modelAndView
      * @param modelAndView
      */
     private void preProcess(Marker marker, ModelAndView modelAndView) {
@@ -273,8 +277,7 @@ public class MarkController extends BaseController {
             lockService.watch(LockType.GROUP, marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
             lockService.watch(LockType.MARKER, marker.getId());
 
-            MarkGroup group = groupService
-                    .findOne(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
+            MarkGroup group = groupService.findOne(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
             if (group == null) {
                 task = new Task();
                 task.setExist(false);
@@ -308,8 +311,8 @@ public class MarkController extends BaseController {
         while (task == null) {
             List<MarkLibrary> list = new ArrayList<MarkLibrary>();
             // 需要判断评卷员是否绑定了班级
-            list = libraryService.findUnMarked(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber(),
-                    marker.getId(), marker.getClassCount() != null && marker.getClassCount() > 0, retry, 20);
+            list = libraryService.findUnMarked(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber(), marker.getId(),
+                    marker.getClassCount() != null && marker.getClassCount() > 0, retry, 20);
             if (list.isEmpty()) {
                 break;
             }
@@ -330,8 +333,8 @@ public class MarkController extends BaseController {
         int retry = 1;
         Task task = null;
         while (task == null) {
-            List<TrialLibrary> list = trialService.findUnMarkedLibrary(marker.getExamId(), marker.getSubjectCode(),
-                    marker.getGroupNumber(), marker.getId(), retry, 10);
+            List<TrialLibrary> list = trialService
+                    .findUnMarkedLibrary(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber(), marker.getId(), retry, 10);
             if (list.isEmpty()) {
                 break;
             }
@@ -356,15 +359,15 @@ public class MarkController extends BaseController {
             lockService.watch(LockType.GROUP, marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
             lockService.watch(LockType.STUDENT, markResult.getStudentId());
             lockService.watch(LockType.MARKER, marker.getId());
-            lockService.waitlock(LockType.GROUP_LIBRARY, markResult.getStudentId(), marker.getExamId(),
-                    marker.getSubjectCode(), marker.getGroupNumber());
+            lockService.waitlock(LockType.GROUP_LIBRARY, markResult.getStudentId(), marker.getExamId(), marker.getSubjectCode(),
+                    marker.getGroupNumber());
             success = markService.submitTask(markResult, marker);
         } catch (Exception e) {
             success = false;
             log.error("save task error", e);
         } finally {
-            lockService.unlock(LockType.GROUP_LIBRARY, markResult.getStudentId(), marker.getExamId(),
-                    marker.getSubjectCode(), marker.getGroupNumber());
+            lockService.unlock(LockType.GROUP_LIBRARY, markResult.getStudentId(), marker.getExamId(), marker.getSubjectCode(),
+                    marker.getGroupNumber());
             lockService.unwatch(LockType.MARKER, marker.getId());
             lockService.unwatch(LockType.STUDENT, markResult.getStudentId());
             lockService.unwatch(LockType.GROUP, marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
@@ -379,9 +382,8 @@ public class MarkController extends BaseController {
 
     @RequestMapping("/gethistory")
     @ResponseBody
-    public Object history(HttpServletRequest request, @RequestParam int pageNumber, @RequestParam int pageSize,
-            @RequestParam String order, @RequestParam String sort,
-            @RequestParam(required = false, defaultValue = "false") Boolean isTag,
+    public Object history(HttpServletRequest request, @RequestParam int pageNumber, @RequestParam int pageSize, @RequestParam String order,
+            @RequestParam String sort, @RequestParam(required = false, defaultValue = "false") Boolean isTag,
             @RequestParam(required = false) Integer studentId) throws Exception {
         Marker marker = RequestUtils.getWebUser(request).getMarker();
         List<Task> list = new ArrayList<>();
@@ -425,11 +427,13 @@ public class MarkController extends BaseController {
             // 试评查找给分历史记录
             List<TrialHistory> historyList = new ArrayList<TrialHistory>();
             if (studentId != null) {
-                historyList = trialService.findHistory(marker.getExamId(), marker.getSubjectCode(),
-                        marker.getGroupNumber(), marker.getId(), studentId, pageNumber, pageSize, querySort);
+                historyList = trialService
+                        .findHistory(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber(), marker.getId(), studentId,
+                                pageNumber, pageSize, querySort);
             } else {
-                historyList = trialService.findHistory(marker.getExamId(), marker.getSubjectCode(),
-                        marker.getGroupNumber(), marker.getId(), pageNumber, pageSize, querySort, null);
+                historyList = trialService
+                        .findHistory(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber(), marker.getId(), pageNumber,
+                                pageSize, querySort, null);
             }
             for (TrialHistory history : historyList) {
                 TrialLibrary library = trialService.findLibrary(history.getLibraryId());
@@ -445,8 +449,7 @@ public class MarkController extends BaseController {
 
     @RequestMapping("/change-name")
     @ResponseBody
-    public JSONObject changeName(HttpServletRequest request, @RequestParam String name,
-            @RequestParam(required = false) String password) {
+    public JSONObject changeName(HttpServletRequest request, @RequestParam String name, @RequestParam(required = false) String password) {
         Marker marker = RequestUtils.getWebUser(request).getMarker();
         JSONObject result = new JSONObject();
         marker.setName(name);
@@ -466,8 +469,7 @@ public class MarkController extends BaseController {
     public JSONObject updateSetting(HttpServletRequest request, @RequestParam String setting) {
         Marker marker = RequestUtils.getWebUser(request).getMarker();
         JSONObject result = new JSONObject();
-        markerService
-                .updateMarkSetting(marker.getId(), StringEscapeUtils.unescapeHtml(StringUtils.trimToNull(setting)));
+        markerService.updateMarkSetting(marker.getId(), StringEscapeUtils.unescapeHtml(StringUtils.trimToNull(setting)));
         result.accumulate("success", true);
         return result;
     }

+ 25 - 30
stmms-web/src/main/java/cn/com/qmth/stmms/mark/interceptor/MarkInterceptor.java

@@ -3,60 +3,55 @@ package cn.com.qmth.stmms.mark.interceptor;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import cn.com.qmth.stmms.common.enums.Role;
+import cn.com.qmth.stmms.common.session.model.StmmsSession;
+import cn.com.qmth.stmms.common.utils.AccessControlUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 
 import cn.com.qmth.stmms.biz.exam.model.Marker;
 import cn.com.qmth.stmms.biz.exam.service.MarkerService;
 import cn.com.qmth.stmms.common.domain.WebUser;
-import cn.com.qmth.stmms.common.interceptor.CommonInterceptor;
+import cn.com.qmth.stmms.common.interceptor.SessionInterceptor;
 import cn.com.qmth.stmms.common.utils.RequestUtils;
 
 /**
- * SubjectHeader模块操作拦截器
- * 
+ * Mark模块操作拦截器
+ *
  * @author XKJ
- * 
  */
-public class MarkInterceptor extends CommonInterceptor {
+public class MarkInterceptor extends SessionInterceptor {
 
     @Autowired
     private MarkerService markerService;
 
     @Override
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
-        String uri = RequestUtils.getURI(request);
-        Marker marker = buildMarker(RequestUtils.getWebUser(request));
-        if (marker != null) {
-            if (uri.endsWith("mark/reset")) {
-                return true;
-            } else if (marker.getLastLoginTime() == null) {
+        WebUser wu = buildWebUser(request);
+        if (wu != null) {
+            if (!request.getServletPath().endsWith("mark/reset") && wu.getMarker().getLastLoginTime() == null) {
                 // 首次登录,强制重置密码及姓名
                 return redirect(request, response, "/mark/reset");
             }
             return true;
         }
-        if (uri.endsWith("mark/index")) {
-            return redirect(request, response, "/mark-login");
-        }
-        return sessionExpireStatus(request, response, "/mark-login");
+        return sessionExpire(request, response, "/login");
     }
 
-    private Marker buildMarker(WebUser wu) {
-        Marker marker = null;
-        if (wu != null && !wu.isSchoolAdmin() && !wu.isSchoolViewer() && !wu.isSubjectHeader()) {
-            Marker local = markerService.findById(wu.getId());
-            if (!wu.getWebToken().equals(local.getWebToken())) {
-                return null;
-            }
-            marker = wu.getMarker();
-            if (marker == null) {
-                marker = local;
-                if (marker != null) {
-                    wu.setDataObject(marker);
-                    wu.setName(marker.getName());
-                }
+    private WebUser buildWebUser(HttpServletRequest request) {
+        StmmsSession session = RequestUtils.getSession(request);
+        Integer userId = session.getWebUserId();
+        Role role = session.getWebUserRole();
+        if (userId != null && Role.MARKER == role) {
+            Marker marker = markerService.findById(userId);
+            if (marker != null && marker.getAccessTokenRefreshTime() != null && StringUtils
+                    .equals(session.getWebUserToken(), marker.getAccessToken()) && !AccessControlUtils
+                    .expired(marker.getAccessTokenRefreshTime())) {
+                WebUser wu = new WebUser(marker, session.getWebUserLogoutUrl());
+                RequestUtils.setWebUser(request, wu);
+                return wu;
             }
         }
-        return marker;
+        return null;
     }
 }

+ 132 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/open/controller/OpenAccountController.java

@@ -0,0 +1,132 @@
+package cn.com.qmth.stmms.open.controller;
+
+import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
+import cn.com.qmth.stmms.biz.exam.model.Marker;
+import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
+import cn.com.qmth.stmms.biz.exam.service.MarkerService;
+import cn.com.qmth.stmms.biz.school.model.School;
+import cn.com.qmth.stmms.biz.school.service.SchoolService;
+import cn.com.qmth.stmms.biz.user.model.OpenAccount;
+import cn.com.qmth.stmms.biz.user.service.UserService;
+import cn.com.qmth.stmms.common.authorization.AuthorizationService;
+import cn.com.qmth.stmms.common.domain.OpenUser;
+import cn.com.qmth.stmms.common.domain.WebUser;
+import cn.com.qmth.stmms.common.enums.AccountType;
+import cn.com.qmth.stmms.common.enums.MarkStatus;
+import cn.com.qmth.stmms.common.session.model.StmmsSession;
+import cn.com.qmth.stmms.common.signature.SignatureInfo;
+import cn.com.qmth.stmms.common.signature.SignatureType;
+import cn.com.qmth.stmms.common.utils.RequestUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Date;
+import java.util.List;
+
+@Controller("openAccountController")
+@RequestMapping("/open")
+public class OpenAccountController {
+
+    @Autowired
+    private AuthorizationService authorizationService;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private SchoolService schoolService;
+
+    @Autowired
+    private MarkerService markerService;
+
+    @Autowired
+    private MarkGroupService groupService;
+
+    @RequestMapping(value = "/marker/login", method = RequestMethod.POST)
+    public String markerLogin(HttpServletRequest request, RedirectAttributes redirect, @RequestParam String account,
+            @RequestParam String returnUrl) {
+        // 请求参数验证
+        account = StringUtils.trimToEmpty(account);
+        returnUrl = StringUtils.trimToEmpty(returnUrl);
+        if (returnUrl.length() == 0) {
+            return errorRedirect(redirect, null, "returnUrl不能为空");
+        }
+        if (account.length() == 0) {
+            return errorRedirect(redirect, returnUrl, "account不能为空");
+        }
+        // 签名验证,获取访问学校
+        School school = null;
+        SignatureInfo info = authorizationService.buildSignature(request);
+        if (info != null && info.getType() == SignatureType.SECRET) {
+            school = schoolService.findByAccessKey(info.getInvoker());
+        }
+        if (school == null || !school.isEnable() || !info.validate(school.getAccessSecret())) {
+            return errorRedirect(redirect, returnUrl, "接口鉴权失败");
+        }
+        // 查找第三方账号并登陆
+        OpenAccount oa = userService.openAccountLogin(school.getId(), account);
+        if (oa == null || !oa.isEnable()) {
+            return errorRedirect(redirect, returnUrl, "第三方账号不存在");
+        }
+        // 保存第三方账号到session
+        RequestUtils.getSession(request).saveOpenUser(new OpenUser(oa, returnUrl));
+        return "redirect:/open/marker/list";
+    }
+
+    @RequestMapping(value = "/marker/list", method = RequestMethod.GET)
+    public ModelAndView markerList(HttpServletRequest request) {
+        OpenAccount account = RequestUtils.getOpenUser(request).getOpenAccount();
+        ModelAndView modelAndView = new ModelAndView("modules/open/marker-list");
+        // TODO - 增加评卷员列表,构造考试、科目、分组联动数据集合
+        List<Marker> markerList = markerService
+                .findByAccountAndMarkStatus(AccountType.OPEN_ACCOUNT, account.getId(), MarkStatus.TRIAL, MarkStatus.FORMAL);
+        return modelAndView;
+    }
+
+    @RequestMapping(value = "/marker/select", method = RequestMethod.POST)
+    public ModelAndView markerSelect(HttpServletRequest request, @RequestParam Integer markerId) {
+        // 评卷员与评卷分组校验
+        OpenAccount account = RequestUtils.getOpenUser(request).getOpenAccount();
+        Marker marker = markerService.findById(markerId);
+        if (marker == null || !marker.isEnable() || AccountType.OPEN_ACCOUNT != marker.getAccountType() || marker.getAccountId() == null
+                || !marker.getAccountId().equals(account.getId())) {
+            return new ModelAndView("redirect:/open/marker/list");
+        }
+        MarkGroup group = groupService.findOne(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
+        if (group == null || group.getStatus() == MarkStatus.FINISH) {
+            return new ModelAndView("redirect:/open/marker/list");
+        }
+        // 完成评卷员登陆并保存session
+        marker.refreshAccessToken();
+        marker.setLastLoginIp(request.getRemoteAddr());
+        marker.setLastLoginTime(new Date());
+        RequestUtils.getSession(request).saveWebUser(new WebUser(markerService.save(marker), "/open/marker/list"));
+        return new ModelAndView("redirect:/mark/index");
+    }
+
+    @RequestMapping(value = "/error")
+    public ModelAndView error(HttpServletRequest request, @RequestParam(required = false) String message,
+            @RequestParam(required = false) String returnUrl) {
+        ModelAndView modelAndView = new ModelAndView("modules/open/error");
+        modelAndView.addObject("message", StringUtils.trimToNull(message));
+        modelAndView.addObject("returnUrl", StringUtils.trimToNull(returnUrl));
+        return modelAndView;
+    }
+
+    private String errorRedirect(RedirectAttributes redirect, String returnUrl, String... errors) {
+        if (errors != null && errors.length > 0) {
+            redirect.addFlashAttribute("message", StringUtils.join(errors, "\n"));
+        }
+        if (returnUrl != null && returnUrl.length() > 0) {
+            redirect.addFlashAttribute("returnUrl", returnUrl);
+        }
+        return "redirect:/open/error";
+    }
+}

+ 50 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/open/interceptor/OpenInterceptor.java

@@ -0,0 +1,50 @@
+package cn.com.qmth.stmms.open.interceptor;
+
+import cn.com.qmth.stmms.biz.user.model.OpenAccount;
+import cn.com.qmth.stmms.biz.user.service.UserService;
+import cn.com.qmth.stmms.common.domain.OpenUser;
+import cn.com.qmth.stmms.common.interceptor.SessionInterceptor;
+import cn.com.qmth.stmms.common.session.model.StmmsSession;
+import cn.com.qmth.stmms.common.utils.AccessControlUtils;
+import cn.com.qmth.stmms.common.utils.RequestUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Mark模块操作拦截器
+ *
+ * @author XKJ
+ */
+public class OpenInterceptor extends SessionInterceptor {
+
+    @Autowired
+    private UserService userService;
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        OpenUser ou = buildOpenUser(request);
+        if (ou != null) {
+            return true;
+        }
+        return redirect(request, response, "/open/error");
+    }
+
+    private OpenUser buildOpenUser(HttpServletRequest request) {
+        StmmsSession session = RequestUtils.getSession(request);
+        Integer userId = session.getOpenUserId();
+        if (userId != null) {
+            OpenAccount oa = userService.findOpenAccount(userId);
+            if (oa != null && oa.isEnable() && oa.getAccessTokenRefreshTime() != null && StringUtils
+                    .equals(session.getOpenUserToken(), oa.getAccessToken()) && !AccessControlUtils
+                    .expired(oa.getAccessTokenRefreshTime())) {
+                OpenUser ou = new OpenUser(oa, session.getOpenUserReturnUrl());
+                RequestUtils.setOpenUser(request, ou);
+                return ou;
+            }
+        }
+        return null;
+    }
+}

+ 7 - 11
stmms-web/src/main/webapp/WEB-INF/application.properties

@@ -1,14 +1,10 @@
 #jdbc config
-driverClassName=com.mysql.jdbc.Driver
-#jdbc config
-url=jdbc:mysql://localhost:3306/stmms_ft?useUnicode=true&characterEncoding=UTF-8
-username=root
-password=root
-
-#cookie config
-cookie.max.age=36000
-cookie.domain=
-cookie.path=/
+jdbc.driver=com.mysql.jdbc.Driver
+jdbc.url=jdbc:mysql://localhost:3306/stmms_ft?useUnicode=true&characterEncoding=UTF-8
+jdbc.username=root
+jdbc.password=root
+jdbc.maxActive=50
+jdbc.initSize=5
 
 #server config
 slice.image.server=https://ft-slice.markingcloud.com
@@ -43,5 +39,5 @@ mark.cleanLockSchedule=0 0 3 * * ?
 
 marker.showBtnImportAndBtnUpdateImport=false
 marker.forceMode=
-##\u9996\u9875\u53ef\u9009\u7684logo\u6587\u4ef6
+##\u9996\u9875\u53EF\u9009\u7684logo\u6587\u4EF6
 index.logo=

+ 33 - 35
stmms-web/src/main/webapp/WEB-INF/applicationContext.xml

@@ -1,36 +1,34 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<beans xmlns="http://www.springframework.org/schema/beans"
-	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
-	xsi:schemaLocation="
-http://www.springframework.org/schema/beans
-http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
-http://www.springframework.org/schema/context
-http://www.springframework.org/schema/context/spring-context-3.2.xsd
-">
-	<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" >
-		<property name="ignoreResourceNotFound" value="true" />
-		<property name="locations">
-			<list>
-				<value>/WEB-INF/application.properties</value>
-				<value>/WEB-INF/sas.properties</value>
-			</list>
-		</property>
-	</bean>
-
-	<bean id="properties" class="org.springframework.beans.factory.config.PropertiesFactoryBean" >
-		<property name="ignoreResourceNotFound" value="true" />
-		<property name="locations">
-			<list>
-				<value>/WEB-INF/application.properties</value>
-			</list>
-		</property>
-	</bean>
-	
-	<import resource="classpath*:service-context.xml" />
-	<import resource="spring-cache.xml" />
-
-	<bean id="springContextHolder" class="cn.com.qmth.stmms.biz.utils.SpringContextHolder"/>
-	<bean id="sessionService" class="cn.com.qmth.stmms.common.session.service.impl.CookieSessionServiceImpl"/>
-	<!-- 配置 JSR303 Bean Validator 定义 -->
-	<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
+       xsi:schemaLocation="
+http://www.springframework.org/schema/beans
+http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
+http://www.springframework.org/schema/context
+http://www.springframework.org/schema/context/spring-context-3.2.xsd
+">
+    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+        <property name="ignoreResourceNotFound" value="true"/>
+        <property name="locations">
+            <list>
+                <value>/WEB-INF/application.properties</value>
+                <value>/WEB-INF/sas.properties</value>
+            </list>
+        </property>
+    </bean>
+
+    <bean id="properties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
+        <property name="ignoreResourceNotFound" value="true"/>
+        <property name="locations">
+            <list>
+                <value>/WEB-INF/application.properties</value>
+            </list>
+        </property>
+    </bean>
+
+    <import resource="classpath*:service-context.xml"/>
+    <import resource="spring-cache.xml"/>
+
+    <bean id="springContextHolder" class="cn.com.qmth.stmms.biz.utils.SpringContextHolder"/>
+    <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
 </beans>

+ 142 - 142
stmms-web/src/main/webapp/WEB-INF/spring-mvc.xml

@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <beans xmlns="http://www.springframework.org/schema/beans"
-	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
-	   xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context"
-	   xmlns:task="http://www.springframework.org/schema/task"
-	   xsi:schemaLocation="
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
+       xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context"
+       xmlns:task="http://www.springframework.org/schema/task"
+       xsi:schemaLocation="
         http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/context
@@ -12,152 +12,152 @@
         http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
         http://www.springframework.org/schema/task
 		 http://www.springframework.org/schema/task/spring-task-3.2.xsd">
-        
-	<!--  <import resource="applicationContext.xml"/>-->
-	
-	<!-- Handles HTTP GET requests for /static/** by efficiently serving 
-		up static resources in the ${webappRoot}/static/ directory -->
-	<mvc:resources mapping="/resources/**" location="/static/" order="0"/>
-	<mvc:resources mapping="/static/**" location="file:${app.home}/static/" order="0"/>
-	
-	<!-- 自动扫描包下的所有类,使其认为spring mvc的控制器 -->
-	<context:component-scan base-package="cn.com.qmth.stmms.common.controller,
+
+    <bean id="sessionService" class="cn.com.qmth.stmms.common.session.service.impl.CookieSessionServiceImpl"/>
+
+    <!-- Handles HTTP GET requests for /static/** by efficiently serving
+        up static resources in the ${webappRoot}/static/ directory -->
+    <mvc:resources mapping="/resources/**" location="/static/" order="0"/>
+    <mvc:resources mapping="/static/**" location="file:${app.home}/static/" order="0"/>
+
+    <!-- 自动扫描包下的所有类,使其认为spring mvc的控制器 -->
+    <context:component-scan base-package="cn.com.qmth.stmms.common.controller,
 					  cn.com.qmth.stmms.admin,
 					  cn.com.qmth.stmms.api,
 					  cn.com.qmth.stmms.file,
 					  cn.com.qmth.stmms.mark,
 					  cn.com.qmth.stmms.monitor,
-					  cn.com.qmth.stmms.report">
-		<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
-		<context:include-filter type="annotation" expression="org.springframework.stereotype.Component" />
-	</context:component-scan>
+					  cn.com.qmth.stmms.report,
+					  cn.com.qmth.stmms.open">
+        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
+        <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
+    </context:component-scan>
+
+    <mvc:annotation-driven/>
+
+    <mvc:interceptors>
+        <mvc:interceptor>
+            <mvc:mapping path="/admin/**"/>
+            <mvc:mapping path="/mark/**"/>
+            <mvc:mapping path="/open/**"/>
+            <bean id="sessionInterceptor" class="cn.com.qmth.stmms.common.interceptor.SessionInterceptor"/>
+        </mvc:interceptor>
+        <mvc:interceptor>
+            <mvc:mapping path="/admin/**"/>
+            <bean id="adminInterceptor" class="cn.com.qmth.stmms.admin.interceptor.AdminInterceptor"/>
+        </mvc:interceptor>
+        <mvc:interceptor>
+            <mvc:mapping path="/admin/sys/**"/>
+            <bean id="sysAdminInterceptor" class="cn.com.qmth.stmms.admin.interceptor.SysAdminInterceptor"/>
+        </mvc:interceptor>
+        <mvc:interceptor>
+            <mvc:mapping path="/admin/exam/**"/>
+            <bean id="adminExamInterceptor" class="cn.com.qmth.stmms.admin.interceptor.AdminExamInterceptor"/>
+        </mvc:interceptor>
+        <mvc:interceptor>
+            <mvc:mapping path="/mark/**"/>
+            <bean id="markInterceptor" class="cn.com.qmth.stmms.mark.interceptor.MarkInterceptor"/>
+        </mvc:interceptor>
+        <mvc:interceptor>
+            <mvc:mapping path="/open/**"/>
+            <mvc:exclude-mapping path="/open/marker/login"/>
+            <mvc:exclude-mapping path="/open/error"/>
+            <bean id="openInterceptor" class="cn.com.qmth.stmms.open.interceptor.OpenInterceptor"/>
+        </mvc:interceptor>
+        <mvc:interceptor>
+            <mvc:mapping path="/admin/**"/>
+            <bean id="methodInterceptor" class="cn.com.qmth.stmms.common.interceptor.MethodInterceptor"/>
+        </mvc:interceptor>
+        <mvc:interceptor>
+            <mvc:mapping path="/api/**"/>
+            <bean id="apiInterceptor" class="cn.com.qmth.stmms.api.interceptor.ApiInterceptor"/>
+        </mvc:interceptor>
+    </mvc:interceptors>
+
+    <!-- <bean
+        class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
+        <property name="mediaTypes">
+            <map>
+                <entry key="atom" value="application/atom+xml" />
+                <entry key="html" value="text/html" />
+                <entry key="json" value="application/json" />
+            </map>
+        </property>
+        <property name="viewResolvers">
+            <list>
+                <bean class="org.springframework.web.servlet.view.BeanNameViewResolver" />
+                <bean
+                    class="org.springframework.web.servlet.view.InternalResourceViewResolver">
+                    <property name="prefix" value="/WEB-INF/jsp/" />
+                    <property name="suffix" value=".jsp" />
+                    <property name="exposedContextBeanNames">
+                        <list>
+                            <value>properties</value>
+                        </list>
+                    </property>
+                </bean>
+            </list>
+        </property>
+        <property name="defaultViews">
+            <list>
+                <bean
+                    class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
+            </list>
+        </property>
+    </bean> -->
+
+    <!-- 视图文件解析配置 -->
+    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
+        <property name="prefix" value="/WEB-INF/views/"/>
+        <property name="suffix" value=".jsp"/>
+        <property name="exposedContextBeanNames">
+            <list>
+                <value>properties</value>
+            </list>
+        </property>
+    </bean>
+    <!-- Mapping exception to the handler view -->
+    <!-- <bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
+        to /error.jsp -->
+    <!-- <property name="defaultErrorView" value="error"/> <property name="exceptionMappings">
+        <props> </props> </property> </bean> -->
 
-	<mvc:annotation-driven />
- 
-	<mvc:interceptors>
-		<mvc:interceptor>
-			<mvc:mapping path="/**" />
-			<mvc:exclude-mapping path="/resources/**"/>
-			<mvc:exclude-mapping path="/static/**"/>
-			<mvc:exclude-mapping path="/file/**"/>
-			<mvc:exclude-mapping path="/api/**"/>
-			<bean id="commonInterceptor" class="cn.com.qmth.stmms.common.interceptor.CommonInterceptor"></bean>
-		</mvc:interceptor>
-		<mvc:interceptor>
-			<mvc:mapping path="/admin/**" />
-			<bean id="adminInterceptor" class="cn.com.qmth.stmms.admin.interceptor.AdminInterceptor"></bean>
-		</mvc:interceptor>
-		<mvc:interceptor>
-			<mvc:mapping path="/admin/sys/**" />
-			<bean id="sysAdminInterceptor" class="cn.com.qmth.stmms.admin.interceptor.SysAdminInterceptor"></bean>
-		</mvc:interceptor>
-		<mvc:interceptor>
-			<mvc:mapping path="/admin/exam/**" />
-			<bean id="adminExamInterceptor" class="cn.com.qmth.stmms.admin.interceptor.AdminExamInterceptor"></bean>
-		</mvc:interceptor>
-		<mvc:interceptor>
-			<mvc:mapping path="/api/**" />
-			<bean id="apiInterceptor" class="cn.com.qmth.stmms.api.interceptor.ApiInterceptor"></bean>
-		</mvc:interceptor>
-		<mvc:interceptor>
-			<mvc:mapping path="/mark/**" />
-			<bean id="markInterceptor" class="cn.com.qmth.stmms.mark.interceptor.MarkInterceptor"></bean>
-		</mvc:interceptor>
-		<mvc:interceptor>
-			<mvc:mapping path="/**" />
-			<mvc:exclude-mapping path="/resources/**"/>
-			<mvc:exclude-mapping path="/static/**"/>
-			<mvc:exclude-mapping path="/file/**"/>
-			<mvc:exclude-mapping path="/api/**"/>
-			<bean id="methodInterceptor" class="cn.com.qmth.stmms.common.interceptor.MethodInterceptor"></bean>
-		</mvc:interceptor>
-	</mvc:interceptors>
+    <!-- 配置SpringMVC @ResponseBody和@RequestBody注解 -->
+    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
+        <property name="messageConverters">
+            <list>
+                <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
+                <ref bean="jsonHttpMessageConverter"/>
+            </list>
+        </property>
+    </bean>
 
-	<!-- <bean
-		class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
-		<property name="mediaTypes">
-			<map>
-				<entry key="atom" value="application/atom+xml" />
-				<entry key="html" value="text/html" />
-				<entry key="json" value="application/json" />
-			</map>
-		</property>
-		<property name="viewResolvers">
-			<list>
-				<bean class="org.springframework.web.servlet.view.BeanNameViewResolver" />
-				<bean
-					class="org.springframework.web.servlet.view.InternalResourceViewResolver">
-					<property name="prefix" value="/WEB-INF/jsp/" />
-					<property name="suffix" value=".jsp" />
-					<property name="exposedContextBeanNames">
-						<list>
-							<value>properties</value>
-						</list>
-					</property>
-				</bean>
-			</list>
-		</property>
-		<property name="defaultViews">
-			<list>
-				<bean
-					class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
-			</list>
-		</property>
-	</bean> -->
-	
-	<!-- 视图文件解析配置 -->
-	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
-		<property name="prefix" value="/WEB-INF/views/"/>
-		<property name="suffix" value=".jsp"/>
-		<property name="exposedContextBeanNames">
-			<list>
-				<value>properties</value>
-			</list>
-		</property>
-	</bean>
-	<!-- Mapping exception to the handler view -->
-	<!-- <bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> 
-		to /error.jsp -->
-	<!-- <property name="defaultErrorView" value="error"/> <property name="exceptionMappings"> 
-		<props> </props> </property> </bean> -->
+    <bean id="jsonHttpMessageConverter"
+          class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
+        <property name="supportedMediaTypes">
+            <list>
+                <value>application/json;charset=UTF-8</value>
+            </list>
+        </property>
+    </bean>
 
-	<!-- 配置SpringMVC @ResponseBody和@RequestBody注解 -->
-	<bean
-		class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
-		<property name="messageConverters">
-			<list>
-				<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
-				<ref bean="jsonHttpMessageConverter" />
-			</list>
-		</property>
-	</bean>
+    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
 
-	<bean id="jsonHttpMessageConverter"
-		class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
-		<property name="supportedMediaTypes">
-			<list>
-				<value>application/json;charset=UTF-8</value>
-			</list>
-		</property>
-	</bean>
-	
-	<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />
-	
-	<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" >
-		<property name="ignoreResourceNotFound" value="true" />
-		<property name="locations">
-			<list>
-				<value>/WEB-INF/application.properties</value>
-			</list>
-		</property>
-	</bean>
+    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+        <property name="ignoreResourceNotFound" value="true"/>
+        <property name="locations">
+            <list>
+                <value>/WEB-INF/application.properties</value>
+            </list>
+        </property>
+    </bean>
 
-	<bean id="properties" class="org.springframework.beans.factory.config.PropertiesFactoryBean" >
-		<property name="ignoreResourceNotFound" value="true" />
-		<property name="locations">
-			<list>
-				<value>/WEB-INF/application.properties</value>
-			</list>
-		</property>
-	</bean>
+    <bean id="properties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
+        <property name="ignoreResourceNotFound" value="true"/>
+        <property name="locations">
+            <list>
+                <value>/WEB-INF/application.properties</value>
+            </list>
+        </property>
+    </bean>
 </beans> 

+ 59 - 0
stmms-web/src/main/webapp/WEB-INF/views/modules/exam/markerAccount.jsp

@@ -0,0 +1,59 @@
+<%@ page contentType="text/html;charset=UTF-8" %>
+<%@ include file="/WEB-INF/views/include/taglib.jsp" %>
+<html>
+<head>
+    <title>账号绑定</title>
+    <meta name="decorator" content="default"/>
+    <%@include file="/WEB-INF/views/include/head.jsp" %>
+</head>
+<body>
+<ul class="nav nav-tabs">
+    <li class="active"><a href="##">账号绑定</a></li>
+</ul>
+<br/>
+<form id="edit-form" action="${ctx}/admin/exam/marker/account/" method="post" class="form-horizontal">
+    <tags:message content="${message}"/>
+    <input name="markerId" type="hidden" value="${marker.id }"/>
+    <input name="accountType" type="hidden" value="OPEN_ACCOUNT"/>
+    <div class="control-group">
+        <label class="control-label">评卷员</label>
+        <div class="controls">
+            ${marker.loginName }
+        </div>
+    </div>
+    <div class="control-group">
+        <label class="control-label"></label>
+        <div class="controls">
+            <select class="input-large" id="account-select" name="accountId">
+                <c:forEach var="account" items="${openAccountList}">
+                    <option value="${account.id}">${account.account}_${account.name}</option>
+                </c:forEach>
+            </select>
+        </div>
+    </div>
+    <div class="form-actions">
+        <a href="##" id="bind-button" class="btn btn-primary">绑定</a>
+        <c:if test="${marker.accountType != null || marker.accountId != null}">
+            &nbsp;&nbsp;
+            <a href="##" id="unbind-button" class="btn btn-warning">解绑</a>
+        </c:if>
+        &nbsp;&nbsp;
+        <a href="${ctx}/admin/exam/marker?subjectCode=${marker.subjectCode}&groupNumber=${marker.groupNumber}" class="btn">返回</a>
+    </div>
+</form>
+
+<script language="JavaScript">
+    $('#bind-button').click(function () {
+        submit('bind')
+    })
+    $('#unbind-button').click(function () {
+        submit('unbind')
+    })
+
+    function submit(action) {
+        $('#edit-form').attr('action', $('#edit-form').attr('action') + action)
+        $('#edit-form').submit()
+    }
+</script>
+</body>
+</html>

+ 319 - 284
stmms-web/src/main/webapp/WEB-INF/views/modules/exam/markerList.jsp

@@ -1,14 +1,21 @@
 <%@ page contentType="text/html;charset=UTF-8" %>
-<%@ include file="/WEB-INF/views/include/taglib.jsp"%>
+<%@ include file="/WEB-INF/views/include/taglib.jsp" %>
 <html>
 <head>
-	<title>评卷员管理</title>
-	<meta name="decorator" content="default"/>
-	<%@include file="/WEB-INF/views/include/head.jsp" %>
-	<style type="text/css">.sort{color:#0663A2;cursor:pointer;}</style>
-	<style type="text/css">
-        .sort{color:#0663A2;cursor:pointer;}
-        .taskWindow,.reSetPasswordWin{
+    <title>评卷员管理</title>
+    <meta name="decorator" content="default"/>
+    <%@include file="/WEB-INF/views/include/head.jsp" %>
+    <style type="text/css">.sort {
+        color: #0663A2;
+        cursor: pointer;
+    }</style>
+    <style type="text/css">
+        .sort {
+            color: #0663A2;
+            cursor: pointer;
+        }
+
+        .taskWindow, .reSetPasswordWin {
             width: 400px;
             min-height: 150px;
             background: #fff;
@@ -21,13 +28,15 @@
             text-align: center;
             z-index: 99999;
         }
-        .task-header,.password-header{
+
+        .task-header, .password-header {
             width: 100%;
             height: 46px;
             background: #5d6d7d;
             color: #fff;
         }
-        .task-content,.password-content{
+
+        .task-content, .password-content {
             font-size: 18px;
             color: #005277;
             text-align: left;
@@ -35,12 +44,14 @@
             line-height: 30px;
             word-wrap: break-word;
         }
+
         .image-close {
             margin-top: 15px;
             margin-right: 15px;
             cursor: pointer;
             float: right;
         }
+
         .title {
             font-size: 22px;
             font-weight: bold;
@@ -49,25 +60,29 @@
             padding: 13px 0 0 13px;
             float: left;
         }
-        .task-count,.password-value{
+
+        .task-count, .password-value {
             width: 150px;
         }
-        .btn-info{
+
+        .btn-info {
             height: 25px;
             margin-left: 320px;
             margin-bottom: 20px;
         }
-        .wrong,.passwordWrong{
+
+        .wrong, .passwordWrong {
             font-size: 12px;
             color: #f00;
             line-height: 30px;
             padding-left: 20px;
         }
-        #cover{
+
+        #cover {
             position: fixed;
-            z-index:9999;
-            top:0px;
-            left:0px;
+            z-index: 9999;
+            top: 0px;
+            left: 0px;
             display: none;
             width: 100%;
             height: 100%;
@@ -77,23 +92,23 @@
     </style>
 </head>
 <body>
-    <ul class="nav nav-tabs">
-        <li><a href="${ctx}/admin/exam/group?subjectCode=${query.subjectCode}">大题管理</a></li>
-        <li class="active"><a href="##">评卷员管理</a></li>
-        <li><a href="${ctx}/admin/exam/trial?subjectCode=${query.subjectCode}">试评管理</a></li>
-        <li><a href="${ctx}/admin/exam/library?subjectCode=${query.subjectCode}">任务管理</a></li>
-        <li><a href="${ctx}/admin/exam/arbitrate?subjectCode=${query.subjectCode}">仲裁管理</a></li>
-        <li><a href="${ctx}/admin/exam/quality?subjectCode=${query.subjectCode}">质量监控</a></li>
-    </ul>
-    <div id="importBox" class="hide">
-        <form id="importForm" action="${ctx}/admin/exam/marker/import" method="post" enctype="multipart/form-data"
-              style="padding-left:20px;text-align:center;" class="form-search" onsubmit="loading('正在导入,请稍等...');"><br/>
-            <input name="subjectCode" type="hidden" value="${query.subjectCode }"/>
-            <input id="uploadFile" name="file" type="file" style="width:330px"/><br/><br/>  
-            <input id="btnImportSubmit" class="btn btn-primary" type="submit" value="   导    入   "/>
-            <a href="${ctx}/admin/exam/marker/template">下载模板</a>
-        </form>
-    </div>
+<ul class="nav nav-tabs">
+    <li><a href="${ctx}/admin/exam/group?subjectCode=${query.subjectCode}">大题管理</a></li>
+    <li class="active"><a href="##">评卷员管理</a></li>
+    <li><a href="${ctx}/admin/exam/trial?subjectCode=${query.subjectCode}">试评管理</a></li>
+    <li><a href="${ctx}/admin/exam/library?subjectCode=${query.subjectCode}">任务管理</a></li>
+    <li><a href="${ctx}/admin/exam/arbitrate?subjectCode=${query.subjectCode}">仲裁管理</a></li>
+    <li><a href="${ctx}/admin/exam/quality?subjectCode=${query.subjectCode}">质量监控</a></li>
+</ul>
+<div id="importBox" class="hide">
+    <form id="importForm" action="${ctx}/admin/exam/marker/import" method="post" enctype="multipart/form-data"
+          style="padding-left:20px;text-align:center;" class="form-search" onsubmit="loading('正在导入,请稍等...');"><br/>
+        <input name="subjectCode" type="hidden" value="${query.subjectCode }"/>
+        <input id="uploadFile" name="file" type="file" style="width:330px"/><br/><br/>  
+        <input id="btnImportSubmit" class="btn btn-primary" type="submit" value="   导    入   "/>
+        <a href="${ctx}/admin/exam/marker/template">下载模板</a>
+    </form>
+</div>
 
 <%--     <div id="importUpdateBox" class="hide">
         <form id="importUpdateForm" action="${ctx}/admin/exam/marker/importUpdate" method="post" enctype="multipart/form-data"
@@ -103,269 +118,289 @@
             <a href="${ctx}/admin/exam/marker/updateLoginNameTemplate">下载模板</a>
         </form>
     </div> --%>
-    
-	<form id="searchForm"  action="${ctx}/admin/exam/marker" method="post" class="breadcrumb form-search">
-		<input type="hidden" id="pageNumber" name="pageNumber" value="${query.pageNumber}"/>
-		<input type="hidden" id="pageSize" name="pageSize" value="${query.pageSize}"/>
-		<div>
-			<label>科目</label>
-			<select class="input-large" id="subject-select" name="subjectCode">
-				<c:forEach items="${subjectList}" var="subject">
-				<option value="${subject.code}" <c:if test="${subject.code==query.subjectCode}">selected</c:if>>${subject.code}-${subject.name}</option>
-				</c:forEach>
-			</select>
-			<label>大题</label>
-            <select class="input-medium" id="group-select" name="groupNumber">
-            </select>
-			<label>登录名</label>
-			<input type="text" name="loginName" value="${query.loginName}"  maxlength="30" class="input-small"/>
-			&nbsp;
-			<input id="btnSubmit" class="btn btn-primary" type="button" value="查询" onclick="goSearch()"/>
-			<c:if test="${web_user.schoolAdmin==true}">
+
+<form id="searchForm" action="${ctx}/admin/exam/marker" method="post" class="breadcrumb form-search">
+    <input type="hidden" id="pageNumber" name="pageNumber" value="${query.pageNumber}"/>
+    <input type="hidden" id="pageSize" name="pageSize" value="${query.pageSize}"/>
+    <div>
+        <label>科目</label>
+        <select class="input-large" id="subject-select" name="subjectCode">
+            <c:forEach items="${subjectList}" var="subject">
+                <option value="${subject.code}"
+                        <c:if test="${subject.code==query.subjectCode}">selected</c:if>>${subject.code}-${subject.name}</option>
+            </c:forEach>
+        </select>
+        <label>大题</label>
+        <select class="input-medium" id="group-select" name="groupNumber">
+        </select>
+        <label>登录名</label>
+        <input type="text" name="loginName" value="${query.loginName}" maxlength="30" class="input-small"/>
+        &nbsp;
+        <input id="btnSubmit" class="btn btn-primary" type="button" value="查询" onclick="goSearch()"/>
+        <c:if test="${web_user.schoolAdmin==true}">
             &nbsp;
             <a href="${ctx}/admin/exam/marker/batch-create" class="btn btn-success">新增</a>
-            </c:if>
-			&nbsp;
-			<c:if test="${showBtnImport}">
-				&nbsp;<input id="btnImport" class="btn" type="button" value="导入"/>
-<!-- 				&nbsp;<input id="btnUpdateImport" class="btn" type="button" value="批量修改评卷员账号"/> -->
-			</c:if>
-			<input id="btnExport" class="btn" type="button" value="导出"/>
-		</div>
-	</form>
-	<tags:message content="${message}"/>
-	<table id="contentTable" class="table table-striped table-bordered table-condensed">
-		<thead>
-			<tr>
-				<th>科目</th>
-				<th>大题</th>
-				<th>登录名</th>
-				<th>姓名</th>
-				<th>密码</th>
-				<th>状态</th>
-				<th>已评数量</th>
-				<th>正在评卷</th>
-				<th>任务数</th>
-				<th>绑定班级</th>
-				<th>操作</th>
-			</tr>
-		</thead>
-		<tbody>
-		<c:forEach items="${query.result}" var="marker">
-			<tr>
-				<td>
-				<c:if test="${marker.subject!=null}">
-				${marker.subject.code}-${marker.subject.name}
-				</c:if>
-				</td>
-				<td>
+        </c:if>
+        &nbsp;
+        <c:if test="${showBtnImport}">
+            &nbsp;<input id="btnImport" class="btn" type="button" value="导入"/>
+            <!-- &nbsp;<input id="btnUpdateImport" class="btn" type="button" value="批量修改评卷员账号"/> -->
+        </c:if>
+        <input id="btnExport" class="btn" type="button" value="导出"/>
+    </div>
+</form>
+<tags:message content="${message}"/>
+<table id="contentTable" class="table table-striped table-bordered table-condensed">
+    <thead>
+    <tr>
+        <th>科目</th>
+        <th>大题</th>
+        <th>登录名</th>
+        <th>姓名</th>
+        <th>密码</th>
+        <th>状态</th>
+        <th>已评数量</th>
+        <th>正在评卷</th>
+        <th>任务数</th>
+        <th>绑定班级</th>
+        <th>绑定账号</th>
+        <th>操作</th>
+    </tr>
+    </thead>
+    <tbody>
+    <c:forEach items="${query.result}" var="marker">
+        <tr>
+            <td>
+                <c:if test="${marker.subject!=null}">
+                    ${marker.subject.code}-${marker.subject.name}
+                </c:if>
+            </td>
+            <td>
                 <c:if test="${marker.group!=null}">
-                ${marker.group.number}-${marker.group.title}
+                    ${marker.group.number}-${marker.group.title}
                 </c:if>
-                </td>
-				<td>${marker.loginName}</td>
-				<td>${marker.name}</td>
-				<td>${marker.password}</td>
-				<td>${marker.enable eq true ? "启用" : "禁用"}</td>
-				<td>${marker.markedCount}</td>
-				<td>${marker.currentCount}</td>
-				<td>${marker.topCount}</td>
-				<td><a href="${ctx}/admin/exam/marker/class/${marker.id}"><c:if test="${marker.classCount == null}">0</c:if>${marker.classCount}</a></td>
-				<td>
-				    <c:if test="${web_user.schoolAdmin==true}">
-					<a href="javascript:void(0)" class="reset-button" data-id="${marker.id}">重置</a>
-					<c:if test="${marker.enable==true}">
-					<a href="javascript:void(0)" class="toggle-button" data-id="${marker.id}" data-value="false">禁用</a>
-					</c:if>
-					<c:if test="${marker.enable==false}">
-					<a href="javascript:void(0)" class="toggle-button" data-id="${marker.id}" data-value="true">启用</a>
-					</c:if>
-					<a href="javascript:void(0)" class="release-button" data-id="${marker.id}">回收</a>
-					<a href="javascript:void(0)" class="task-button" data-id="${marker.id}" onclick="showTaskWin('${marker.id}');">设置评卷数</a>
-                    <a href="javascript:void(0)" class="reSetPassword-button" data-id="${marker.id}" onclick="reSetPassword('${marker.id}');">重置密码</a>
-					</c:if>
-				</td>
-			</tr>
-		</c:forEach>
-		</tbody>
-	</table>
-	    <div class="taskWindow" style="display:none"><div class="task-header">
-        <p class="title">设置任务数</p><p class="image-close"><img src="${ctxStatic}/mark-new/images/images-close.png" /></p></div>
-        <div class="task-content"><input type="text" class="task-count" placeholder="请输入任务数"/><i class="wrong"></i></div>
-        <a href="#" class="btn btn-small btn-info task-btn">确定</a>
-    </div>
+            </td>
+            <td>${marker.loginName}</td>
+            <td>${marker.name}</td>
+            <td>${marker.password}</td>
+            <td>${marker.enable eq true ? "启用" : "禁用"}</td>
+            <td>${marker.markedCount}</td>
+            <td>${marker.currentCount}</td>
+            <td>${marker.topCount}</td>
+            <td><a href="${ctx}/admin/exam/marker/class/${marker.id}"><c:if test="${marker.classCount == null}">0</c:if>
+                    ${marker.classCount}</a></td>
+            <td>
+                <a href="${ctx}/admin/exam/marker/account/${marker.id}">
+                    <c:if test="${marker.accountType == null || marker.accountId == null}">#</c:if>
+                    <c:if test="${marker.openAccount != null}">${marker.openAccount.name}</c:if>
+                </a>
+            </td>
+            <td>
+                <c:if test="${web_user.schoolAdmin==true}">
+                    <a href="javascript:void(0)" class="reset-button" data-id="${marker.id}">重置</a>
+                    <c:if test="${marker.enable==true}">
+                        <a href="javascript:void(0)" class="toggle-button" data-id="${marker.id}" data-value="false">禁用</a>
+                    </c:if>
+                    <c:if test="${marker.enable==false}">
+                        <a href="javascript:void(0)" class="toggle-button" data-id="${marker.id}" data-value="true">启用</a>
+                    </c:if>
+                    <a href="javascript:void(0)" class="release-button" data-id="${marker.id}">回收</a>
+                    <a href="javascript:void(0)" class="task-button" data-id="${marker.id}" onclick="showTaskWin('${marker.id}');">设置评卷数</a>
+                    <a href="javascript:void(0)" class="reSetPassword-button" data-id="${marker.id}"
+                       onclick="reSetPassword('${marker.id}');">重置密码</a>
+                </c:if>
+            </td>
+        </tr>
+    </c:forEach>
+    </tbody>
+</table>
+<div class="taskWindow" style="display:none">
+    <div class="task-header">
+        <p class="title">设置任务数</p>
+        <p class="image-close"><img src="${ctxStatic}/mark-new/images/images-close.png"/></p></div>
+    <div class="task-content"><input type="text" class="task-count" placeholder="请输入任务数"/><i class="wrong"></i></div>
+    <a href="#" class="btn btn-small btn-info task-btn">确定</a>
+</div>
 
-    <div class="reSetPasswordWin" style="display:none"><div class="password-header">
-        <p class="title">重置密码</p><p class="image-close password-close"><img src="${ctxStatic}/mark-new/images/images-close.png" /></p></div>
-        <div class="password-content"><input type="text" class="password-value" placeholder="请输入新的密码"/><i class="passwordWrong"></i></div>
-        <a href="#" class="btn btn-small btn-info password-btn">确定</a>
-    </div>
-	<div class="pagination">${query}</div>
+<div class="reSetPasswordWin" style="display:none">
+    <div class="password-header">
+        <p class="title">重置密码</p>
+        <p class="image-close password-close"><img src="${ctxStatic}/mark-new/images/images-close.png"/></p></div>
+    <div class="password-content"><input type="text" class="password-value" placeholder="请输入新的密码"/><i class="passwordWrong"></i></div>
+    <a href="#" class="btn btn-small btn-info password-btn">确定</a>
+</div>
+<div class="pagination">${query}</div>
 <script type="text/javascript">
-var searchSubjectCode = '${query.subjectCode}';
-var searchGroupNumber = '${query.groupNumber}';
-var markerId ;
-$(document).ready(function () {
-  $('#cover').hide();
-});
-$("#btnImport").click(function(){
-$.jBox($("#importBox").html(), {title:"导入数据", buttons:{"关闭":true},
-bottomText:"导入文件不能超过5M,仅允许导入“xls”或“xlsx”格式文件!"});
-});
-/* $("#btnUpdateImport").click(function(){
-$.jBox($("#importUpdateBox").html(), {title:"导入数据", buttons:{"关闭":true},
-bottomText:"导入文件不能超过5M,仅允许导入“xls”或“xlsx”格式文件!"});
-}); */
+    var searchSubjectCode = '${query.subjectCode}';
+    var searchGroupNumber = '${query.groupNumber}';
+    var markerId;
+    $(document).ready(function () {
+        $('#cover').hide();
+    });
+    $("#btnImport").click(function () {
+        $.jBox($("#importBox").html(), {
+            title: "导入数据", buttons: {"关闭": true},
+            bottomText: "导入文件不能超过5M,仅允许导入“xls”或“xlsx”格式文件!"
+        });
+    });
+    /* $("#btnUpdateImport").click(function(){
+    $.jBox($("#importUpdateBox").html(), {title:"导入数据", buttons:{"关闭":true},
+    bottomText:"导入文件不能超过5M,仅允许导入“xls”或“xlsx”格式文件!"});
+    }); */
 
-$("#btnExport").click(function(){
-	top.$.jBox.confirm("确认要导出评卷员数据吗?","系统提示",function(v,h,f){
-		if(v=="ok"){
-			$("#searchForm").attr("action","/admin/exam/marker/export");
-			$("#searchForm").submit();
-			$("#searchForm").attr("action","${ctx}/admin/exam/marker");
-		}
-	},{buttonsFocus:1});
-	top.$('.jbox-body .jbox-icon').css('top','55px');
-});
-$('.reset-button').click(function(){
-	if(!confirm("确定要重置改评卷员吗?")){
-	    return;
-	}
-	var id = $(this).attr('data-id');
-	$.post('${ctx}/admin/exam/marker/reset', {id: id}, function(result){
-		if(result.success==true){
-			alert('重置成功');
-			$("#searchForm").submit();
-		}else{
-			alert(result.message);
-		}
-	});
-});
-$('.toggle-button').click(function(){
-	var id = $(this).attr('data-id');
-	var enable = $(this).attr('data-value');
-	$.post('${ctx}/admin/exam/marker/toggle', {id: id, enable: enable}, function(result){
-		if(result.success==true){
-			alert('修改成功');
-			$("#searchForm").submit();
-		}else{
-			alert(result.message);
-		}
-	});
-});
-$('.release-button').click(function(){
-    var id = $(this).attr('data-id');
-    $.post('${ctx}/admin/exam/marker/release', {id: id}, function(result){
-        if(result.success==true){
-            alert('回收成功');
-            $("#searchForm").submit();
-        }else{
-            alert(result.message);
+    $("#btnExport").click(function () {
+        top.$.jBox.confirm("确认要导出评卷员数据吗?", "系统提示", function (v, h, f) {
+            if (v == "ok") {
+                $("#searchForm").attr("action", "/admin/exam/marker/export");
+                $("#searchForm").submit();
+                $("#searchForm").attr("action", "${ctx}/admin/exam/marker");
+            }
+        }, {buttonsFocus: 1});
+        top.$('.jbox-body .jbox-icon').css('top', '55px');
+    });
+    $('.reset-button').click(function () {
+        if (!confirm("确定要重置改评卷员吗?")) {
+            return;
         }
+        var id = $(this).attr('data-id');
+        $.post('${ctx}/admin/exam/marker/reset', {id: id}, function (result) {
+            if (result.success == true) {
+                alert('重置成功');
+                $("#searchForm").submit();
+            } else {
+                alert(result.message);
+            }
+        });
     });
-});
-$('#subject-select').change(function(){
-    var code = $(this).val();
-    $('#group-select').empty();
-    $('#group-select').append('<option value="">请选择</option>');
-    if(code==''){
-        $('#group-select').val('0').trigger('change');
-        return;
-    }
-    $.post('${ctx}/admin/exam/group/query', {subjectCode: code}, function(result){
-        var parent = $('#group-select');
-        for(var i=0;i<result.length;i++){
-            var group = result[i];
-            var dom = $('<option value="'+group.number+'">'+group.number+'-'+group.title+'</option>').appendTo(parent);
-            if(searchSubjectCode==code && searchGroupNumber==group.number){
-                dom.attr('selected', 'selected');
+    $('.toggle-button').click(function () {
+        var id = $(this).attr('data-id');
+        var enable = $(this).attr('data-value');
+        $.post('${ctx}/admin/exam/marker/toggle', {id: id, enable: enable}, function (result) {
+            if (result.success == true) {
+                alert('修改成功');
+                $("#searchForm").submit();
+            } else {
+                alert(result.message);
+            }
+        });
+    });
+    $('.release-button').click(function () {
+        var id = $(this).attr('data-id');
+        $.post('${ctx}/admin/exam/marker/release', {id: id}, function (result) {
+            if (result.success == true) {
+                alert('回收成功');
+                $("#searchForm").submit();
+            } else {
+                alert(result.message);
             }
+        });
+    });
+    $('#subject-select').change(function () {
+        var code = $(this).val();
+        $('#group-select').empty();
+        $('#group-select').append('<option value="">请选择</option>');
+        if (code == '') {
+            $('#group-select').val('0').trigger('change');
+            return;
         }
-        parent.trigger('change');
+        $.post('${ctx}/admin/exam/group/query', {subjectCode: code}, function (result) {
+            var parent = $('#group-select');
+            for (var i = 0; i < result.length; i++) {
+                var group = result[i];
+                var dom = $('<option value="' + group.number + '">' + group.number + '-' + group.title + '</option>').appendTo(parent);
+                if (searchSubjectCode == code && searchGroupNumber == group.number) {
+                    dom.attr('selected', 'selected');
+                }
+            }
+            parent.trigger('change');
+        });
+    });
+    $('#subject-select').trigger('change');
+
+    function page(n, s) {
+        $("#pageNumber").val(n);
+        $("#pageSize").val(s);
+        $("#searchForm").attr("action", "${ctx}/admin/exam/marker");
+        $("#searchForm").submit();
+        return false;
+    }
+
+    function goSearch() {
+        $("#pageNumber").val(1);
+        $("#pageSize").val('${query.pageSize}');
+        $("#searchForm").attr("action", "${ctx}/admin/exam/marker");
+        $("#searchForm").submit();
+        return false;
+    }
+
+    function showTaskWin(obj) {
+        $('.wrong').html('');
+        $('.task-count').val('');
+        $('.taskWindow').show();
+        $('#cover').show();
+        markerId = obj;
+    }
+
+    function reSetPassword(obj) {
+        $('.passwordWrong').html('');
+        $('.password-value').val('');
+        $('.reSetPasswordWin').show();
+        $('#cover').show();
+        markerId = obj;
+    }
+
+    $('.image-close').click(function () {
+        $('.taskWindow').hide();
+        $('#cover').hide();
+    });
+    $('.password-close').click(function () {
+        $('.reSetPasswordWin').hide();
+        $('#cover').hide();
     });
-});
-$('#subject-select').trigger('change');
 
-function page(n,s){
-	$("#pageNumber").val(n);
-	$("#pageSize").val(s);
-	$("#searchForm").attr("action","${ctx}/admin/exam/marker");
-	$("#searchForm").submit();
-	return false;
-}
-function goSearch(){
-	$("#pageNumber").val(1);
-	$("#pageSize").val('${query.pageSize}');
-	$("#searchForm").attr("action","${ctx}/admin/exam/marker");
-	$("#searchForm").submit();
-	return false;
-}
-function showTaskWin(obj){
-	  $('.wrong').html('');
-	  $('.task-count').val('');
-	  $('.taskWindow').show();
-	  $('#cover').show();
-	  markerId = obj;
-	}
-	function reSetPassword(obj){
-	  $('.passwordWrong').html('');
-	  $('.password-value').val('');
-	  $('.reSetPasswordWin').show();
-	  $('#cover').show();
-	  markerId = obj;
-	}
-	$('.image-close').click(function () {
-	  $('.taskWindow').hide();
-	  $('#cover').hide();
-	});
-	$('.password-close').click(function () {
-	  $('.reSetPasswordWin').hide();
-	  $('#cover').hide();
-	});
+    $('.task-btn').click(function () {
+        var taskCount = $('.task-count').val();
+        var wrongMessage = $('.wrong');
+        taskCount = taskCount.replace(/(^\s*)|(\s*$)/g, "");
+        if (taskCount.length == 0) {
+            wrongMessage.html('任务数不能为空!');
+            return false;
+        } else {
+            if (!/^\+?[1-9][0-9]*$/.test(taskCount)) {
+                wrongMessage.html('请输入正整数!');
+                return false;
+            }
+        }
+        $.post('${ctx}/admin/exam/marker/setTaskCount', {id: markerId, taskCount: taskCount}, function (result) {
+            if (result.success == true) {
+                $("#searchForm").submit();
+            } else {
+                alert(result.message);
+            }
+            $('.taskWindow').hide();
+        });
+    });
+    $('.password-btn').click(function () {
+        var password = $('.password-value').val();
+        var wrongMessage = $('.passwordWrong');
+        password = password.replace(/(^\s*)|(\s*$)/g, "");
+        if (password.length == 0) {
+            wrongMessage.html('密码不能为空!');
+            return false;
+        } else if (password.length < 4) {
+            wrongMessage.html('密码至少4位!');
+            return false;
+        }
+        $.post('${ctx}/admin/exam/marker/reSetPassword', {id: markerId, password: password}, function (result) {
+            if (result.success == true) {
+                $("#searchForm").submit();
+            } else {
+                alert(result.message);
+            }
+            $('.reSetPasswordWin').hide();
+        });
+    });
 
-	$('.task-btn').click(function () {
-	  var taskCount = $('.task-count').val();
-	  var wrongMessage = $('.wrong');
-	       taskCount = taskCount.replace(/(^\s*)|(\s*$)/g, "");
-	       if(taskCount.length == 0){
-	         wrongMessage.html('任务数不能为空!');
-	         return false;
-	       }else {
-	         if(!/^\+?[1-9][0-9]*$/.test(taskCount)){
-	           wrongMessage.html('请输入正整数!');
-	           return false;
-	         }
-	       }
-	  $.post('${ctx}/admin/exam/marker/setTaskCount', {id: markerId,taskCount:taskCount}, function(result){
-	    if(result.success==true){
-	      $("#searchForm").submit();
-	    }else{
-	      alert(result.message);
-	    }
-	    $('.taskWindow').hide();
-	  });
-	});
-	    $('.password-btn').click(function () {
-	      var password = $('.password-value').val();
-	      var wrongMessage = $('.passwordWrong');
-	      password = password.replace(/(^\s*)|(\s*$)/g, "");
-	      if(password.length == 0){
-	        wrongMessage.html('密码不能为空!');
-	        return false;
-	      }else if(password.length < 4){
-	        wrongMessage.html('密码至少4位!');
-	        return false;
-	      }
-	      $.post('${ctx}/admin/exam/marker/reSetPassword', {id: markerId,password:password}, function(result){
-	        if(result.success==true){
-	          $("#searchForm").submit();
-	        }else{
-	          alert(result.message);
-	        }
-	        $('.reSetPasswordWin').hide();
-	      });
-	    });
-	    
-</script>	
+</script>
 </body>
 </html>

+ 76 - 64
stmms-web/src/main/webapp/WEB-INF/views/modules/sys/schoolEdit.jsp

@@ -1,71 +1,83 @@
 <%@ page contentType="text/html;charset=UTF-8" %>
-<%@ include file="/WEB-INF/views/include/taglib.jsp"%>
+<%@ include file="/WEB-INF/views/include/taglib.jsp" %>
 <html>
 <head>
-	<title>新建学校</title>
-	<meta name="decorator" content="default"/>
-	<%@include file="/WEB-INF/views/include/head.jsp" %>
-	<script type="text/javascript">
-		$(document).ready(function() {
-			$("#name").focus();
-			$("#inputForm").validate({
-				submitHandler: function(form){
-					loading('正在提交,请稍等...');
-					form.submit();
-				},
-				errorContainer: "#messageBox",
-				errorPlacement: function(error, element) {
-					$("#messageBox").text("输入有误,请先更正。");
-					if (element.is(":checkbox")||element.is(":radio")||element.parent().is(".input-append")){
-						error.appendTo(element.parent().parent());
-					} else {
-						error.insertAfter(element);
-					}
-				}
-			});
-		});
-	</script>
+    <title>新建学校</title>
+    <meta name="decorator" content="default"/>
+    <%@include file="/WEB-INF/views/include/head.jsp" %>
+    <script type="text/javascript">
+        $(document).ready(function () {
+            $("#name").focus();
+            $("#inputForm").validate({
+                submitHandler: function (form) {
+                    loading('正在提交,请稍等...');
+                    form.submit();
+                },
+                errorContainer: "#messageBox",
+                errorPlacement: function (error, element) {
+                    $("#messageBox").text("输入有误,请先更正。");
+                    if (element.is(":checkbox") || element.is(":radio") || element.parent().is(".input-append")) {
+                        error.appendTo(element.parent().parent());
+                    } else {
+                        error.insertAfter(element);
+                    }
+                }
+            });
+        });
+    </script>
 </head>
 <body>
-	<form:form id="inputForm" modelAttribute="school" action="${ctx}/admin/sys/school/save" method="post" class="form-horizontal">
-		<form:hidden path="id"/>
-		<tags:message content="${message}"/>
-		<div class="control-group">
-			<label class="control-label">名称</label>
-			<div class="controls">
-				<form:input path="name" htmlEscape="false" maxlength="200" class="required"/>
-			</div>
-		</div>
-		<div class="control-group">
-			<label class="control-label">省份</label>
-			<div class="controls">
-				<form:input path="province" htmlEscape="false" maxlength="200" class="required"/>
-			</div>
-		</div>
-		<div class="control-group">
-			<label class="control-label">地市</label>
-			<div class="controls">
-				<form:input path="city" htmlEscape="false" maxlength="200" class="required"/>
-			</div>
-		</div>
-		<div class="control-group">
-			<label class="control-label">地址</label>
-			<div class="controls">
-				<form:input path="address" htmlEscape="false" maxlength="400"/>
-			</div>
-		</div>
-		<div class="control-group">
-			<label class="control-label">描述</label>
-			<div class="controls">
-				<form:textarea path="description" htmlEscape="false" rows="4" maxlength="200" class="input-xxlarge"/>
-			</div>
-		</div>
-		<div class="form-actions">
-			<%-- <shiro:hasPermission name="exam:course:edit"> --%>
-			<input id="btnSubmit" class="btn btn-primary" type="submit" value="保 存"/>&nbsp;
-			<%-- </shiro:hasPermission> --%>
-			<input id="btnCancel" class="btn" type="button" value="返 回" onclick="history.go(-1)"/>
-		</div>
-	</form:form>
+<form:form id="inputForm" modelAttribute="school" action="${ctx}/admin/sys/school/save" method="post" class="form-horizontal">
+    <form:hidden path="id"/>
+    <tags:message content="${message}"/>
+    <div class="control-group">
+        <label class="control-label">名称</label>
+        <div class="controls">
+            <form:input path="name" htmlEscape="false" maxlength="200" class="required"/>
+        </div>
+    </div>
+    <div class="control-group">
+        <label class="control-label">省份</label>
+        <div class="controls">
+            <form:input path="province" htmlEscape="false" maxlength="200" class="required"/>
+        </div>
+    </div>
+    <div class="control-group">
+        <label class="control-label">地市</label>
+        <div class="controls">
+            <form:input path="city" htmlEscape="false" maxlength="200" class="required"/>
+        </div>
+    </div>
+    <div class="control-group">
+        <label class="control-label">地址</label>
+        <div class="controls">
+            <form:input path="address" htmlEscape="false" maxlength="400"/>
+        </div>
+    </div>
+    <div class="control-group">
+        <label class="control-label">AccessKey</label>
+        <div class="controls">
+            <form:label path="accessKey"/>
+        </div>
+    </div>
+    <div class="control-group">
+        <label class="control-label">AccessSecret</label>
+        <div class="controls">
+            <form:label path="accessSecret"/>
+        </div>
+    </div>
+    <div class="control-group">
+        <label class="control-label">描述</label>
+        <div class="controls">
+            <form:textarea path="description" htmlEscape="false" rows="4" maxlength="200" class="input-xxlarge"/>
+        </div>
+    </div>
+    <div class="form-actions">
+            <%-- <shiro:hasPermission name="exam:course:edit"> --%>
+        <input id="btnSubmit" class="btn btn-primary" type="submit" value="保 存"/>&nbsp;
+            <%-- </shiro:hasPermission> --%>
+        <input id="btnCancel" class="btn" type="button" value="返 回" onclick="history.go(-1)"/>
+    </div>
+</form:form>
 </body>
 </html>

+ 67 - 79
stmms-web/src/main/webapp/WEB-INF/web.xml

@@ -1,88 +1,76 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	xmlns="http://java.sun.com/xml/ns/javaee"
-	xmlns:jsp="http://java.sun.com/xml/ns/javaee/jsp"
-	xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
-	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
-	id="WebApp_ID" version="2.5">
-	<display-name>stmms-web</display-name>
-	<session-config>
-		<session-timeout>300</session-timeout>
-	</session-config>
+         xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:jsp="http://java.sun.com/xml/ns/javaee/jsp"
+         xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+         id="WebApp_ID" version="2.5">
+    <display-name>stmms-web</display-name>
+    <session-config>
+        <session-timeout>300</session-timeout>
+    </session-config>
 
-	<listener>
-		<description>spring监听器</description>
-		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
-	</listener>
-	<listener>
-		<description>Introspector缓存清除监听器</description>
-		<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
-	</listener>
+    <listener>
+        <description>spring监听器</description>
+        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+    </listener>
+    <listener>
+        <description>Introspector缓存清除监听器</description>
+        <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
+    </listener>
 
-	<filter>
-		<description>字符集过滤器</description>
-		<filter-name>encodingFilter</filter-name>
-		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
-		<init-param>
-			<description>字符集编码</description>
-			<param-name>encoding</param-name>
-			<param-value>UTF-8</param-value>
-		</init-param>
-	</filter>
-	<filter>
-		<filter-name>sessionCookieFilter</filter-name>
-		<filter-class>cn.com.qmth.stmms.common.filter.SessionFilter</filter-class>
-		<init-param>
-			<param-name>excludeURI</param-name>
-			<param-value>/api,/static,/file</param-value>
-		</init-param>
-	</filter>
-	<filter>
-		<filter-name>characterEncodingFilter</filter-name>
-		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
-		<init-param>
-			<param-name>encoding</param-name>
-			<param-value>utf-8</param-value>
-		</init-param>
-	</filter>
-	<filter>
-		<filter-name>sitemeshFilter</filter-name>
-		<filter-class>com.opensymphony.sitemesh.webapp.SiteMeshFilter</filter-class>
-	</filter>
+    <filter>
+        <description>字符集过滤器</description>
+        <filter-name>encodingFilter</filter-name>
+        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
+        <init-param>
+            <description>字符集编码</description>
+            <param-name>encoding</param-name>
+            <param-value>UTF-8</param-value>
+        </init-param>
+    </filter>
+    <filter>
+        <filter-name>characterEncodingFilter</filter-name>
+        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
+        <init-param>
+            <param-name>encoding</param-name>
+            <param-value>utf-8</param-value>
+        </init-param>
+    </filter>
+    <filter>
+        <filter-name>sitemeshFilter</filter-name>
+        <filter-class>com.opensymphony.sitemesh.webapp.SiteMeshFilter</filter-class>
+    </filter>
 
 
-	<filter-mapping>
-		<filter-name>characterEncodingFilter</filter-name>
-		<servlet-name>stmms-web</servlet-name>
-	</filter-mapping>
-	<filter-mapping>
-		<filter-name>encodingFilter</filter-name>
-		<servlet-name>stmms-web</servlet-name>
-	</filter-mapping>
-	<filter-mapping>
-		<filter-name>sessionCookieFilter</filter-name>
-		<servlet-name>stmms-web</servlet-name>
-	</filter-mapping>
-	<filter-mapping>
-		<filter-name>sitemeshFilter</filter-name>
-		<servlet-name>stmms-web</servlet-name>
-	</filter-mapping>
+    <filter-mapping>
+        <filter-name>characterEncodingFilter</filter-name>
+        <servlet-name>stmms-web</servlet-name>
+    </filter-mapping>
+    <filter-mapping>
+        <filter-name>encodingFilter</filter-name>
+        <servlet-name>stmms-web</servlet-name>
+    </filter-mapping>
+    <filter-mapping>
+        <filter-name>sitemeshFilter</filter-name>
+        <servlet-name>stmms-web</servlet-name>
+    </filter-mapping>
 
-	<servlet>
-		<servlet-name>stmms-web</servlet-name>
-		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
-		<init-param>
-			<param-name>contextConfigLocation</param-name>
-			<param-value>/WEB-INF/spring-mvc.xml</param-value>
-		</init-param>
-		<load-on-startup>1</load-on-startup>
-	</servlet>
-	<servlet-mapping>
-		<servlet-name>stmms-web</servlet-name>
-		<url-pattern>/</url-pattern>
-	</servlet-mapping>
+    <servlet>
+        <servlet-name>stmms-web</servlet-name>
+        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
+        <init-param>
+            <param-name>contextConfigLocation</param-name>
+            <param-value>/WEB-INF/spring-mvc.xml</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>stmms-web</servlet-name>
+        <url-pattern>/</url-pattern>
+    </servlet-mapping>
 
-	<welcome-file-list>
-		<welcome-file></welcome-file>
-	</welcome-file-list>
+    <welcome-file-list>
+        <welcome-file></welcome-file>
+    </welcome-file-list>
 </web-app>

+ 667 - 573
stmms-web/src/main/webapp/sql/stmms_ft.sql

@@ -9,19 +9,21 @@
 
 DROP TABLE IF EXISTS `b_campus`;
 
-CREATE TABLE `b_campus` (
-  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
-  `school_id` int(11) NOT NULL COMMENT '学校ID',
-  `name` varchar(64) NOT NULL COMMENT '名称',
-  `province` varchar(16) DEFAULT NULL COMMENT '省份',
-  `city` varchar(16) DEFAULT NULL COMMENT '城市',
-  `district` varchar(16) DEFAULT NULL COMMENT '地区',
-  `address` varchar(128) DEFAULT NULL COMMENT '地址',
-  `phone` varchar(32) DEFAULT NULL COMMENT '电话',
-  `description` varchar(256) DEFAULT NULL COMMENT '学校描述',
-  PRIMARY KEY (`id`),
-  KEY `index1` (`school_id`,`name`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='学习中心表';
+CREATE TABLE `b_campus`
+(
+    `id`          int(11)     NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `school_id`   int(11)     NOT NULL COMMENT '学校ID',
+    `name`        varchar(64) NOT NULL COMMENT '名称',
+    `province`    varchar(16)  DEFAULT NULL COMMENT '省份',
+    `city`        varchar(16)  DEFAULT NULL COMMENT '城市',
+    `district`    varchar(16)  DEFAULT NULL COMMENT '地区',
+    `address`     varchar(128) DEFAULT NULL COMMENT '地址',
+    `phone`       varchar(32)  DEFAULT NULL COMMENT '电话',
+    `description` varchar(256) DEFAULT NULL COMMENT '学校描述',
+    PRIMARY KEY (`id`),
+    KEY `index1` (`school_id`, `name`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='学习中心表';
 
 
 
@@ -30,21 +32,26 @@ CREATE TABLE `b_campus` (
 
 DROP TABLE IF EXISTS `b_school`;
 
-CREATE TABLE `b_school` (
-  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
-  `name` varchar(64) NOT NULL COMMENT '名称',
-  `province` varchar(16) NOT NULL COMMENT '省份',
-  `city` varchar(16) NOT NULL COMMENT '城市',
-  `address` varchar(128) DEFAULT NULL COMMENT '地址',
-  `phone` varchar(32) DEFAULT NULL COMMENT '电话',
-  `logo_url` varchar(64) DEFAULT NULL COMMENT '图片地址',
-  `description` varchar(256) DEFAULT NULL COMMENT '描述',
-  `enable` tinyint(1) NOT NULL COMMENT '是否禁用',
-  `time_create` datetime NOT NULL COMMENT '创建时间',
-  `time_modified` datetime DEFAULT NULL COMMENT '修改时间',
-  PRIMARY KEY (`id`),
-  KEY `area` (`province`,`city`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='学校表';
+CREATE TABLE `b_school`
+(
+    `id`            int(11)     NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `name`          varchar(64) NOT NULL COMMENT '名称',
+    `province`      varchar(16) NOT NULL COMMENT '省份',
+    `city`          varchar(16) NOT NULL COMMENT '城市',
+    `address`       varchar(128) DEFAULT NULL COMMENT '地址',
+    `phone`         varchar(32)  DEFAULT NULL COMMENT '电话',
+    `logo_url`      varchar(64)  DEFAULT NULL COMMENT '图片地址',
+    `description`   varchar(256) DEFAULT NULL COMMENT '描述',
+    `enable`        tinyint(1)  NOT NULL COMMENT '是否禁用',
+    `access_key`    varchar(64)  DEFAULT NULL COMMENT 'AccessKey',
+    `access_secret` varchar(64)  DEFAULT NULL COMMENT 'AccessSecret',
+    `time_create`   datetime    NOT NULL COMMENT '创建时间',
+    `time_modified` datetime     DEFAULT NULL COMMENT '修改时间',
+    PRIMARY KEY (`id`),
+    UNIQUE KEY `index1` (`access_key`),
+    KEY `area` (`province`, `city`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='学校表';
 
 
 # Dump of table b_user
@@ -52,47 +59,73 @@ CREATE TABLE `b_school` (
 
 DROP TABLE IF EXISTS `b_user`;
 
-CREATE TABLE `b_user` (
-  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
-  `login_name` varchar(64) NOT NULL COMMENT '登录名',
-  `name` varchar(64) NOT NULL COMMENT '名称',
-  `password` varchar(128) NOT NULL COMMENT '密码',
-  `role` varchar(16) NOT NULL COMMENT '角色',
-  `enable` tinyint(1) NOT NULL COMMENT '是否启用',
-  `school_id` int(11) NOT NULL COMMENT '所属学校ID',
-  `subject_code` varchar(32) DEFAULT NULL COMMENT '绑定科目代码',
-  `last_login_time` datetime DEFAULT NULL COMMENT '最后一次登录时间',
-  `last_login_ip` varchar(64) DEFAULT NULL COMMENT '最后一次登录IP',
-  `created_time` datetime NOT NULL COMMENT '创建时间',
-  `updated_time` datetime DEFAULT NULL COMMENT '修改时间',
-  `web_token` varchar(128) DEFAULT NULL COMMENT '登录密钥',
-  PRIMARY KEY (`id`),
-  KEY `index1` (`login_name`),
-  KEY `index2` (`school_id`,`enable`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表';
+CREATE TABLE `b_user`
+(
+    `id`                        int(11)      NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `login_name`                varchar(64)  NOT NULL COMMENT '登录名',
+    `name`                      varchar(64)  NOT NULL COMMENT '名称',
+    `password`                  varchar(128) NOT NULL COMMENT '密码',
+    `role`                      varchar(16)  NOT NULL COMMENT '角色',
+    `enable`                    tinyint(1)   NOT NULL COMMENT '是否启用',
+    `school_id`                 int(11)      NOT NULL COMMENT '所属学校ID',
+    `subject_code`              varchar(32) DEFAULT NULL COMMENT '绑定科目代码',
+    `last_login_time`           datetime    DEFAULT NULL COMMENT '最后一次登录时间',
+    `last_login_ip`             varchar(64) DEFAULT NULL COMMENT '最后一次登录IP',
+    `created_time`              datetime     NOT NULL COMMENT '创建时间',
+    `updated_time`              datetime    DEFAULT NULL COMMENT '修改时间',
+    `access_token`              varchar(64) DEFAULT NULL COMMENT '访问令牌',
+    `access_token_refresh_time` datetime    DEFAULT NULL COMMENT '访问令牌刷新时间',
+    PRIMARY KEY (`id`),
+    KEY `index1` (`login_name`),
+    KEY `index2` (`school_id`, `enable`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='用户表';
 
 LOCK TABLES `b_user` WRITE;
 
-INSERT INTO `b_user` (`id`, `login_name`, `name`, `password`, `role`, `enable`, `school_id`, `subject_code`, `last_login_time`, `last_login_ip`, `created_time`, `updated_time`)
-VALUES
-	(1,'admin','超级管理员','14e1b600b1fd579f47433b88e8d85291','SYS_ADMIN',1,0,NULL,'2017-09-25 10:07:37','192.168.1.94','2013-10-16 18:11:51','2017-09-25 10:07:37');
+INSERT INTO `b_user` (`id`, `login_name`, `name`, `password`, `role`, `enable`, `school_id`, `subject_code`, `last_login_time`,
+                      `last_login_ip`, `created_time`, `updated_time`)
+VALUES (1, 'admin', '超级管理员', '14e1b600b1fd579f47433b88e8d85291', 'SYS_ADMIN', 1, 0, NULL, '2017-09-25 10:07:37', '192.168.1.94',
+        '2013-10-16 18:11:51', '2017-09-25 10:07:37');
 
 UNLOCK TABLES;
 
 
+# Dump of table b_open_account
+# ------------------------------------------------------------
+
+DROP TABLE IF EXISTS `b_open_account`;
+
+CREATE TABLE `b_open_account`
+(
+    `id`                        int(11)     NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `school_id`                 int(11)     NOT NULL COMMENT '所属学校ID',
+    `account`                   varchar(64) NOT NULL COMMENT '账号',
+    `name`                      varchar(64) NOT NULL COMMENT '姓名',
+    `access_token`              varchar(64) DEFAULT NULL COMMENT '访问令牌',
+    `access_token_refresh_time` datetime    DEFAULT NULL COMMENT '令牌刷新时间',
+    `create_time`               datetime    NOT NULL COMMENT '创建时间',
+    PRIMARY KEY (`id`),
+    UNIQUE KEY `index1` (`school_id`, `account`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='第三方账号表';
+
+
 # Dump of table eb_check_student
 # ------------------------------------------------------------
 
 DROP TABLE IF EXISTS `eb_check_student`;
-CREATE TABLE `eb_check_student` (
-  `student_id` int(11) NOT NULL COMMENT '学生ID',
-  `is_checked` tinyint(1) DEFAULT NULL COMMENT '是否审核',
-  `exam_id` int(11) NOT NULL COMMENT '考试ID',
-  `subject_code` varchar(32) NOT NULL COMMENT '科目代码',
-  `type` varchar(16) NOT NULL COMMENT '类型',
-  `update_time` datetime DEFAULT NULL COMMENT '修改时间',
-  PRIMARY KEY (`student_id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='审核学生表';
+CREATE TABLE `eb_check_student`
+(
+    `student_id`   int(11)     NOT NULL COMMENT '学生ID',
+    `is_checked`   tinyint(1) DEFAULT NULL COMMENT '是否审核',
+    `exam_id`      int(11)     NOT NULL COMMENT '考试ID',
+    `subject_code` varchar(32) NOT NULL COMMENT '科目代码',
+    `type`         varchar(16) NOT NULL COMMENT '类型',
+    `update_time`  datetime   DEFAULT NULL COMMENT '修改时间',
+    PRIMARY KEY (`student_id`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='审核学生表';
 
 
 # Dump of table eb_exam
@@ -100,27 +133,29 @@ CREATE TABLE `eb_check_student` (
 
 DROP TABLE IF EXISTS `eb_exam`;
 
-CREATE TABLE `eb_exam` (
-  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
-  `name` varchar(64) NOT NULL COMMENT '考试名称',
-  `school_id` int(11) NOT NULL COMMENT '学校ID',
-  `exam_time` datetime NOT NULL COMMENT '考试时间',
-  `status` varchar(16) NOT NULL COMMENT '状态',
-  `description` varchar(128) DEFAULT NULL COMMENT '描述',
-  `force_special_tag` tinyint(1) NOT NULL COMMENT '强制标记',
-  `slice_config` text DEFAULT NULL COMMENT '裁切图配置',
-  `creator_id` int(11) NOT NULL COMMENT '创建人',
-  `create_time` datetime NOT NULL COMMENT '创建时间',
-  `update_time` datetime DEFAULT NULL COMMENT '修改时间',
-  `excellent_score` double DEFAULT NULL COMMENT '优秀分',
-  `pass_score` double DEFAULT NULL COMMENT '及格分',
-  `need_calculate` tinyint(1) NOT NULL COMMENT '需要统分',
-  `process` double DEFAULT NULL COMMENT '统分进度',
-  `sas_config` text DEFAULT NULL COMMENT '统计配置',
-  `sheet_config` text DEFAULT NULL COMMENT '原图遮盖配置',
-  PRIMARY KEY (`id`),
-  KEY `index1` (`school_id`,`create_time`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='考试表';
+CREATE TABLE `eb_exam`
+(
+    `id`                int(11)     NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `name`              varchar(64) NOT NULL COMMENT '考试名称',
+    `school_id`         int(11)     NOT NULL COMMENT '学校ID',
+    `exam_time`         datetime    NOT NULL COMMENT '考试时间',
+    `status`            varchar(16) NOT NULL COMMENT '状态',
+    `description`       varchar(128) DEFAULT NULL COMMENT '描述',
+    `force_special_tag` tinyint(1)  NOT NULL COMMENT '强制标记',
+    `slice_config`      text         DEFAULT NULL COMMENT '裁切图配置',
+    `creator_id`        int(11)     NOT NULL COMMENT '创建人',
+    `create_time`       datetime    NOT NULL COMMENT '创建时间',
+    `update_time`       datetime     DEFAULT NULL COMMENT '修改时间',
+    `excellent_score`   double       DEFAULT NULL COMMENT '优秀分',
+    `pass_score`        double       DEFAULT NULL COMMENT '及格分',
+    `need_calculate`    tinyint(1)  NOT NULL COMMENT '需要统分',
+    `process`           double       DEFAULT NULL COMMENT '统分进度',
+    `sas_config`        text         DEFAULT NULL COMMENT '统计配置',
+    `sheet_config`      text         DEFAULT NULL COMMENT '原图遮盖配置',
+    PRIMARY KEY (`id`),
+    KEY `index1` (`school_id`, `create_time`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='考试表';
 
 
 
@@ -129,12 +164,14 @@ CREATE TABLE `eb_exam` (
 
 DROP TABLE IF EXISTS `eb_exam_package`;
 
-CREATE TABLE `eb_exam_package` (
-  `exam_id` int(11) NOT NULL COMMENT '考试ID',
-  `code` varchar(128) NOT NULL COMMENT '试卷袋编号',
-  `pic_count` int(11) NOT NULL COMMENT '上传图片数量',
-  PRIMARY KEY (`exam_id`,`code`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='卷袋信息表';
+CREATE TABLE `eb_exam_package`
+(
+    `exam_id`   int(11)      NOT NULL COMMENT '考试ID',
+    `code`      varchar(128) NOT NULL COMMENT '试卷袋编号',
+    `pic_count` int(11)      NOT NULL COMMENT '上传图片数量',
+    PRIMARY KEY (`exam_id`, `code`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='卷袋信息表';
 
 
 
@@ -143,24 +180,26 @@ CREATE TABLE `eb_exam_package` (
 
 DROP TABLE IF EXISTS `eb_exam_question`;
 
-CREATE TABLE `eb_exam_question` (
-  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
-  `exam_id` int(11) NOT NULL COMMENT '考试ID',
-  `subject_code` varchar(32) NOT NULL DEFAULT '' COMMENT '科目代码',
-  `group_number` int(11) NOT NULL COMMENT '分组序号',
-  `main_number` int(11) NOT NULL COMMENT '大题号',
-  `sub_number` int(11) NOT NULL COMMENT '小题号',
-  `is_objective` tinyint(1) NOT NULL COMMENT '是否客观题',
-  `main_title` varchar(32) NOT NULL COMMENT '大题名称',
-  `answer` varchar(16) DEFAULT NULL COMMENT '正确答案',
-  `total_score` double NOT NULL COMMENT '满分',
-  `interval_score` double NOT NULL COMMENT '评卷间隔分',
-  `total_count` int(11) DEFAULT NULL COMMENT '总人数',
-  `paper_type` varchar(32) DEFAULT NULL COMMENT '试卷类型',
-  `objective_policy` varchar(16) DEFAULT NULL COMMENT '客观题判分策略',
-  PRIMARY KEY (`id`),
-  KEY `index1` (`exam_id`,`subject_code`,`is_objective`,`main_number`,`sub_number`,`paper_type`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='小题信息表';
+CREATE TABLE `eb_exam_question`
+(
+    `id`               int(11)     NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `exam_id`          int(11)     NOT NULL COMMENT '考试ID',
+    `subject_code`     varchar(32) NOT NULL DEFAULT '' COMMENT '科目代码',
+    `group_number`     int(11)     NOT NULL COMMENT '分组序号',
+    `main_number`      int(11)     NOT NULL COMMENT '大题号',
+    `sub_number`       int(11)     NOT NULL COMMENT '小题号',
+    `is_objective`     tinyint(1)  NOT NULL COMMENT '是否客观题',
+    `main_title`       varchar(32) NOT NULL COMMENT '大题名称',
+    `answer`           varchar(16)          DEFAULT NULL COMMENT '正确答案',
+    `total_score`      double      NOT NULL COMMENT '满分',
+    `interval_score`   double      NOT NULL COMMENT '评卷间隔分',
+    `total_count`      int(11)              DEFAULT NULL COMMENT '总人数',
+    `paper_type`       varchar(32)          DEFAULT NULL COMMENT '试卷类型',
+    `objective_policy` varchar(16)          DEFAULT NULL COMMENT '客观题判分策略',
+    PRIMARY KEY (`id`),
+    KEY `index1` (`exam_id`, `subject_code`, `is_objective`, `main_number`, `sub_number`, `paper_type`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='小题信息表';
 
 
 
@@ -169,45 +208,47 @@ CREATE TABLE `eb_exam_question` (
 
 DROP TABLE IF EXISTS `eb_exam_student`;
 
-CREATE TABLE `eb_exam_student` (
-  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
-  `exam_id` int(11) NOT NULL COMMENT '考试ID',
-  `school_id` int(11) NOT NULL COMMENT '学校ID',
-  `campus_name` varchar(64) NOT NULL COMMENT '学习中心名称',
-  `subject_code` varchar(32) NOT NULL COMMENT '科目代码',
-  `subject_name` varchar(32) NOT NULL COMMENT '科目名称',
-  `exam_number` varchar(64) NOT NULL COMMENT '准考证号',
-  `student_code` varchar(64) NOT NULL COMMENT '学号',
-  `name` varchar(32) NOT NULL COMMENT '姓名',
-  `package_code` varchar(128) DEFAULT NULL COMMENT '试卷袋编号',
-  `exam_site` varchar(64) DEFAULT NULL COMMENT '考点',
-  `exam_room` varchar(64) DEFAULT NULL COMMENT '考场',
-  `remark` varchar(255) DEFAULT NULL COMMENT '备注',
-  `batch_code` varchar(32) DEFAULT NULL COMMENT '扫描批次号',
-  `sheet_count` int(11) NOT NULL COMMENT '原图数量',
-  `slice_count` int(11) NOT NULL COMMENT '小图数量',
-  `answers` text COMMENT '客观题识别结果',
-  `is_upload` tinyint(1) NOT NULL COMMENT '是否已上传',
-  `is_absent` tinyint(1) NOT NULL COMMENT '是否缺考',
-  `is_manual_absent` tinyint(1) NOT NULL COMMENT '是否人工指定缺考',
-  `is_breach` tinyint(1) NOT NULL COMMENT '是否违纪',
-  `is_exception` tinyint(1) NOT NULL COMMENT '是否数据异常',
-  `upload_time` datetime DEFAULT NULL COMMENT '上传时间',
-  `objective_score` double DEFAULT NULL COMMENT '客观总分',
-  `subjective_score` double DEFAULT NULL COMMENT '主观总分',
-  `objective_score_list` text COMMENT '客观得分明细',
-  `subjective_score_list` text COMMENT '主观得分明细',
-  `subject_level` varchar(64) DEFAULT NULL COMMENT '层次',
-  `subject_category` varchar(64) DEFAULT NULL COMMENT '专业类型',
-  `paper_type` varchar(32) DEFAULT NULL COMMENT '试卷类型',
-  `college` varchar(64) DEFAULT NULL COMMENT '学院',
-  `class_name` varchar(64) DEFAULT NULL COMMENT '班级',
-  `teacher` varchar(64) DEFAULT NULL COMMENT '任课老师',
-  PRIMARY KEY (`id`),
-  UNIQUE KEY `index1` (`exam_id`,`exam_number`),
-  KEY `index2` (`exam_id`,`student_code`),
-  KEY `index3` (`exam_id`,`subject_code`,`upload_time`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='考试考生库';
+CREATE TABLE `eb_exam_student`
+(
+    `id`                    int(11)     NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `exam_id`               int(11)     NOT NULL COMMENT '考试ID',
+    `school_id`             int(11)     NOT NULL COMMENT '学校ID',
+    `campus_name`           varchar(64) NOT NULL COMMENT '学习中心名称',
+    `subject_code`          varchar(32) NOT NULL COMMENT '科目代码',
+    `subject_name`          varchar(32) NOT NULL COMMENT '科目名称',
+    `exam_number`           varchar(64) NOT NULL COMMENT '准考证号',
+    `student_code`          varchar(64) NOT NULL COMMENT '学号',
+    `name`                  varchar(32) NOT NULL COMMENT '姓名',
+    `package_code`          varchar(128) DEFAULT NULL COMMENT '试卷袋编号',
+    `exam_site`             varchar(64)  DEFAULT NULL COMMENT '考点',
+    `exam_room`             varchar(64)  DEFAULT NULL COMMENT '考场',
+    `remark`                varchar(255) DEFAULT NULL COMMENT '备注',
+    `batch_code`            varchar(32)  DEFAULT NULL COMMENT '扫描批次号',
+    `sheet_count`           int(11)     NOT NULL COMMENT '原图数量',
+    `slice_count`           int(11)     NOT NULL COMMENT '小图数量',
+    `answers`               text COMMENT '客观题识别结果',
+    `is_upload`             tinyint(1)  NOT NULL COMMENT '是否已上传',
+    `is_absent`             tinyint(1)  NOT NULL COMMENT '是否缺考',
+    `is_manual_absent`      tinyint(1)  NOT NULL COMMENT '是否人工指定缺考',
+    `is_breach`             tinyint(1)  NOT NULL COMMENT '是否违纪',
+    `is_exception`          tinyint(1)  NOT NULL COMMENT '是否数据异常',
+    `upload_time`           datetime     DEFAULT NULL COMMENT '上传时间',
+    `objective_score`       double       DEFAULT NULL COMMENT '客观总分',
+    `subjective_score`      double       DEFAULT NULL COMMENT '主观总分',
+    `objective_score_list`  text COMMENT '客观得分明细',
+    `subjective_score_list` text COMMENT '主观得分明细',
+    `subject_level`         varchar(64)  DEFAULT NULL COMMENT '层次',
+    `subject_category`      varchar(64)  DEFAULT NULL COMMENT '专业类型',
+    `paper_type`            varchar(32)  DEFAULT NULL COMMENT '试卷类型',
+    `college`               varchar(64)  DEFAULT NULL COMMENT '学院',
+    `class_name`            varchar(64)  DEFAULT NULL COMMENT '班级',
+    `teacher`               varchar(64)  DEFAULT NULL COMMENT '任课老师',
+    PRIMARY KEY (`id`),
+    UNIQUE KEY `index1` (`exam_id`, `exam_number`),
+    KEY `index2` (`exam_id`, `student_code`),
+    KEY `index3` (`exam_id`, `subject_code`, `upload_time`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='考试考生库';
 
 
 
@@ -216,23 +257,25 @@ CREATE TABLE `eb_exam_student` (
 
 DROP TABLE IF EXISTS `eb_exam_subject`;
 
-CREATE TABLE `eb_exam_subject` (
-  `exam_id` int(11) NOT NULL COMMENT '考试ID',
-  `code` varchar(32) NOT NULL COMMENT '科目代码',
-  `name` varchar(32) NOT NULL COMMENT '科目名称',
-  `level` varchar(64) DEFAULT NULL COMMENT '层次',
-  `category` varchar(64) DEFAULT NULL COMMENT '专业类型',
-  `objective_score` double NOT NULL COMMENT '客观题满分',
-  `subjective_score` double NOT NULL COMMENT '主观题满分',
-  `total_score` double NOT NULL COMMENT '全卷满分',
-  `upload_count` int(11) NOT NULL COMMENT '已上传人数',
-  `has_answer` tinyint(1) NOT NULL COMMENT '是否上传答案',
-  `has_paper` tinyint(1) NOT NULL COMMENT '是否上传试卷',
-  `remark` varchar(255) DEFAULT NULL COMMENT '备注',
-  `slice_config` text DEFAULT NULL COMMENT '裁切图配置',
-  `sheet_config` text DEFAULT NULL COMMENT '原图遮盖配置',
-  PRIMARY KEY (`exam_id`,`code`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='考试科目表';
+CREATE TABLE `eb_exam_subject`
+(
+    `exam_id`          int(11)     NOT NULL COMMENT '考试ID',
+    `code`             varchar(32) NOT NULL COMMENT '科目代码',
+    `name`             varchar(32) NOT NULL COMMENT '科目名称',
+    `level`            varchar(64)  DEFAULT NULL COMMENT '层次',
+    `category`         varchar(64)  DEFAULT NULL COMMENT '专业类型',
+    `objective_score`  double      NOT NULL COMMENT '客观题满分',
+    `subjective_score` double      NOT NULL COMMENT '主观题满分',
+    `total_score`      double      NOT NULL COMMENT '全卷满分',
+    `upload_count`     int(11)     NOT NULL COMMENT '已上传人数',
+    `has_answer`       tinyint(1)  NOT NULL COMMENT '是否上传答案',
+    `has_paper`        tinyint(1)  NOT NULL COMMENT '是否上传试卷',
+    `remark`           varchar(255) DEFAULT NULL COMMENT '备注',
+    `slice_config`     text         DEFAULT NULL COMMENT '裁切图配置',
+    `sheet_config`     text         DEFAULT NULL COMMENT '原图遮盖配置',
+    PRIMARY KEY (`exam_id`, `code`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='考试科目表';
 
 
 
@@ -241,12 +284,14 @@ CREATE TABLE `eb_exam_subject` (
 
 DROP TABLE IF EXISTS `eb_export_imglist`;
 
-CREATE TABLE `eb_export_imglist` (
-  `exam_id` int(11) NOT NULL,
-  `exam_number` varchar(50) COLLATE utf8_bin NOT NULL,
-  `Remark` varchar(255) COLLATE utf8_bin DEFAULT NULL,
-  PRIMARY KEY (`exam_id`,`exam_number`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+CREATE TABLE `eb_export_imglist`
+(
+    `exam_id`     int(11)                      NOT NULL,
+    `exam_number` varchar(50) COLLATE utf8_bin NOT NULL,
+    `Remark`      varchar(255) COLLATE utf8_bin DEFAULT NULL,
+    PRIMARY KEY (`exam_id`, `exam_number`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8;
 
 
 
@@ -255,26 +300,28 @@ CREATE TABLE `eb_export_imglist` (
 
 DROP TABLE IF EXISTS `eb_mark_group`;
 
-CREATE TABLE `eb_mark_group` (
-  `exam_id` int(11) NOT NULL COMMENT '考试ID',
-  `subject_code` varchar(32) NOT NULL COMMENT '科目代码',
-  `number` int(11) NOT NULL COMMENT '序号',
-  `status` varchar(16) NOT NULL COMMENT '评卷状态',
-  `pic_list` varchar(255) NOT NULL COMMENT '小图配置',
-  `total_score` double NOT NULL COMMENT '满分',
-  `trial_count` int(11) DEFAULT NULL COMMENT '试评数量',
-  `double_rate` double DEFAULT NULL COMMENT '双评比例',
-  `arbitrate_threshold` double DEFAULT NULL COMMENT '仲裁阈值',
-  `score_policy` varchar(16) DEFAULT NULL COMMENT '合分策略',
-  `mark_mode` varchar(16) DEFAULT NULL COMMENT '强制评卷模式',
-  `sheet_view` tinyint(1) NOT NULL COMMENT '是否允许查看原卷',
-  `build_time` datetime DEFAULT NULL COMMENT '最后生成任务时间',
-  `library_count` int(11) NOT NULL COMMENT '任务总量',
-  `marked_count` int(11) NOT NULL COMMENT '已评数量',
-  `left_count` int(11) NOT NULL COMMENT '剩余数量',
-  `third_policy` varchar(32) DEFAULT NULL COMMENT '三评规则',
-  PRIMARY KEY (`exam_id`,`subject_code`,`number`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='评卷分组表';
+CREATE TABLE `eb_mark_group`
+(
+    `exam_id`             int(11)      NOT NULL COMMENT '考试ID',
+    `subject_code`        varchar(32)  NOT NULL COMMENT '科目代码',
+    `number`              int(11)      NOT NULL COMMENT '序号',
+    `status`              varchar(16)  NOT NULL COMMENT '评卷状态',
+    `pic_list`            varchar(255) NOT NULL COMMENT '小图配置',
+    `total_score`         double       NOT NULL COMMENT '满分',
+    `trial_count`         int(11)     DEFAULT NULL COMMENT '试评数量',
+    `double_rate`         double      DEFAULT NULL COMMENT '双评比例',
+    `arbitrate_threshold` double      DEFAULT NULL COMMENT '仲裁阈值',
+    `score_policy`        varchar(16) DEFAULT NULL COMMENT '合分策略',
+    `mark_mode`           varchar(16) DEFAULT NULL COMMENT '强制评卷模式',
+    `sheet_view`          tinyint(1)   NOT NULL COMMENT '是否允许查看原卷',
+    `build_time`          datetime    DEFAULT NULL COMMENT '最后生成任务时间',
+    `library_count`       int(11)      NOT NULL COMMENT '任务总量',
+    `marked_count`        int(11)      NOT NULL COMMENT '已评数量',
+    `left_count`          int(11)      NOT NULL COMMENT '剩余数量',
+    `third_policy`        varchar(32) DEFAULT NULL COMMENT '三评规则',
+    PRIMARY KEY (`exam_id`, `subject_code`, `number`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='评卷分组表';
 
 
 
@@ -283,31 +330,36 @@ CREATE TABLE `eb_mark_group` (
 
 DROP TABLE IF EXISTS `eb_marker`;
 
-CREATE TABLE `eb_marker` (
-  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
-  `exam_id` int(11) NOT NULL COMMENT '考试ID',
-  `subject_code` varchar(12) NOT NULL COMMENT '科目代码',
-  `group_number` int(11) NOT NULL COMMENT '大题号',
-  `login_name` varchar(128) NOT NULL COMMENT '登录名',
-  `name` varchar(64) NOT NULL COMMENT '姓名',
-  `password` varchar(128) NOT NULL COMMENT '密码',
-  `enable` tinyint(1) NOT NULL COMMENT '是否禁用',
-  `mode` varchar(16) DEFAULT NULL COMMENT '评卷模式',
-  `top_count` int(11) DEFAULT NULL COMMENT '评卷数上限',
-  `last_login_ip` varchar(64) DEFAULT NULL COMMENT '上次登录IP',
-  `last_login_time` datetime DEFAULT NULL COMMENT '上次登录时间',
-  `avg_score` double DEFAULT NULL COMMENT '平均分',
-  `avg_speed` double DEFAULT NULL COMMENT '平均时长',
-  `finish_count` int(11) DEFAULT NULL COMMENT '完成数量',
-  `stdev_score` double DEFAULT NULL COMMENT '方差',
-  `valid_count` int(11) DEFAULT NULL COMMENT '有效数量',
-  `class_count` int(11) NOT NULL COMMENT '班级数量',
-  `mark_setting` text DEFAULT NULL COMMENT '个性化评卷参数设置',
-  `web_token` varchar(128) DEFAULT NULL COMMENT '登录密钥',
-  PRIMARY KEY (`id`),
-  KEY `index1` (`login_name`),
-  KEY `index2` (`exam_id`,`subject_code`,`group_number`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='评卷员表';
+CREATE TABLE `eb_marker`
+(
+    `id`                        int(11)     NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `exam_id`                   int(11)     NOT NULL COMMENT '考试ID',
+    `subject_code`              varchar(32) NOT NULL COMMENT '科目代码',
+    `group_number`              int(11)     NOT NULL COMMENT '分组编号',
+    `login_name`                varchar(64) NOT NULL COMMENT '登录名',
+    `name`                      varchar(64) NOT NULL COMMENT '姓名',
+    `password`                  varchar(64) NOT NULL COMMENT '密码',
+    `enable`                    tinyint(1)  NOT NULL COMMENT '是否启用',
+    `account_type`              varchar(16) DEFAULT NULL COMMENT '绑定账号类型',
+    `account_id`                int(11)     DEFAULT NULL COMMENT '绑定账号ID',
+    `mode`                      varchar(16) DEFAULT NULL COMMENT '评卷模式',
+    `top_count`                 int(11)     DEFAULT NULL COMMENT '评卷数上限',
+    `avg_score`                 double      DEFAULT NULL COMMENT '平均分',
+    `avg_speed`                 double      DEFAULT NULL COMMENT '平均时长',
+    `finish_count`              int(11)     DEFAULT NULL COMMENT '完成数量',
+    `stdev_score`               double      DEFAULT NULL COMMENT '方差',
+    `valid_count`               int(11)     DEFAULT NULL COMMENT '有效数量',
+    `class_count`               int(11)     NOT NULL COMMENT '班级数量',
+    `mark_setting`              text        DEFAULT NULL COMMENT '个性化评卷参数设置',
+    `access_token`              varchar(64) DEFAULT NULL COMMENT '访问令牌',
+    `access_token_refresh_time` datetime    DEFAULT NULL COMMENT '访问令牌刷新时间',
+    `last_login_ip`             varchar(64) DEFAULT NULL COMMENT '上次登录IP',
+    `last_login_time`           datetime    DEFAULT NULL COMMENT '上次登录时间',
+    PRIMARY KEY (`id`),
+    UNIQUE KEY `index1` (`login_name`),
+    KEY `index2` (`exam_id`, `subject_code`, `group_number`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='评卷员表';
 
 
 # Dump of table eb_marker_class
@@ -315,13 +367,15 @@ CREATE TABLE `eb_marker` (
 
 DROP TABLE IF EXISTS `eb_marker_class`;
 
-CREATE TABLE `eb_marker_class` (
-  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
-  `class_name` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '班级名称',
-  `marker_id` int(11) NOT NULL COMMENT '评卷员ID',
-  PRIMARY KEY (`id`),
-  KEY `index1` (`marker_id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='评卷员班级表';
+CREATE TABLE `eb_marker_class`
+(
+    `id`         int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `class_name` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '班级名称',
+    `marker_id`  int(11) NOT NULL COMMENT '评卷员ID',
+    PRIMARY KEY (`id`),
+    KEY `index1` (`marker_id`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='评卷员班级表';
 
 
 # Dump of table m_arbitrate_history
@@ -329,24 +383,26 @@ CREATE TABLE `eb_marker_class` (
 
 DROP TABLE IF EXISTS `m_arbitrate_history`;
 
-CREATE TABLE `m_arbitrate_history` (
-  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
-  `exam_id` int(11) NOT NULL COMMENT '考试ID',
-  `subject_code` varchar(32) NOT NULL COMMENT '科目代码',
-  `group_number` int(11) NOT NULL COMMENT '大题号',
-  `exam_number` varchar(64) NOT NULL COMMENT '准考证号',
-  `student_id` int(11) NOT NULL COMMENT '考生ID',
-  `status` int(11) NOT NULL COMMENT '状态',
-  `user_id` int(11) DEFAULT NULL COMMENT '处理人ID',
-  `total_score` double DEFAULT NULL COMMENT '总分',
-  `score_list` varchar(255) DEFAULT NULL COMMENT '给分明细',
-  `create_time` datetime NOT NULL COMMENT '创建时间',
-  `update_time` datetime DEFAULT NULL COMMENT '处理时间',
-  PRIMARY KEY (`id`),
-  KEY `index1` (`exam_id`,`subject_code`,`group_number`,`status`),
-  KEY `index2` (`user_id`,`status`,`update_time`),
-  KEY `index3` (`student_id`,`status`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='仲裁记录表';
+CREATE TABLE `m_arbitrate_history`
+(
+    `id`           int(11)     NOT NULL AUTO_INCREMENT COMMENT '自增主键',
+    `exam_id`      int(11)     NOT NULL COMMENT '考试ID',
+    `subject_code` varchar(32) NOT NULL COMMENT '科目代码',
+    `group_number` int(11)     NOT NULL COMMENT '大题号',
+    `exam_number`  varchar(64) NOT NULL COMMENT '准考证号',
+    `student_id`   int(11)     NOT NULL COMMENT '考生ID',
+    `status`       int(11)     NOT NULL COMMENT '状态',
+    `user_id`      int(11)      DEFAULT NULL COMMENT '处理人ID',
+    `total_score`  double       DEFAULT NULL COMMENT '总分',
+    `score_list`   varchar(255) DEFAULT NULL COMMENT '给分明细',
+    `create_time`  datetime    NOT NULL COMMENT '创建时间',
+    `update_time`  datetime     DEFAULT NULL COMMENT '处理时间',
+    PRIMARY KEY (`id`),
+    KEY `index1` (`exam_id`, `subject_code`, `group_number`, `status`),
+    KEY `index2` (`user_id`, `status`, `update_time`),
+    KEY `index3` (`student_id`, `status`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='仲裁记录表';
 
 
 # Dump of table m_library
@@ -354,30 +410,32 @@ CREATE TABLE `m_arbitrate_history` (
 
 DROP TABLE IF EXISTS `m_library`;
 
-CREATE TABLE `m_library` (
-  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
-  `exam_id` int(11) NOT NULL COMMENT '考试ID',
-  `subject_code` varchar(32) NOT NULL COMMENT '科目代码',
-  `group_number` int(11) NOT NULL COMMENT '大题号',
-  `campus_id` int(11) NOT NULL COMMENT '学习中心ID',
-  `student_id` int(11) NOT NULL COMMENT '考生ID',
-  `exam_number` varchar(64) NOT NULL COMMENT '准考证号',
-  `task_number` int(11) DEFAULT NULL COMMENT '多评任务序号',
-  `marker_id` int(11) DEFAULT NULL COMMENT '评卷员ID',
-  `marker_time` datetime DEFAULT NULL COMMENT '评卷时间',
-  `marker_score` double DEFAULT NULL COMMENT '评卷总分',
-  `marker_score_list` varchar(256) DEFAULT NULL COMMENT '评卷给分明细',
-  `status` int(11) NOT NULL COMMENT '状态',
-  `header_id` int(11) DEFAULT NULL COMMENT '科组长ID',
-  `header_score` double DEFAULT NULL COMMENT '科组长总分',
-  `header_score_list` varchar(255) DEFAULT NULL COMMENT '科组长给分明细',
-  `header_time` datetime DEFAULT NULL COMMENT '科组长评卷时间',
-  `marker_spent` int(11) DEFAULT NULL COMMENT '最新评卷时间',
-  PRIMARY KEY (`id`),
-  KEY `index1` (`exam_id`,`subject_code`,`group_number`,`status`),
-  UNIQUE KEY `index2` (`student_id`,`group_number`,`task_number`),
-  KEY `index3` (`marker_id`,`status`,`marker_time`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='评卷任务表';
+CREATE TABLE `m_library`
+(
+    `id`                int(11)     NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `exam_id`           int(11)     NOT NULL COMMENT '考试ID',
+    `subject_code`      varchar(32) NOT NULL COMMENT '科目代码',
+    `group_number`      int(11)     NOT NULL COMMENT '大题号',
+    `campus_id`         int(11)     NOT NULL COMMENT '学习中心ID',
+    `student_id`        int(11)     NOT NULL COMMENT '考生ID',
+    `exam_number`       varchar(64) NOT NULL COMMENT '准考证号',
+    `task_number`       int(11)      DEFAULT NULL COMMENT '多评任务序号',
+    `marker_id`         int(11)      DEFAULT NULL COMMENT '评卷员ID',
+    `marker_time`       datetime     DEFAULT NULL COMMENT '评卷时间',
+    `marker_score`      double       DEFAULT NULL COMMENT '评卷总分',
+    `marker_score_list` varchar(256) DEFAULT NULL COMMENT '评卷给分明细',
+    `status`            int(11)     NOT NULL COMMENT '状态',
+    `header_id`         int(11)      DEFAULT NULL COMMENT '科组长ID',
+    `header_score`      double       DEFAULT NULL COMMENT '科组长总分',
+    `header_score_list` varchar(255) DEFAULT NULL COMMENT '科组长给分明细',
+    `header_time`       datetime     DEFAULT NULL COMMENT '科组长评卷时间',
+    `marker_spent`      int(11)      DEFAULT NULL COMMENT '最新评卷时间',
+    PRIMARY KEY (`id`),
+    KEY `index1` (`exam_id`, `subject_code`, `group_number`, `status`),
+    UNIQUE KEY `index2` (`student_id`, `group_number`, `task_number`),
+    KEY `index3` (`marker_id`, `status`, `marker_time`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='评卷任务表';
 
 
 # Dump of table m_special_tag
@@ -385,15 +443,17 @@ CREATE TABLE `m_library` (
 
 DROP TABLE IF EXISTS `m_special_tag`;
 
-CREATE TABLE `m_special_tag` (
-  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
-  `library_id` int(11) NOT NULL COMMENT '评卷任务ID',
-  `position_x` double NOT NULL COMMENT 'X轴位置',
-  `position_y` double NOT NULL COMMENT 'Y轴位置',
-  `tag_name` varchar(64) NOT NULL COMMENT '标记内容',
-  PRIMARY KEY (`id`),
-  KEY `index1` (`library_id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='特殊标记表';
+CREATE TABLE `m_special_tag`
+(
+    `id`         int(11)     NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `library_id` int(11)     NOT NULL COMMENT '评卷任务ID',
+    `position_x` double      NOT NULL COMMENT 'X轴位置',
+    `position_y` double      NOT NULL COMMENT 'Y轴位置',
+    `tag_name`   varchar(64) NOT NULL COMMENT '标记内容',
+    PRIMARY KEY (`id`),
+    KEY `index1` (`library_id`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='特殊标记表';
 
 
 # Dump of table m_track
@@ -401,398 +461,432 @@ CREATE TABLE `m_special_tag` (
 
 DROP TABLE IF EXISTS `m_track`;
 
-CREATE TABLE `m_track` (
-  `library_id` int(11) NOT NULL COMMENT '评卷任务ID',
-  `question_number` varchar(128) NOT NULL COMMENT '完整题号',
-  `number` int(11) NOT NULL COMMENT '序号',
-  `student_id` int(11) NOT NULL COMMENT '考生ID',
-  `exam_id` int(11) NOT NULL COMMENT '考试ID',
-  `subject_code` varchar(32) NOT NULL COMMENT '科目代码',
-  `group_number` int(11) NOT NULL COMMENT '大题题号',
-  `marker_id` int(11) NOT NULL COMMENT '评卷员ID',
-  `score` double NOT NULL COMMENT '给分',
-  `position_x` double NOT NULL COMMENT 'X轴位置',
-  `position_y` double NOT NULL COMMENT 'Y轴位置',
-  PRIMARY KEY (`library_id`,`question_number`,`number`),
-  KEY `index1` (`student_id`,`group_number`),
-  KEY `index2` (`marker_id`),
-  KEY `index3` (`exam_id`,`subject_code`,`group_number`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='轨迹给分表';
+CREATE TABLE `m_track`
+(
+    `library_id`      int(11)      NOT NULL COMMENT '评卷任务ID',
+    `question_number` varchar(128) NOT NULL COMMENT '完整题号',
+    `number`          int(11)      NOT NULL COMMENT '序号',
+    `student_id`      int(11)      NOT NULL COMMENT '考生ID',
+    `exam_id`         int(11)      NOT NULL COMMENT '考试ID',
+    `subject_code`    varchar(32)  NOT NULL COMMENT '科目代码',
+    `group_number`    int(11)      NOT NULL COMMENT '大题题号',
+    `marker_id`       int(11)      NOT NULL COMMENT '评卷员ID',
+    `score`           double       NOT NULL COMMENT '给分',
+    `position_x`      double       NOT NULL COMMENT 'X轴位置',
+    `position_y`      double       NOT NULL COMMENT 'Y轴位置',
+    PRIMARY KEY (`library_id`, `question_number`, `number`),
+    KEY `index1` (`student_id`, `group_number`),
+    KEY `index2` (`marker_id`),
+    KEY `index3` (`exam_id`, `subject_code`, `group_number`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='轨迹给分表';
 
 
 # Dump of table m_trial_history
 # ------------------------------------------------------------
 
 DROP TABLE IF EXISTS `m_trial_history`;
-CREATE TABLE `m_trial_history` (
-  `library_id` int(11) NOT NULL COMMENT '评卷任务ID',
-  `marker_id` int(11) NOT NULL COMMENT '评卷员ID',
-  `exam_id` int(11) NOT NULL COMMENT '考试ID',
-  `subject_code` varchar(32) NOT NULL COMMENT '科目代码',
-  `group_number` int(11) NOT NULL COMMENT '大题号',
-  `student_id` int(11) NOT NULL COMMENT '考生ID',
-  `marker_score` double NOT NULL COMMENT '评卷总分',
-  `marker_score_list` varchar(255) NOT NULL COMMENT '评卷总分明细',
-  `marker_time` datetime NOT NULL COMMENT '评卷时间',
-  PRIMARY KEY (`library_id`,`marker_id`),
-  KEY `index1` (`exam_id`,`subject_code`,`group_number`),
-  KEY `index2` (`student_id`),
-  KEY `index3` (`marker_id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='试评记录表';
+CREATE TABLE `m_trial_history`
+(
+    `library_id`        int(11)      NOT NULL COMMENT '评卷任务ID',
+    `marker_id`         int(11)      NOT NULL COMMENT '评卷员ID',
+    `exam_id`           int(11)      NOT NULL COMMENT '考试ID',
+    `subject_code`      varchar(32)  NOT NULL COMMENT '科目代码',
+    `group_number`      int(11)      NOT NULL COMMENT '大题号',
+    `student_id`        int(11)      NOT NULL COMMENT '考生ID',
+    `marker_score`      double       NOT NULL COMMENT '评卷总分',
+    `marker_score_list` varchar(255) NOT NULL COMMENT '评卷总分明细',
+    `marker_time`       datetime     NOT NULL COMMENT '评卷时间',
+    PRIMARY KEY (`library_id`, `marker_id`),
+    KEY `index1` (`exam_id`, `subject_code`, `group_number`),
+    KEY `index2` (`student_id`),
+    KEY `index3` (`marker_id`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='试评记录表';
 
 
 # Dump of table m_trial_library
 # ------------------------------------------------------------
 
 DROP TABLE IF EXISTS `m_trial_library`;
-CREATE TABLE `m_trial_library` (
-  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
-  `exam_id` int(11) NOT NULL COMMENT '考试ID',
-  `subject_code` varchar(32) NOT NULL COMMENT '科目代码',
-  `group_number` int(11) NOT NULL COMMENT '大题号',
-  `campus_id` int(11) NOT NULL COMMENT '学习中心ID',
-  `student_id` int(11) NOT NULL COMMENT '考生ID',
-  `exam_number` varchar(64) NOT NULL COMMENT '准考证号',
-  PRIMARY KEY (`id`),
-  UNIQUE KEY `index2` (`student_id`,`group_number`) USING BTREE,
-  KEY `index1` (`exam_id`,`subject_code`,`group_number`)
-) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='试评任务表';
+CREATE TABLE `m_trial_library`
+(
+    `id`           int(11)     NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `exam_id`      int(11)     NOT NULL COMMENT '考试ID',
+    `subject_code` varchar(32) NOT NULL COMMENT '科目代码',
+    `group_number` int(11)     NOT NULL COMMENT '大题号',
+    `campus_id`    int(11)     NOT NULL COMMENT '学习中心ID',
+    `student_id`   int(11)     NOT NULL COMMENT '考生ID',
+    `exam_number`  varchar(64) NOT NULL COMMENT '准考证号',
+    PRIMARY KEY (`id`),
+    UNIQUE KEY `index2` (`student_id`, `group_number`) USING BTREE,
+    KEY `index1` (`exam_id`, `subject_code`, `group_number`)
+) ENGINE = InnoDB
+  AUTO_INCREMENT = 4
+  DEFAULT CHARSET = utf8 COMMENT ='试评任务表';
 
 
 # Dump of table m_trial_tag
 # ------------------------------------------------------------
 
 DROP TABLE IF EXISTS `m_trial_tag`;
-CREATE TABLE `m_trial_tag` (
-  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
-  `library_id` int(11) NOT NULL COMMENT '评卷任务ID',
-  `marker_id` int(11) NOT NULL COMMENT '评卷员ID',
-  `content` varchar(16) NOT NULL COMMENT '标记内容',
-  `position_x` double NOT NULL COMMENT 'X轴位置',
-  `position_y` double NOT NULL COMMENT 'Y轴位置',
-  PRIMARY KEY (`id`),
-  KEY `index1` (`library_id`,`marker_id`),
-  KEY `index2` (`marker_id`)
-) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='试评特殊标记表';
+CREATE TABLE `m_trial_tag`
+(
+    `id`         int(11)     NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `library_id` int(11)     NOT NULL COMMENT '评卷任务ID',
+    `marker_id`  int(11)     NOT NULL COMMENT '评卷员ID',
+    `content`    varchar(16) NOT NULL COMMENT '标记内容',
+    `position_x` double      NOT NULL COMMENT 'X轴位置',
+    `position_y` double      NOT NULL COMMENT 'Y轴位置',
+    PRIMARY KEY (`id`),
+    KEY `index1` (`library_id`, `marker_id`),
+    KEY `index2` (`marker_id`)
+) ENGINE = InnoDB
+  AUTO_INCREMENT = 4
+  DEFAULT CHARSET = utf8 COMMENT ='试评特殊标记表';
 
 
 # Dump of table m_trial_track
 # ------------------------------------------------------------
 
 DROP TABLE IF EXISTS `m_trial_track`;
-CREATE TABLE `m_trial_track` (
-  `library_id` int(11) NOT NULL COMMENT '评卷任务ID',
-  `marker_id` int(11) NOT NULL COMMENT '评卷员ID',
-  `question_number` varchar(32) NOT NULL COMMENT '完整题号',
-  `number` int(11) NOT NULL COMMENT '序号',
-  `exam_id` int(11) NOT NULL COMMENT '考试ID',
-  `subject_code` varchar(255) NOT NULL COMMENT '科目代码',
-  `group_number` int(11) NOT NULL COMMENT '大题题号',
-  `student_id` int(11) NOT NULL COMMENT '考生ID',
-  `score` double NOT NULL COMMENT '给分',
-  `position_x` double NOT NULL COMMENT 'X轴位置',
-  `position_y` double NOT NULL COMMENT 'Y轴位置',
-  PRIMARY KEY (`library_id`,`marker_id`,`number`,`question_number`),
-  KEY `index1` (`exam_id`,`subject_code`,`group_number`),
-  KEY `index2` (`student_id`),
-  KEY `index3` (`marker_id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='试评轨迹表';
+CREATE TABLE `m_trial_track`
+(
+    `library_id`      int(11)      NOT NULL COMMENT '评卷任务ID',
+    `marker_id`       int(11)      NOT NULL COMMENT '评卷员ID',
+    `question_number` varchar(32)  NOT NULL COMMENT '完整题号',
+    `number`          int(11)      NOT NULL COMMENT '序号',
+    `exam_id`         int(11)      NOT NULL COMMENT '考试ID',
+    `subject_code`    varchar(255) NOT NULL COMMENT '科目代码',
+    `group_number`    int(11)      NOT NULL COMMENT '大题题号',
+    `student_id`      int(11)      NOT NULL COMMENT '考生ID',
+    `score`           double       NOT NULL COMMENT '给分',
+    `position_x`      double       NOT NULL COMMENT 'X轴位置',
+    `position_y`      double       NOT NULL COMMENT 'Y轴位置',
+    PRIMARY KEY (`library_id`, `marker_id`, `number`, `question_number`),
+    KEY `index1` (`exam_id`, `subject_code`, `group_number`),
+    KEY `index2` (`student_id`),
+    KEY `index3` (`marker_id`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='试评轨迹表';
 
 
 # Dump of table m_problem_type
 # ------------------------------------------------------------
 
 DROP TABLE IF EXISTS `m_problem_type`;
-CREATE TABLE `m_problem_type` (
-  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
-  `exam_id` int(11) NOT NULL COMMENT '考试ID',
-  `name` varchar(128) NOT NULL COMMENT '名称',
-  `is_custom` tinyint(1) NOT NULL COMMENT '是否自定义',
-  PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='问题类型表';
+CREATE TABLE `m_problem_type`
+(
+    `id`        int(11)      NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `exam_id`   int(11)      NOT NULL COMMENT '考试ID',
+    `name`      varchar(128) NOT NULL COMMENT '名称',
+    `is_custom` tinyint(1)   NOT NULL COMMENT '是否自定义',
+    PRIMARY KEY (`id`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='问题类型表';
 
 
 # Dump of table m_problem_history
 # ------------------------------------------------------------
 
 DROP TABLE IF EXISTS `m_problem_history`;
-CREATE TABLE `m_arbitrate_history` (
-  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
-  `exam_id` int(11) NOT NULL COMMENT '考试ID',
-  `subject_code` varchar(32) NOT NULL COMMENT '科目代码',
-  `group_number` int(11) NOT NULL COMMENT '大题号',
-  `exam_number` varchar(64) NOT NULL COMMENT '准考证号',
-  `student_id` int(11) NOT NULL COMMENT '考生ID',
-  `library_id` int(11) NOT NULL COMMENT '评卷任务ID',
-  `status` int(11) NOT NULL COMMENT '状态',
-  `user_id` int(11) DEFAULT NULL COMMENT '处理人ID',
-  `total_score` double DEFAULT NULL COMMENT '总分',
-  `score_list` varchar(255) DEFAULT NULL COMMENT '给分明细',
-  `create_time` datetime NOT NULL COMMENT '创建时间',
-  `update_time` datetime DEFAULT NULL COMMENT '处理时间',
-  PRIMARY KEY (`id`),
-  KEY `index1` (`exam_id`,`subject_code`,`group_number`,`status`),
-  KEY `index2` (`user_id`,`status`,`update_time`),
-  KEY `index3` (`student_id`,`status`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='问题卷历史表';
+CREATE TABLE `m_arbitrate_history`
+(
+    `id`           int(11)     NOT NULL AUTO_INCREMENT COMMENT '自增主键',
+    `exam_id`      int(11)     NOT NULL COMMENT '考试ID',
+    `subject_code` varchar(32) NOT NULL COMMENT '科目代码',
+    `group_number` int(11)     NOT NULL COMMENT '大题号',
+    `exam_number`  varchar(64) NOT NULL COMMENT '准考证号',
+    `student_id`   int(11)     NOT NULL COMMENT '考生ID',
+    `library_id`   int(11)     NOT NULL COMMENT '评卷任务ID',
+    `status`       int(11)     NOT NULL COMMENT '状态',
+    `user_id`      int(11)      DEFAULT NULL COMMENT '处理人ID',
+    `total_score`  double       DEFAULT NULL COMMENT '总分',
+    `score_list`   varchar(255) DEFAULT NULL COMMENT '给分明细',
+    `create_time`  datetime    NOT NULL COMMENT '创建时间',
+    `update_time`  datetime     DEFAULT NULL COMMENT '处理时间',
+    PRIMARY KEY (`id`),
+    KEY `index1` (`exam_id`, `subject_code`, `group_number`, `status`),
+    KEY `index2` (`user_id`, `status`, `update_time`),
+    KEY `index3` (`student_id`, `status`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='问题卷历史表';
 
 
 # Dump of table s_basic_group
 # ------------------------------------------------------------
 
 DROP TABLE IF EXISTS `s_basic_class_group`;
-CREATE TABLE `s_basic_class_group` (
-  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
-  `exam_id` int(11) DEFAULT NULL COMMENT '考试ID',
-  `subject_code` varchar(255) DEFAULT NULL COMMENT '科目代码',
-  `subject_name` varchar(255) DEFAULT NULL COMMENT '科目名称',
-  `class_name` varchar(255) DEFAULT NULL COMMENT '班级名称',
-  `discrimination` double DEFAULT NULL COMMENT '区分度',
-  `objective` tinyint(1) DEFAULT NULL COMMENT '是否客观题',
-  `group_name` varchar(255) DEFAULT NULL COMMENT '大题名称',
-  `group_number` int(11) DEFAULT NULL COMMENT '大题号',
-  `question_count` int(11) DEFAULT NULL COMMENT '题目数量',
-  `total_score` double DEFAULT NULL COMMENT '总分',
-  `difficulity_level` varchar(255) DEFAULT NULL COMMENT '难度分布',
-  `discrimination_level` varchar(255) DEFAULT NULL COMMENT '区分度分布',
-  `coefficient` double DEFAULT NULL COMMENT '差异系数',
-  `difficulty` double DEFAULT NULL COMMENT '难度',
-  `paper_type` varchar(32) DEFAULT NULL COMMENT '试卷类型',
-  PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='班级大题统计表';
+CREATE TABLE `s_basic_class_group`
+(
+    `id`                   int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `exam_id`              int(11)      DEFAULT NULL COMMENT '考试ID',
+    `subject_code`         varchar(255) DEFAULT NULL COMMENT '科目代码',
+    `subject_name`         varchar(255) DEFAULT NULL COMMENT '科目名称',
+    `class_name`           varchar(255) DEFAULT NULL COMMENT '班级名称',
+    `discrimination`       double       DEFAULT NULL COMMENT '区分度',
+    `objective`            tinyint(1)   DEFAULT NULL COMMENT '是否客观题',
+    `group_name`           varchar(255) DEFAULT NULL COMMENT '大题名称',
+    `group_number`         int(11)      DEFAULT NULL COMMENT '大题号',
+    `question_count`       int(11)      DEFAULT NULL COMMENT '题目数量',
+    `total_score`          double       DEFAULT NULL COMMENT '总分',
+    `difficulity_level`    varchar(255) DEFAULT NULL COMMENT '难度分布',
+    `discrimination_level` varchar(255) DEFAULT NULL COMMENT '区分度分布',
+    `coefficient`          double       DEFAULT NULL COMMENT '差异系数',
+    `difficulty`           double       DEFAULT NULL COMMENT '难度',
+    `paper_type`           varchar(32)  DEFAULT NULL COMMENT '试卷类型',
+    PRIMARY KEY (`id`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='班级大题统计表';
 
 
 # Dump of table s_basic_group
 # ------------------------------------------------------------
 
 DROP TABLE IF EXISTS `s_basic_group`;
-CREATE TABLE `s_basic_group` (
-  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
-  `exam_id` int(11) DEFAULT NULL COMMENT '考试ID',
-  `subject_code` varchar(255) DEFAULT NULL COMMENT '科目代码',
-  `subject_name` varchar(255) DEFAULT NULL COMMENT '科目名称',
-  `objective` tinyint(1) DEFAULT NULL COMMENT '是否客观题',
-  `group_name` varchar(255) DEFAULT NULL COMMENT '大题名称',
-  `group_number` int(11) DEFAULT NULL COMMENT '大题号',
-  `max_score` double DEFAULT NULL COMMENT '最高分',
-  `min_score` double DEFAULT NULL COMMENT '最低分',
-  `avg_score` double DEFAULT NULL COMMENT '平均分',
-  `total_score` double DEFAULT NULL COMMENT '满分',
-  `stdev` double DEFAULT NULL COMMENT '标准差',
-  `coefficient` double DEFAULT NULL COMMENT '差异系数',
-  `score_rate` double DEFAULT NULL COMMENT '得分率',
-  `full_count` int(11) DEFAULT NULL COMMENT '满分人数',
-  `zero_count` int(11) DEFAULT NULL COMMENT '零分人数',
-  `difficulty` double DEFAULT NULL COMMENT '难度',
-  `discrimination` double DEFAULT NULL COMMENT '区分度',
-  `question_count` int(11) DEFAULT NULL COMMENT '题目数量',
-  `reality_count` int(11) DEFAULT NULL COMMENT '有效数量',
-  `difficulity_level` varchar(255) DEFAULT NULL COMMENT '难度分布',
-  `discrimination_level` varchar(255) DEFAULT NULL COMMENT '区分度分布',
-  `paper_type` varchar(32) DEFAULT NULL COMMENT '试卷类型',
-  PRIMARY KEY (`id`),
-  KEY `index1` (`exam_id`,`subject_code`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='大题统计表';
+CREATE TABLE `s_basic_group`
+(
+    `id`                   int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `exam_id`              int(11)      DEFAULT NULL COMMENT '考试ID',
+    `subject_code`         varchar(255) DEFAULT NULL COMMENT '科目代码',
+    `subject_name`         varchar(255) DEFAULT NULL COMMENT '科目名称',
+    `objective`            tinyint(1)   DEFAULT NULL COMMENT '是否客观题',
+    `group_name`           varchar(255) DEFAULT NULL COMMENT '大题名称',
+    `group_number`         int(11)      DEFAULT NULL COMMENT '大题号',
+    `max_score`            double       DEFAULT NULL COMMENT '最高分',
+    `min_score`            double       DEFAULT NULL COMMENT '最低分',
+    `avg_score`            double       DEFAULT NULL COMMENT '平均分',
+    `total_score`          double       DEFAULT NULL COMMENT '满分',
+    `stdev`                double       DEFAULT NULL COMMENT '标准差',
+    `coefficient`          double       DEFAULT NULL COMMENT '差异系数',
+    `score_rate`           double       DEFAULT NULL COMMENT '得分率',
+    `full_count`           int(11)      DEFAULT NULL COMMENT '满分人数',
+    `zero_count`           int(11)      DEFAULT NULL COMMENT '零分人数',
+    `difficulty`           double       DEFAULT NULL COMMENT '难度',
+    `discrimination`       double       DEFAULT NULL COMMENT '区分度',
+    `question_count`       int(11)      DEFAULT NULL COMMENT '题目数量',
+    `reality_count`        int(11)      DEFAULT NULL COMMENT '有效数量',
+    `difficulity_level`    varchar(255) DEFAULT NULL COMMENT '难度分布',
+    `discrimination_level` varchar(255) DEFAULT NULL COMMENT '区分度分布',
+    `paper_type`           varchar(32)  DEFAULT NULL COMMENT '试卷类型',
+    PRIMARY KEY (`id`),
+    KEY `index1` (`exam_id`, `subject_code`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='大题统计表';
 
 
 # Dump of table s_basic_question
 # ------------------------------------------------------------
 
 DROP TABLE IF EXISTS `s_basic_question`;
-CREATE TABLE `s_basic_question` (
-  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
-  `exam_id` int(11) DEFAULT NULL COMMENT '考试ID',
-  `subject_code` varchar(255) DEFAULT NULL COMMENT '科目代码',
-  `subject_name` varchar(255) DEFAULT NULL COMMENT '科目名称',
-  `objective` tinyint(1) DEFAULT NULL COMMENT '是否客观题',
-  `question_name` varchar(128) DEFAULT NULL COMMENT '题目名称',
-  `paper_type` varchar(32) DEFAULT NULL COMMENT '试卷类型',
-  `main_number` int(11) NOT NULL COMMENT '大题号',
-  `sub_number` int(11) NOT NULL COMMENT '小题号',
-  `avg_score` double DEFAULT NULL COMMENT '平均分',
-  `total_score` double DEFAULT NULL COMMENT '满分',
-  `stdev` double DEFAULT NULL COMMENT '标准差',
-  `score_rate` double DEFAULT NULL COMMENT '得分率',
-  `full_score_rate` double DEFAULT NULL COMMENT '满分率',
-  `coefficient` double DEFAULT NULL COMMENT '差异系数',
-  `difficulty` double DEFAULT NULL COMMENT '难度',
-  `discrimination` double DEFAULT NULL COMMENT '区分度',
-  `max_score` double DEFAULT NULL COMMENT '最大分数',
-  `min_score` double DEFAULT NULL COMMENT '最小分数',
-  `reality_count` int(11) DEFAULT NULL COMMENT '有效数量',
-  `zero_count` int(11) DEFAULT NULL COMMENT '零分数量',
-  `options` varchar(255) DEFAULT NULL COMMENT '选项',
-  `answer` varchar(255) DEFAULT NULL COMMENT '答案',
-  `full_count` int(11) DEFAULT NULL COMMENT '满分数量',
-  PRIMARY KEY (`id`),
-  KEY `index1` (`exam_id`,`subject_code`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='小题统计表';
+CREATE TABLE `s_basic_question`
+(
+    `id`              int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `exam_id`         int(11)      DEFAULT NULL COMMENT '考试ID',
+    `subject_code`    varchar(255) DEFAULT NULL COMMENT '科目代码',
+    `subject_name`    varchar(255) DEFAULT NULL COMMENT '科目名称',
+    `objective`       tinyint(1)   DEFAULT NULL COMMENT '是否客观题',
+    `question_name`   varchar(128) DEFAULT NULL COMMENT '题目名称',
+    `paper_type`      varchar(32)  DEFAULT NULL COMMENT '试卷类型',
+    `main_number`     int(11) NOT NULL COMMENT '大题号',
+    `sub_number`      int(11) NOT NULL COMMENT '小题号',
+    `avg_score`       double       DEFAULT NULL COMMENT '平均分',
+    `total_score`     double       DEFAULT NULL COMMENT '满分',
+    `stdev`           double       DEFAULT NULL COMMENT '标准差',
+    `score_rate`      double       DEFAULT NULL COMMENT '得分率',
+    `full_score_rate` double       DEFAULT NULL COMMENT '满分率',
+    `coefficient`     double       DEFAULT NULL COMMENT '差异系数',
+    `difficulty`      double       DEFAULT NULL COMMENT '难度',
+    `discrimination`  double       DEFAULT NULL COMMENT '区分度',
+    `max_score`       double       DEFAULT NULL COMMENT '最大分数',
+    `min_score`       double       DEFAULT NULL COMMENT '最小分数',
+    `reality_count`   int(11)      DEFAULT NULL COMMENT '有效数量',
+    `zero_count`      int(11)      DEFAULT NULL COMMENT '零分数量',
+    `options`         varchar(255) DEFAULT NULL COMMENT '选项',
+    `answer`          varchar(255) DEFAULT NULL COMMENT '答案',
+    `full_count`      int(11)      DEFAULT NULL COMMENT '满分数量',
+    PRIMARY KEY (`id`),
+    KEY `index1` (`exam_id`, `subject_code`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='小题统计表';
 
 
 # Dump of table s_basic_subject
 # ------------------------------------------------------------
 
 DROP TABLE IF EXISTS `s_basic_subject`;
-CREATE TABLE `s_basic_subject` (
-  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
-  `exam_id` int(11) DEFAULT NULL COMMENT '考试ID',
-  `subject_code` varchar(255) DEFAULT NULL COMMENT '科目代码',
-  `subject_name` varchar(255) DEFAULT NULL COMMENT '科目名称',
-  `absent_count` int(11) DEFAULT NULL COMMENT '缺考人数',
-  `breach_count` int(11) DEFAULT NULL COMMENT '违纪人数',
-  `pass_count` int(11) DEFAULT NULL COMMENT '及格人数',
-  `pass_rate` double DEFAULT NULL COMMENT '及格率',
-  `reality_count` int(11) DEFAULT NULL COMMENT '有效人数',
-  `max_score` double DEFAULT NULL COMMENT '最高分',
-  `min_score` double DEFAULT NULL COMMENT '最低分',
-  `avg_score` double DEFAULT NULL COMMENT '平均分',
-  `total_count` int(11) DEFAULT NULL COMMENT '总人数',
-  `score_range` text COMMENT '分数分布',
-  `options` varchar(255) DEFAULT NULL COMMENT '选项',
-  `difficulity_level` varchar(255) DEFAULT NULL COMMENT '难度分布',
-  `discrimination_level` varchar(255) DEFAULT NULL COMMENT '区分度分布',
-  `range_level` varchar(255) DEFAULT NULL COMMENT '高低分段统计',
-  `coefficient` double DEFAULT NULL COMMENT '差异系数',
-  `difficulty` double DEFAULT NULL COMMENT '难度',
-  `discrimination` double DEFAULT NULL COMMENT '区分度',
-  `full_count` int(11) DEFAULT NULL COMMENT '满分人数',
-  `question_count` int(11) DEFAULT NULL COMMENT '题目数量',
-  `stdev` double DEFAULT NULL COMMENT '方差',
-  `total_score` double DEFAULT NULL COMMENT '满分',
-  `zero_count` int(11) DEFAULT NULL COMMENT '零分数量',
-  PRIMARY KEY (`id`),
-  KEY `index1` (`exam_id`,`subject_code`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='科目统计表';
+CREATE TABLE `s_basic_subject`
+(
+    `id`                   int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `exam_id`              int(11)      DEFAULT NULL COMMENT '考试ID',
+    `subject_code`         varchar(255) DEFAULT NULL COMMENT '科目代码',
+    `subject_name`         varchar(255) DEFAULT NULL COMMENT '科目名称',
+    `absent_count`         int(11)      DEFAULT NULL COMMENT '缺考人数',
+    `breach_count`         int(11)      DEFAULT NULL COMMENT '违纪人数',
+    `pass_count`           int(11)      DEFAULT NULL COMMENT '及格人数',
+    `pass_rate`            double       DEFAULT NULL COMMENT '及格率',
+    `reality_count`        int(11)      DEFAULT NULL COMMENT '有效人数',
+    `max_score`            double       DEFAULT NULL COMMENT '最高分',
+    `min_score`            double       DEFAULT NULL COMMENT '最低分',
+    `avg_score`            double       DEFAULT NULL COMMENT '平均分',
+    `total_count`          int(11)      DEFAULT NULL COMMENT '总人数',
+    `score_range`          text COMMENT '分数分布',
+    `options`              varchar(255) DEFAULT NULL COMMENT '选项',
+    `difficulity_level`    varchar(255) DEFAULT NULL COMMENT '难度分布',
+    `discrimination_level` varchar(255) DEFAULT NULL COMMENT '区分度分布',
+    `range_level`          varchar(255) DEFAULT NULL COMMENT '高低分段统计',
+    `coefficient`          double       DEFAULT NULL COMMENT '差异系数',
+    `difficulty`           double       DEFAULT NULL COMMENT '难度',
+    `discrimination`       double       DEFAULT NULL COMMENT '区分度',
+    `full_count`           int(11)      DEFAULT NULL COMMENT '满分人数',
+    `question_count`       int(11)      DEFAULT NULL COMMENT '题目数量',
+    `stdev`                double       DEFAULT NULL COMMENT '方差',
+    `total_score`          double       DEFAULT NULL COMMENT '满分',
+    `zero_count`           int(11)      DEFAULT NULL COMMENT '零分数量',
+    PRIMARY KEY (`id`),
+    KEY `index1` (`exam_id`, `subject_code`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='科目统计表';
 
 
 # Dump of table s_basic_subject_class
 # ------------------------------------------------------------
 
 DROP TABLE IF EXISTS `s_basic_subject_class`;
-CREATE TABLE `s_basic_subject_class` (
-  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
-  `exam_id` int(11) DEFAULT NULL COMMENT '考试ID',
-  `subject_code` varchar(255) DEFAULT NULL COMMENT '科目代码',
-  `subject_name` varchar(255) DEFAULT NULL COMMENT '科目名称',
-  `class_name` varchar(255) DEFAULT NULL COMMENT '班级名称',
-  `excellent_count` int(11) DEFAULT NULL COMMENT '优秀人数',
-  `excellent_rate` double DEFAULT NULL  COMMENT '优秀率',
-  `pass_count` int(11) DEFAULT NULL COMMENT '及格人数',
-  `pass_rate` double DEFAULT NULL COMMENT '及格率',
-  `max_score` double DEFAULT NULL COMMENT '最高分',
-  `min_score` double DEFAULT NULL COMMENT '最低分',
-  `avg_score` double DEFAULT NULL COMMENT '平均分',
-  `coefficient` double DEFAULT NULL COMMENT '差异系数',
-  `difficulty` double DEFAULT NULL COMMENT '难度',
-  `score_range` text DEFAULT NULL COMMENT '分数分布',
-  `stdev` double DEFAULT NULL COMMENT '方差',
-  `difficulity_level` varchar(255) DEFAULT NULL COMMENT '难度分布',
-  `discrimination_level` varchar(255) DEFAULT NULL COMMENT '区分度分布',
-  `range_level` varchar(255) DEFAULT NULL COMMENT '高低分分布',
-  `reality_count` int(11) DEFAULT NULL COMMENT '有效数量',
-  PRIMARY KEY (`id`),
-  KEY `index1` (`exam_id`,`subject_code`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='班级统计表';
+CREATE TABLE `s_basic_subject_class`
+(
+    `id`                   int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `exam_id`              int(11)      DEFAULT NULL COMMENT '考试ID',
+    `subject_code`         varchar(255) DEFAULT NULL COMMENT '科目代码',
+    `subject_name`         varchar(255) DEFAULT NULL COMMENT '科目名称',
+    `class_name`           varchar(255) DEFAULT NULL COMMENT '班级名称',
+    `excellent_count`      int(11)      DEFAULT NULL COMMENT '优秀人数',
+    `excellent_rate`       double       DEFAULT NULL COMMENT '优秀率',
+    `pass_count`           int(11)      DEFAULT NULL COMMENT '及格人数',
+    `pass_rate`            double       DEFAULT NULL COMMENT '及格率',
+    `max_score`            double       DEFAULT NULL COMMENT '最高分',
+    `min_score`            double       DEFAULT NULL COMMENT '最低分',
+    `avg_score`            double       DEFAULT NULL COMMENT '平均分',
+    `coefficient`          double       DEFAULT NULL COMMENT '差异系数',
+    `difficulty`           double       DEFAULT NULL COMMENT '难度',
+    `score_range`          text         DEFAULT NULL COMMENT '分数分布',
+    `stdev`                double       DEFAULT NULL COMMENT '方差',
+    `difficulity_level`    varchar(255) DEFAULT NULL COMMENT '难度分布',
+    `discrimination_level` varchar(255) DEFAULT NULL COMMENT '区分度分布',
+    `range_level`          varchar(255) DEFAULT NULL COMMENT '高低分分布',
+    `reality_count`        int(11)      DEFAULT NULL COMMENT '有效数量',
+    PRIMARY KEY (`id`),
+    KEY `index1` (`exam_id`, `subject_code`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='班级统计表';
 
 
 # Dump of table s_basic_subject_college
 # ------------------------------------------------------------
 
 DROP TABLE IF EXISTS `s_basic_subject_college`;
-CREATE TABLE `s_basic_subject_college` (
-  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
-  `exam_id` int(11) DEFAULT NULL COMMENT '考试ID',
-  `subject_code` varchar(255) DEFAULT NULL COMMENT '科目代码',
-  `subject_name` varchar(255) DEFAULT NULL COMMENT '科目名称',
-  `college_name` varchar(255) DEFAULT NULL COMMENT '学院名称',
-  `excellent_count` int(11) DEFAULT NULL COMMENT '优秀人数',
-  `excellent_rate` double DEFAULT NULL COMMENT '优秀率',
-  `pass_count` int(11) DEFAULT NULL COMMENT '及格人数',
-  `pass_rate` double DEFAULT NULL COMMENT '及格率',
-  `max_score` double DEFAULT NULL COMMENT '最高分',
-  `min_score` double DEFAULT NULL COMMENT '最低分',
-  `avg_score` double DEFAULT NULL COMMENT '平均分',
-  PRIMARY KEY (`id`),
-  KEY `index1` (`exam_id`,`subject_code`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='学院统计表';
+CREATE TABLE `s_basic_subject_college`
+(
+    `id`              int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `exam_id`         int(11)      DEFAULT NULL COMMENT '考试ID',
+    `subject_code`    varchar(255) DEFAULT NULL COMMENT '科目代码',
+    `subject_name`    varchar(255) DEFAULT NULL COMMENT '科目名称',
+    `college_name`    varchar(255) DEFAULT NULL COMMENT '学院名称',
+    `excellent_count` int(11)      DEFAULT NULL COMMENT '优秀人数',
+    `excellent_rate`  double       DEFAULT NULL COMMENT '优秀率',
+    `pass_count`      int(11)      DEFAULT NULL COMMENT '及格人数',
+    `pass_rate`       double       DEFAULT NULL COMMENT '及格率',
+    `max_score`       double       DEFAULT NULL COMMENT '最高分',
+    `min_score`       double       DEFAULT NULL COMMENT '最低分',
+    `avg_score`       double       DEFAULT NULL COMMENT '平均分',
+    PRIMARY KEY (`id`),
+    KEY `index1` (`exam_id`, `subject_code`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='学院统计表';
 
 
 # Dump of table s_basic_subject_teacher
 # ------------------------------------------------------------
 
 DROP TABLE IF EXISTS `s_basic_subject_teacher`;
-CREATE TABLE `s_basic_subject_teacher` (
-  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
-  `exam_id` int(11) DEFAULT NULL COMMENT '考试ID',
-  `subject_code` varchar(255) DEFAULT NULL COMMENT '科目代码',
-  `subject_name` varchar(255) DEFAULT NULL COMMENT '科目名称',
-  `teacher_name` varchar(255) DEFAULT NULL COMMENT '任课老师',
-  `excellent_count` int(11) DEFAULT NULL COMMENT '优秀人数',
-  `excellent_rate` double DEFAULT NULL COMMENT '优秀率',
-  `pass_count` int(11) DEFAULT NULL COMMENT '及格人数',
-  `pass_rate` double DEFAULT NULL COMMENT '及格率',
-  `max_score` double DEFAULT NULL COMMENT '最高分',
-  `min_score` double DEFAULT NULL COMMENT '最低分',
-  `avg_score` double DEFAULT NULL COMMENT '平均分',
-  `reality_count` int(11) DEFAULT NULL COMMENT '有效人数',
-  `relative_avg_score` double DEFAULT NULL COMMENT '平均相对分',
-  PRIMARY KEY (`id`),
-  KEY `index1` (`exam_id`,`subject_code`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='任课老师统计表';
+CREATE TABLE `s_basic_subject_teacher`
+(
+    `id`                 int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `exam_id`            int(11)      DEFAULT NULL COMMENT '考试ID',
+    `subject_code`       varchar(255) DEFAULT NULL COMMENT '科目代码',
+    `subject_name`       varchar(255) DEFAULT NULL COMMENT '科目名称',
+    `teacher_name`       varchar(255) DEFAULT NULL COMMENT '任课老师',
+    `excellent_count`    int(11)      DEFAULT NULL COMMENT '优秀人数',
+    `excellent_rate`     double       DEFAULT NULL COMMENT '优秀率',
+    `pass_count`         int(11)      DEFAULT NULL COMMENT '及格人数',
+    `pass_rate`          double       DEFAULT NULL COMMENT '及格率',
+    `max_score`          double       DEFAULT NULL COMMENT '最高分',
+    `min_score`          double       DEFAULT NULL COMMENT '最低分',
+    `avg_score`          double       DEFAULT NULL COMMENT '平均分',
+    `reality_count`      int(11)      DEFAULT NULL COMMENT '有效人数',
+    `relative_avg_score` double       DEFAULT NULL COMMENT '平均相对分',
+    PRIMARY KEY (`id`),
+    KEY `index1` (`exam_id`, `subject_code`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='任课老师统计表';
 
 
 # Dump of table s_basic_subject_teacher_class
 # ------------------------------------------------------------
 
 DROP TABLE IF EXISTS `s_basic_subject_teacher_class`;
-CREATE TABLE `s_basic_subject_teacher_class` (
-  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
-  `exam_id` int(11) DEFAULT NULL COMMENT '考试ID',
-  `subject_code` varchar(255) DEFAULT NULL COMMENT '科目代码',
-  `subject_name` varchar(255) DEFAULT NULL COMMENT '科目名称',
-  `teacher_name` varchar(255) DEFAULT NULL COMMENT '任课老师',
-  `class_name` varchar(255) DEFAULT NULL COMMENT '班级名称',
-  `excellent_count` int(11) DEFAULT NULL COMMENT '优秀人数',
-  `excellent_rate` double DEFAULT NULL COMMENT '优秀率',
-  `pass_count` int(11) DEFAULT NULL COMMENT '及格人数',
-  `pass_rate` double DEFAULT NULL COMMENT '及格率',
-  `max_score` double DEFAULT NULL COMMENT '最高分',
-  `min_score` double DEFAULT NULL COMMENT '最低分',
-  `avg_score` double DEFAULT NULL COMMENT '平均分',
-  `reality_count` int(11) DEFAULT NULL COMMENT '有效人数',
-  `relative_avg_score` double DEFAULT NULL COMMENT '平均相对分',
-  PRIMARY KEY (`id`),
-  KEY `index1` (`exam_id`,`subject_code`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='任课老师班级统计表';
+CREATE TABLE `s_basic_subject_teacher_class`
+(
+    `id`                 int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `exam_id`            int(11)      DEFAULT NULL COMMENT '考试ID',
+    `subject_code`       varchar(255) DEFAULT NULL COMMENT '科目代码',
+    `subject_name`       varchar(255) DEFAULT NULL COMMENT '科目名称',
+    `teacher_name`       varchar(255) DEFAULT NULL COMMENT '任课老师',
+    `class_name`         varchar(255) DEFAULT NULL COMMENT '班级名称',
+    `excellent_count`    int(11)      DEFAULT NULL COMMENT '优秀人数',
+    `excellent_rate`     double       DEFAULT NULL COMMENT '优秀率',
+    `pass_count`         int(11)      DEFAULT NULL COMMENT '及格人数',
+    `pass_rate`          double       DEFAULT NULL COMMENT '及格率',
+    `max_score`          double       DEFAULT NULL COMMENT '最高分',
+    `min_score`          double       DEFAULT NULL COMMENT '最低分',
+    `avg_score`          double       DEFAULT NULL COMMENT '平均分',
+    `reality_count`      int(11)      DEFAULT NULL COMMENT '有效人数',
+    `relative_avg_score` double       DEFAULT NULL COMMENT '平均相对分',
+    PRIMARY KEY (`id`),
+    KEY `index1` (`exam_id`, `subject_code`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='任课老师班级统计表';
 
 
 # Dump of table s_range_subject
 # ------------------------------------------------------------
 
 DROP TABLE IF EXISTS `s_range_subject`;
-CREATE TABLE `s_range_subject` (
-  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
-  `exam_id` int(11) DEFAULT NULL COMMENT '考试ID',
-  `subject_code` varchar(255) DEFAULT NULL COMMENT '科目代码',
-  `subject_name` varchar(255) DEFAULT NULL COMMENT '科目名称',
-  `subject_level` varchar(255) DEFAULT NULL COMMENT '科目层次',
-  `totalCount` int(11) DEFAULT NULL COMMENT '总人数',
-  `count0_49` int(11) DEFAULT NULL COMMENT '0-49',
-  `count50_59` int(11) DEFAULT NULL COMMENT '50-59',
-  `count60_69` int(11) DEFAULT NULL COMMENT '60-69',
-  `count70_79` int(11) DEFAULT NULL COMMENT '70-79',
-  `count80_89` int(11) DEFAULT NULL COMMENT '80-89',
-  `count90_100` int(11) DEFAULT NULL COMMENT '90-100',
-  `lt60` int(11) DEFAULT NULL COMMENT '<60',
-  `mte60` int(11) DEFAULT NULL COMMENT '>=60',
-  `percent0_49` double NOT NULL COMMENT '0-49占比',
-  `percent50_59` double NOT NULL COMMENT '50-59占比',
-  `percent60_69` double NOT NULL COMMENT '60-69占比',
-  `percent70_79` double NOT NULL COMMENT '70-79占比',
-  `percent80_89` double NOT NULL COMMENT '80-89占比',
-  `percent90_100` double NOT NULL COMMENT '90-100占比',
-  `percent_lt60` double NOT NULL COMMENT '<60占比',
-  `percent_mte60` double NOT NULL COMMENT '>=60占比',
-  PRIMARY KEY (`id`),
-  KEY `index1` (`exam_id`,`subject_code`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='分段统计表';
+CREATE TABLE `s_range_subject`
+(
+    `id`            int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `exam_id`       int(11)      DEFAULT NULL COMMENT '考试ID',
+    `subject_code`  varchar(255) DEFAULT NULL COMMENT '科目代码',
+    `subject_name`  varchar(255) DEFAULT NULL COMMENT '科目名称',
+    `subject_level` varchar(255) DEFAULT NULL COMMENT '科目层次',
+    `totalCount`    int(11)      DEFAULT NULL COMMENT '总人数',
+    `count0_49`     int(11)      DEFAULT NULL COMMENT '0-49',
+    `count50_59`    int(11)      DEFAULT NULL COMMENT '50-59',
+    `count60_69`    int(11)      DEFAULT NULL COMMENT '60-69',
+    `count70_79`    int(11)      DEFAULT NULL COMMENT '70-79',
+    `count80_89`    int(11)      DEFAULT NULL COMMENT '80-89',
+    `count90_100`   int(11)      DEFAULT NULL COMMENT '90-100',
+    `lt60`          int(11)      DEFAULT NULL COMMENT '<60',
+    `mte60`         int(11)      DEFAULT NULL COMMENT '>=60',
+    `percent0_49`   double  NOT NULL COMMENT '0-49占比',
+    `percent50_59`  double  NOT NULL COMMENT '50-59占比',
+    `percent60_69`  double  NOT NULL COMMENT '60-69占比',
+    `percent70_79`  double  NOT NULL COMMENT '70-79占比',
+    `percent80_89`  double  NOT NULL COMMENT '80-89占比',
+    `percent90_100` double  NOT NULL COMMENT '90-100占比',
+    `percent_lt60`  double  NOT NULL COMMENT '<60占比',
+    `percent_mte60` double  NOT NULL COMMENT '>=60占比',
+    PRIMARY KEY (`id`),
+    KEY `index1` (`exam_id`, `subject_code`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='分段统计表';