Explorar o código

Merge remote-tracking branch 'origin/dev_v2.1.0' into dev_v2.1.0

caozixuan %!s(int64=4) %!d(string=hai) anos
pai
achega
bb3ac00fa9
Modificáronse 18 ficheiros con 381 adicións e 39 borrados
  1. 39 10
      distributed-print/src/main/java/com/qmth/distributed/print/auth/DistributedPrintAuthenticationService.java
  2. 1 0
      distributed-print/src/main/resources/application-dev.properties
  3. 1 0
      distributed-print/src/main/resources/application-release.properties
  4. 1 0
      distributed-print/src/main/resources/application-test.properties
  5. 11 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/domain/SysDomain.java
  6. 6 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/entity/TBSession.java
  7. 0 1
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/service/CallApiOrgCenterService.java
  8. 9 6
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/service/impl/CallApiOrgCenterServiceImpl.java
  9. 4 4
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/service/impl/OrgCenterDataDisposeServiceImpl.java
  10. 10 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/RedisUtil.java
  11. 4 0
      teachcloud-report-business/src/main/java/com/qmth/teachcloud/report/business/mapper/CourseReportMapper.java
  12. 23 0
      teachcloud-report-business/src/main/resources/mapper/CourseReportMapper.xml
  13. 3 0
      teachcloud-report/src/main/java/com/qmth/teachcloud/report/api/WudaOpenApiController.java
  14. 39 10
      teachcloud-report/src/main/java/com/qmth/teachcloud/report/auth/TeachcloudReportAuthenticationService.java
  15. 1 0
      teachcloud-report/src/main/resources/application-dev.properties
  16. 9 8
      teachcloud-report/src/main/resources/application-release.properties
  17. 1 0
      teachcloud-report/src/main/resources/application-test.properties
  18. 219 0
      teachcloud-report/src/test/java/com/qmth/teachcloud/report/XfTest.java

+ 39 - 10
distributed-print/src/main/java/com/qmth/distributed/print/auth/DistributedPrintAuthenticationService.java

@@ -1,9 +1,11 @@
 package com.qmth.distributed.print.auth;
 
+import com.qmth.boot.core.enums.Platform;
 import com.qmth.boot.core.security.model.AccessEntity;
 import com.qmth.boot.core.security.service.AuthorizationService;
 import com.qmth.boot.tools.signature.SignatureType;
 import com.qmth.teachcloud.common.bean.auth.AuthBean;
+import com.qmth.teachcloud.common.config.DictionaryConfig;
 import com.qmth.teachcloud.common.contant.SystemConstant;
 import com.qmth.teachcloud.common.entity.SysUser;
 import com.qmth.teachcloud.common.entity.TBSession;
@@ -11,7 +13,7 @@ import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
 import com.qmth.teachcloud.common.enums.PrivilegePropertyEnum;
 import com.qmth.teachcloud.common.enums.RoleTypeEnum;
 import com.qmth.teachcloud.common.service.CacheService;
-import com.qmth.teachcloud.common.service.TBSessionService;
+import com.qmth.teachcloud.common.util.RedisUtil;
 import com.qmth.teachcloud.common.util.ServletUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -29,10 +31,13 @@ public class DistributedPrintAuthenticationService implements AuthorizationServi
     private final static Logger log = LoggerFactory.getLogger(DistributedPrintAuthenticationService.class);
 
     @Resource
-    TBSessionService tbSessionService;
+    CacheService cacheService;
 
     @Resource
-    CacheService cacheService;
+    RedisUtil redisUtil;
+
+    @Resource
+    DictionaryConfig dictionaryConfig;
 
     @Override
     public AccessEntity findByIdentity(String identity, SignatureType signatureType, String path) {
@@ -42,12 +47,24 @@ public class DistributedPrintAuthenticationService implements AuthorizationServi
     @Override
     public boolean hasPermission(AccessEntity accessEntity, String path) {
         if (Objects.nonNull(accessEntity) && Objects.nonNull(accessEntity.getIdentity())) {
-            TBSession tbSession = tbSessionService.getById(accessEntity.getIdentity());
-            List<String> privilegeUrl = cacheService.privilegeUrlCache(PrivilegePropertyEnum.NO_AUTH);
-            //无需鉴权的url
-            int noAuthCount = Objects.nonNull(privilegeUrl) ? (int) privilegeUrl.stream().filter(s -> s.equalsIgnoreCase(path)).count() : 0;
-            if (noAuthCount > 0) {
-                return true;
+            TBSession tbSession = (TBSession) redisUtil.getUserSession(accessEntity.getIdentity());
+            if (Objects.isNull(tbSession)) {
+                log.warn("Authorization faile: session id not exists: {}", accessEntity.getIdentity());
+                throw ExceptionResultEnum.NOT_LOGIN.exception();
+            }
+            if (tbSession.getExpireTime() <= System.currentTimeMillis()) {
+                log.warn("Authorization faile: session has expired, expire time={}", tbSession.getExpireTime());
+                throw ExceptionResultEnum.NOT_LOGIN.exception();
+            }
+            Platform platform = ServletUtil.getRequestPlatform();
+            String deviceId = ServletUtil.getRequestDeviceId();
+            if (!tbSession.getPlatform().equalsIgnoreCase(platform.name())) {
+                log.warn("Authorization faile: platform invalid, session platform is {}", tbSession.getPlatform());
+                throw ExceptionResultEnum.AUTHORIZATION_ERROR.exception();
+            }
+            if (!tbSession.getDeviceId().equalsIgnoreCase(deviceId)) {
+                log.warn("Authorization faile: deviceId invalid, session deviceId is {} ", tbSession.getDeviceId());
+                throw ExceptionResultEnum.AUTHORIZATION_ERROR.exception();
             }
             Long userId = Long.parseLong(tbSession.getIdentity());
             SysUser sysUser = cacheService.userCache(userId);
@@ -55,7 +72,19 @@ public class DistributedPrintAuthenticationService implements AuthorizationServi
             HttpServletResponse response = ServletUtil.getResponse();
             request.setAttribute(SystemConstant.SESSION, tbSession);
             request.setAttribute(SystemConstant.USER, sysUser);
-            return authFootCommon(userId, SystemConstant.USER_OAUTH_CACHE, path, request, response);
+            boolean auth = authFootCommon(userId, SystemConstant.USER_OAUTH_CACHE, path, request, response);
+            if (auth) {
+                Long expireTime = redisUtil.getUserSessionExpire(accessEntity.getIdentity());
+                if (Objects.nonNull(expireTime) && expireTime.longValue() > -1L) {
+                    if (Objects.nonNull(tbSession.getLastAccessTime()) && (System.currentTimeMillis() - tbSession.getLastAccessTime()) / 1000 > dictionaryConfig.sysDomain().getSessionActive().getSeconds()) {
+                        log.warn("Authorization faile: session active, session active is {}", dictionaryConfig.sysDomain().getSessionActive().getSeconds());
+                        throw ExceptionResultEnum.NOT_LOGIN.exception();
+                    }
+                    tbSession.setLastInfo();
+                    redisUtil.setUserSession(accessEntity.getIdentity(), tbSession, expireTime);
+                }
+            }
+            return auth;
         }
         return false;
     }

+ 1 - 0
distributed-print/src/main/resources/application-dev.properties

@@ -56,6 +56,7 @@ sys.config.htmlToPdfUrl=/usr/local/bin/wkhtmltopdf
 sys.config.autoCreatePdfResetMaxCount=5
 sys.config.threadPoolCoreSize=1
 sys.config.customThreadPoolCoreSize=true
+sys.config.sessionActive=4h
 spring.resources.static-locations=file:${sys.config.serverUpload},classpath:/META-INF/resources/,classpath:/resources/
 
 org.center.url=https://solar.qmth.com.cn

+ 1 - 0
distributed-print/src/main/resources/application-release.properties

@@ -56,6 +56,7 @@ sys.config.htmlToPdfUrl=/usr/local/bin/wkhtmltopdf
 sys.config.autoCreatePdfResetMaxCount=5
 sys.config.threadPoolCoreSize=1
 sys.config.customThreadPoolCoreSize=true
+sys.config.sessionActive=4h
 spring.resources.static-locations=file:${sys.config.serverUpload},classpath:/META-INF/resources/,classpath:/resources/
 
 org.center.url=https://solar.qmth.com.cn

+ 1 - 0
distributed-print/src/main/resources/application-test.properties

@@ -56,6 +56,7 @@ sys.config.htmlToPdfUrl=/usr/local/bin/wkhtmltopdf
 sys.config.autoCreatePdfResetMaxCount=5
 sys.config.threadPoolCoreSize=1
 sys.config.customThreadPoolCoreSize=true
+sys.config.sessionActive=4h
 spring.resources.static-locations=file:${sys.config.serverUpload},classpath:/META-INF/resources/,classpath:/resources/
 
 org.center.url=https://solar.qmth.com.cn

+ 11 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/domain/SysDomain.java

@@ -1,6 +1,7 @@
 package com.qmth.teachcloud.common.domain;
 
 import java.io.Serializable;
+import java.time.Duration;
 import java.util.List;
 
 /**
@@ -42,6 +43,16 @@ public class SysDomain implements Serializable {
 
     String reportUrl;
 
+    Duration sessionActive;
+
+    public Duration getSessionActive() {
+        return sessionActive;
+    }
+
+    public void setSessionActive(Duration sessionActive) {
+        this.sessionActive = sessionActive;
+    }
+
     public String getReportUrl() {
         return reportUrl;
     }

+ 6 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/entity/TBSession.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.qmth.teachcloud.common.util.ServletUtil;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
@@ -92,6 +93,11 @@ public class TBSession implements Serializable {
         this.lastAccessTime = System.currentTimeMillis();
     }
 
+    public void setLastInfo() {
+        this.lastAccessTime = System.currentTimeMillis();
+        this.lastAccessIp = ServletUtil.getRequest().getLocalAddr();
+    }
+
     public String getId() {
         return id;
     }

+ 0 - 1
teachcloud-common/src/main/java/com/qmth/teachcloud/common/service/CallApiOrgCenterService.java

@@ -17,5 +17,4 @@ public interface CallApiOrgCenterService {
      * @throws IOException 异常
      */
     List<Map> callOrgInfo() throws IOException;
-
 }

+ 9 - 6
teachcloud-common/src/main/java/com/qmth/teachcloud/common/service/impl/CallApiOrgCenterServiceImpl.java

@@ -28,17 +28,20 @@ public class CallApiOrgCenterServiceImpl implements CallApiOrgCenterService {
     @Transactional(rollbackFor = Exception.class)
     @Override
     public List<Map> callOrgInfo() throws IOException {
+        List<Map> orgList = null;
+        if ((Objects.isNull(dictionaryConfig.sysDomain().getAccessKey()) || Objects.equals(dictionaryConfig.sysDomain().getAccessKey(), ""))
+                && (Objects.isNull(dictionaryConfig.sysDomain().getAccessSecret()) || Objects.equals(dictionaryConfig.sysDomain().getAccessSecret(), ""))) {
+            return orgList;
+        }
         String url = dictionaryConfig.orgCenterDomain().getUrl() + dictionaryConfig.orgCenterDomain().getOrgQueryApi();
 
-        Map<String,Object> params = new HashMap<>();
+        Map<String, Object> params = new HashMap<>();
         int pageNumber = 1;
         int pageSize = 100;
 
-        List<Map> orgList = null;
-
-        while (true){
-            params.put("pageNumber",pageNumber);
-            params.put("pageSize",pageSize);
+        while (true) {
+            params.put("pageNumber", pageNumber);
+            params.put("pageSize", pageSize);
             long timestamp = System.currentTimeMillis();
             String accessToken = SignatureEntity.build(SignatureType.SECRET, SystemConstant.METHOD, dictionaryConfig.orgCenterDomain().getOrgQueryApi(), timestamp, dictionaryConfig.sysDomain().getAccessKey(), dictionaryConfig.sysDomain().getAccessSecret());
             String result = HttpUtil.post(url, params, accessToken, timestamp);

+ 4 - 4
teachcloud-common/src/main/java/com/qmth/teachcloud/common/service/impl/OrgCenterDataDisposeServiceImpl.java

@@ -15,10 +15,7 @@ import org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.Resource;
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.stream.Collectors;
 
 /**
@@ -37,6 +34,9 @@ public class OrgCenterDataDisposeServiceImpl implements OrgCenterDataDisposeServ
     @Override
     public void updateSchoolInfo() throws IOException {
         List<Map> result = callApiOrgCenterService.callOrgInfo();
+        if (Objects.isNull(result) || result.size() == 0) {
+            return;
+        }
         // 删除原有学校
         List<BasicSchool> basicSchoolList = basicSchoolService.list();
         List<Long> oldIdList = basicSchoolList.stream().map(BasicSchool::getId).collect(Collectors.toList());

+ 10 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/RedisUtil.java

@@ -53,6 +53,16 @@ public class RedisUtil {
         redisTemplate.opsForValue().set(SystemConstant.SESSION + sessionId, o, time, TimeUnit.SECONDS);
     }
 
+    /**
+     * 获取过期时间
+     *
+     * @param sessionId
+     * @return
+     */
+    public Long getUserSessionExpire(String sessionId) {
+        return redisTemplate.opsForValue().getOperations().getExpire(SystemConstant.SESSION + sessionId);
+    }
+
     /**
      * 批量获取key的value
      *

+ 4 - 0
teachcloud-report-business/src/main/java/com/qmth/teachcloud/report/business/mapper/CourseReportMapper.java

@@ -6,6 +6,7 @@ import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * @Description:
@@ -17,4 +18,7 @@ public interface CourseReportMapper {
     List<ExamBaseInfo> findExamTotalByExamId(@Param("examId") Long examId);
 
     List<CollegeCourseInfo> findCollegeCourseInfo(@Param("examId") Long examId,@Param("collegeId") Long collegeId);
+
+    List<Map<String, Object>> list1();
+
 }

+ 23 - 0
teachcloud-report-business/src/main/resources/mapper/CourseReportMapper.xml

@@ -55,4 +55,27 @@
         WHERE
             exam_id = #{examId}
     </select>
+    <select id="list1" resultType="java.util.Map">
+        SELECT
+	d.exam_student_id id,
+	e.small_question_number num,
+	t.score,
+	e.full_score,
+	f.inspect_college_id
+FROM
+	(
+SELECT
+	*
+FROM
+	t_b_answer a
+WHERE
+	a.exam_record_id IN ( SELECT id FROM t_b_exam_record b WHERE b.exam_student_id IN ( SELECT id FROM t_b_exam_student WHERE course_code = 1001 ) )
+	) t
+	JOIN t_b_exam_record d ON t.exam_record_id = d.id
+	JOIN t_b_exam_student f ON d.exam_student_id = f.id
+	JOIN t_b_paper_struct e ON d.paper_id = e.paper_id
+	AND t.number_type = e.number_type
+	AND t.main_number = e.big_question_number
+	AND t.sub_number = e.small_question_number
+    </select>
 </mapper>

+ 3 - 0
teachcloud-report/src/main/java/com/qmth/teachcloud/report/api/WudaOpenApiController.java

@@ -1,5 +1,7 @@
 package com.qmth.teachcloud.report.api;
 
+import com.qmth.boot.api.annotation.Aac;
+import com.qmth.boot.api.annotation.BOOL;
 import com.qmth.boot.api.constant.ApiConstant;
 import com.qmth.teachcloud.common.config.DictionaryConfig;
 import com.qmth.teachcloud.common.entity.SysUser;
@@ -31,6 +33,7 @@ public class WudaOpenApiController {
     @ApiOperation(value = "cas鉴权接口")
     @RequestMapping(value = "/authentication", method = RequestMethod.GET)
     @ApiResponses({@ApiResponse(code = 200, message = "返回消息", response = Result.class)})
+    @Aac(auth = BOOL.FALSE)
     public void authentication(HttpServletRequest request, HttpServletResponse response) throws IOException {
         String uid = request.getRemoteUser();
         if (Objects.isNull(uid)) {

+ 39 - 10
teachcloud-report/src/main/java/com/qmth/teachcloud/report/auth/TeachcloudReportAuthenticationService.java

@@ -1,9 +1,11 @@
 package com.qmth.teachcloud.report.auth;
 
+import com.qmth.boot.core.enums.Platform;
 import com.qmth.boot.core.security.model.AccessEntity;
 import com.qmth.boot.core.security.service.AuthorizationService;
 import com.qmth.boot.tools.signature.SignatureType;
 import com.qmth.teachcloud.common.bean.auth.AuthBean;
+import com.qmth.teachcloud.common.config.DictionaryConfig;
 import com.qmth.teachcloud.common.contant.SystemConstant;
 import com.qmth.teachcloud.common.entity.SysUser;
 import com.qmth.teachcloud.common.entity.TBSession;
@@ -11,7 +13,7 @@ import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
 import com.qmth.teachcloud.common.enums.PrivilegePropertyEnum;
 import com.qmth.teachcloud.common.enums.RoleTypeEnum;
 import com.qmth.teachcloud.common.service.CacheService;
-import com.qmth.teachcloud.common.service.TBSessionService;
+import com.qmth.teachcloud.common.util.RedisUtil;
 import com.qmth.teachcloud.common.util.ServletUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -29,10 +31,13 @@ public class TeachcloudReportAuthenticationService implements AuthorizationServi
     private final static Logger log = LoggerFactory.getLogger(TeachcloudReportAuthenticationService.class);
 
     @Resource
-    TBSessionService tbSessionService;
+    CacheService cacheService;
 
     @Resource
-    CacheService cacheService;
+    RedisUtil redisUtil;
+
+    @Resource
+    DictionaryConfig dictionaryConfig;
 
     @Override
     public AccessEntity findByIdentity(String identity, SignatureType signatureType, String path) {
@@ -42,12 +47,24 @@ public class TeachcloudReportAuthenticationService implements AuthorizationServi
     @Override
     public boolean hasPermission(AccessEntity accessEntity, String path) {
         if (Objects.nonNull(accessEntity) && Objects.nonNull(accessEntity.getIdentity())) {
-            TBSession tbSession = tbSessionService.getById(accessEntity.getIdentity());
-            List<String> privilegeUrl = cacheService.privilegeUrlCache(PrivilegePropertyEnum.NO_AUTH);
-            //无需鉴权的url
-            int noAuthCount = Objects.nonNull(privilegeUrl) ? (int) privilegeUrl.stream().filter(s -> s.equalsIgnoreCase(path)).count() : 0;
-            if (noAuthCount > 0) {
-                return true;
+            TBSession tbSession = (TBSession) redisUtil.getUserSession(accessEntity.getIdentity());
+            if (Objects.isNull(tbSession)) {
+                log.warn("Authorization faile: session id not exists: {}", accessEntity.getIdentity());
+                throw ExceptionResultEnum.NOT_LOGIN.exception();
+            }
+            if (tbSession.getExpireTime() <= System.currentTimeMillis()) {
+                log.warn("Authorization faile: session has expired, expire time={}", tbSession.getExpireTime());
+                throw ExceptionResultEnum.NOT_LOGIN.exception();
+            }
+            Platform platform = ServletUtil.getRequestPlatform();
+            String deviceId = ServletUtil.getRequestDeviceId();
+            if (!tbSession.getPlatform().equalsIgnoreCase(platform.name())) {
+                log.warn("Authorization faile: platform invalid, session platform is {}", tbSession.getPlatform());
+                throw ExceptionResultEnum.AUTHORIZATION_ERROR.exception();
+            }
+            if (!tbSession.getDeviceId().equalsIgnoreCase(deviceId)) {
+                log.warn("Authorization faile: deviceId invalid, session deviceId is {} ", tbSession.getDeviceId());
+                throw ExceptionResultEnum.AUTHORIZATION_ERROR.exception();
             }
             Long userId = Long.parseLong(tbSession.getIdentity());
             SysUser sysUser = cacheService.userCache(userId);
@@ -55,7 +72,19 @@ public class TeachcloudReportAuthenticationService implements AuthorizationServi
             HttpServletResponse response = ServletUtil.getResponse();
             request.setAttribute(SystemConstant.SESSION, tbSession);
             request.setAttribute(SystemConstant.USER, sysUser);
-            return authFootCommon(userId, SystemConstant.USER_OAUTH_CACHE, path, request, response);
+            boolean auth = authFootCommon(userId, SystemConstant.USER_OAUTH_CACHE, path, request, response);
+            if (auth) {
+                Long expireTime = redisUtil.getUserSessionExpire(accessEntity.getIdentity());
+                if (Objects.nonNull(expireTime) && expireTime.longValue() > -1L) {
+                    if (Objects.nonNull(tbSession.getLastAccessTime()) && (System.currentTimeMillis() - tbSession.getLastAccessTime()) / 1000 > dictionaryConfig.sysDomain().getSessionActive().getSeconds()) {
+                        log.warn("Authorization faile: session active, session active is {}", dictionaryConfig.sysDomain().getSessionActive().getSeconds());
+                        throw ExceptionResultEnum.NOT_LOGIN.exception();
+                    }
+                    tbSession.setLastInfo();
+                    redisUtil.setUserSession(accessEntity.getIdentity(), tbSession, expireTime);
+                }
+            }
+            return auth;
         }
         return false;
     }

+ 1 - 0
teachcloud-report/src/main/resources/application-dev.properties

@@ -58,6 +58,7 @@ sys.config.adminLogoUrl=http://qmth-test.oss-cn-shenzhen.aliyuncs.com/frontend/w
 #sys.config.autoCreatePdfResetMaxCount=5
 sys.config.threadPoolCoreSize=1
 sys.config.customThreadPoolCoreSize=false
+sys.config.sessionActive=1h
 sys.config.reportUrl=http://localhost:9099/#/student-report/
 spring.resources.static-locations=file:${sys.config.serverUpload},classpath:/META-INF/resources/,classpath:/resources/
 

+ 9 - 8
teachcloud-report/src/main/resources/application-release.properties

@@ -17,10 +17,10 @@ db.username=wdfx
 db.password=qmth_wdfx
 
 #redis\u6570\u636E\u6E90\u914D\u7F6E
-redis.host=qmth-redis2.redis.rds.aliyuncs.com
-redis.database=9
-redis.port=6379
-redis.password=wdfx_redis:Wdfx_redis
+com.qmth.redis.host=qmth-redis2.redis.rds.aliyuncs.com
+com.qmth.redis.db=9
+com.qmth.redis.port=6379
+com.qmth.redis.password=wdfx_redis:Wdfx_redis
 
 #mysql\u914D\u7F6E
 com.qmth.mysql.url=jdbc:mysql://${db.host}:${db.port}/${db.name}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8
@@ -35,7 +35,7 @@ aliyun.oss.name=oss-cn-shenzhen.aliyuncs.com
 aliyun.oss.endpoint=http://${aliyun.oss.name}
 aliyun.oss.accessKeyId=LTAI4Fi8jVRYT49QBXU9x5QX
 aliyun.oss.accessKeySecret=97aBLBfkQR5mzCiQa82yWLAH57eUd8
-aliyun.oss.bucket=teachcloud-test
+aliyun.oss.bucket=teachcloud-whu-prod
 aliyun.oss.url=http://${aliyun.oss.bucket}.${aliyun.oss.name}
 
 #\u7CFB\u7EDF\u914D\u7F6E
@@ -48,15 +48,16 @@ sys.config.fileHost=localhost:8877
 sys.config.serverHost=localhost:8877
 #sys.config.accessKey=0bce69d94a7b4aef8bc0badf150351a9
 #sys.config.accessSecret=LdUwb5X4etmjW7fDn0KAdoXG0Yt7AkDu
-sys.config.accessKey=274f823e5f59410f8b3bb6edcd8e2b6e
-sys.config.accessSecret=y7AO6W0TOdTF8HpWBwGHbp3wfIHsmUKr
+sys.config.accessKey=fb19fcd10beb4da9aae52839b74bc144
+sys.config.accessSecret=8XafgoK0Tb6gCvIspAfYieJpjYsX42K9
 sys.config.adminLogoUrl=http://qmth-test.oss-cn-shenzhen.aliyuncs.com/frontend/wx_logo.png
 #sys.config.htmlToPdfUrl=/usr/local/bin/wkhtmltopdf
 #sys.config.htmlToPdfUrl=/usr/local/bin/wkhtmltopdf
 #sys.config.autoCreatePdfResetMaxCount=5
 sys.config.threadPoolCoreSize=1
 sys.config.customThreadPoolCoreSize=false
-sys.config.reportUrl=https://localhost:8877/#/student-report/
+sys.config.sessionActive=1h
+sys.config.reportUrl=https://whu.qmth.com.cn/#/student-report/
 spring.resources.static-locations=file:${sys.config.serverUpload},classpath:/META-INF/resources/,classpath:/resources/
 
 org.center.url=https://solar.qmth.com.cn

+ 1 - 0
teachcloud-report/src/main/resources/application-test.properties

@@ -56,6 +56,7 @@ sys.config.adminLogoUrl=http://qmth-test.oss-cn-shenzhen.aliyuncs.com/frontend/w
 #sys.config.autoCreatePdfResetMaxCount=5
 sys.config.threadPoolCoreSize=1
 sys.config.customThreadPoolCoreSize=false
+sys.config.sessionActive=1h
 sys.config.reportUrl=http://192.168.10.136:7005/#/student-report/
 spring.resources.static-locations=file:${sys.config.serverUpload},classpath:/META-INF/resources/,classpath:/resources/
 

+ 219 - 0
teachcloud-report/src/test/java/com/qmth/teachcloud/report/XfTest.java

@@ -0,0 +1,219 @@
+package com.qmth.teachcloud.report;
+
+import com.alibaba.fastjson.JSONObject;
+import com.qmth.teachcloud.report.business.mapper.CourseReportMapper;
+import org.junit.Test;
+import org.junit.platform.commons.util.CollectionUtils;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.stream.Collectors;
+
+@SpringBootTest(classes = {TeachcloudReportApplication.class})
+@RunWith(SpringRunner.class)
+public class XfTest {
+
+
+    @Resource
+    private CourseReportMapper courseReportMapper;
+
+    /**
+     * 中等难度
+     */
+    @Test
+    public void trail() {
+        List<Map<String, Object>> studentPaperList = courseReportMapper.list1();
+
+        Map<String, List<Map<String, Object>>> scoreRateListMap = studentPaperList.stream().collect(Collectors.groupingBy(m -> m.get("num").toString()));
+        List<Map<String, Object>> finalList = new ArrayList<>();
+        for (Map.Entry<String, List<Map<String, Object>>> entry : scoreRateListMap.entrySet()) {
+            Map<String, Object> scoreRateMap = new HashMap<>();
+            scoreRateMap.put("num", entry.getKey());
+            double avgScoreRate = entry.getValue().stream().map(m -> {
+                double rate = new BigDecimal(m.get("score").toString()).divide(new BigDecimal(m.get("full_score").toString()), 8, BigDecimal.ROUND_HALF_UP).doubleValue();
+                return rate;
+            }).collect(Collectors.averagingDouble(m -> m));
+            scoreRateMap.put("scoreRate", new BigDecimal(avgScoreRate).setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue());
+            scoreRateMap.put("scoreRate8", avgScoreRate);
+            finalList.add(scoreRateMap);
+        }
+
+        List<String> nums = finalList.stream().filter(m -> Double.parseDouble(m.get("scoreRate").toString()) >= 0.3 && Double.parseDouble(m.get("scoreRate").toString()) <= 0.7).map(m -> m.get("num").toString()).collect(Collectors.toList());
+        System.out.println("中等难度题:" + JSONObject.toJSONString(nums));
+
+        List<Map<String, Object>> mapList = studentPaperList.stream().filter(m -> nums.contains(m.get("num").toString())).collect(Collectors.toList());
+        double middleScoreRate = mapList.stream().map(m -> {
+            double rate = new BigDecimal(m.get("score").toString()).divide(new BigDecimal(m.get("full_score").toString()), 8, BigDecimal.ROUND_HALF_UP).doubleValue();
+            return rate;
+        }).collect(Collectors.averagingDouble(m -> m));
+
+        System.out.println("全校中等难度得分率:" + middleScoreRate);
+
+
+        List<Map<String, Object>> mapList2 = studentPaperList.stream().filter(m -> nums.contains(m.get("num").toString()) && Objects.equals(m.get("inspect_college_id").toString(), "135697788569649152")).collect(Collectors.toList());
+        double middleScoreRate2 = mapList2.stream().map(m -> {
+            double rate = new BigDecimal(m.get("score").toString()).divide(new BigDecimal(m.get("full_score").toString()), 8, BigDecimal.ROUND_HALF_UP).doubleValue();
+            return rate;
+        }).collect(Collectors.averagingDouble(m -> m));
+
+        System.out.println("本院中等难度得分率:" + middleScoreRate2);
+    }
+
+
+    /**
+     * 效度
+     */
+    @Test
+    public void trail1() {
+        List<Map<String, Object>> studentPaperList = courseReportMapper.list1();
+
+        // 所有题卡
+        List<String> nums = studentPaperList.stream().map(m -> m.get("num").toString()).collect(Collectors.toList());
+
+        Map<String, List<Map<String, Object>>> studentMap = studentPaperList.stream().collect(Collectors.groupingBy(m -> m.get("id").toString()));
+        List<Map<String, Object>> studentScoreList = new ArrayList<>();
+        // 计算单个考生总分
+        for (Map.Entry<String, List<Map<String, Object>>> entry : studentMap.entrySet()) {
+            Map<String, Object> scoreRateMap = new HashMap<>();
+            scoreRateMap.put("id", entry.getKey());
+            double totalScore = entry.getValue().stream().map(m -> new BigDecimal(m.get("score").toString()).setScale(8, BigDecimal.ROUND_HALF_UP).doubleValue()).collect(Collectors.summingDouble(m -> m));
+            scoreRateMap.put("totalScore", totalScore);
+            studentScoreList.add(scoreRateMap);
+        }
+
+        // 计算百分等级
+        List<Map<String, Object>> newList = studentScoreList.stream().map(m -> {
+            double score = Double.parseDouble(m.get("totalScore").toString());
+            long lowCount = studentScoreList.stream().filter(m1 -> Double.parseDouble(m1.get("totalScore").toString()) < score).count();
+            long equelCount = studentScoreList.stream().filter(m1 -> Double.parseDouble(m1.get("totalScore").toString()) == score).count();
+            BigDecimal one = new BigDecimal(lowCount).add(new BigDecimal(equelCount).divide(new BigDecimal("2")));
+            int grade = one.multiply(new BigDecimal("100")).divide(new BigDecimal(studentScoreList.size()), 0, BigDecimal.ROUND_DOWN).intValue();
+            m.put("grade", grade);
+            return m;
+        }).collect(Collectors.toList());
+
+        int count27 = new BigDecimal(newList.size()).multiply(new BigDecimal("27")).divide(new BigDecimal("100"), 0, BigDecimal.ROUND_DOWN).intValue();
+        // 前27%考生
+        newList.sort((o1, o2) -> Integer.parseInt(o2.get("grade").toString()) - Integer.parseInt(o1.get("grade").toString()));
+        List<String> before27Ids = newList.stream().limit(count27).map(m -> m.get("id").toString()).collect(Collectors.toList());
+
+        List<Map<String, Object>> before27 = studentPaperList.stream().filter(m -> before27Ids.contains(m.get("id").toString())).collect(Collectors.toList());
+        Map<String, List<Map<String, Object>>> scoreRateListMap = before27.stream().collect(Collectors.groupingBy(m -> m.get("num").toString()));
+        Map<String, Object> amap = new HashMap<>();
+        for (Map.Entry<String, List<Map<String, Object>>> entry : scoreRateListMap.entrySet()) {
+            double avgScoreRate = entry.getValue().stream().map(m -> {
+                double rate = new BigDecimal(m.get("score").toString()).divide(new BigDecimal(m.get("full_score").toString()), 8, BigDecimal.ROUND_HALF_UP).doubleValue();
+                return rate;
+            }).collect(Collectors.averagingDouble(m -> m));
+            amap.put(entry.getKey(), new BigDecimal(avgScoreRate).setScale(8, BigDecimal.ROUND_HALF_UP).doubleValue());
+        }
+
+        // 后27%考生
+        newList.sort(Comparator.comparingInt(o -> Integer.parseInt(o.get("grade").toString())));
+
+        List<String> after27Ids = newList.stream().limit(count27).map(m -> m.get("id").toString()).collect(Collectors.toList());
+        List<Map<String, Object>> after27 = studentPaperList.stream().filter(m -> after27Ids.contains(m.get("id").toString())).collect(Collectors.toList());
+
+        Map<String, List<Map<String, Object>>> scoreRateListMap2 = after27.stream().collect(Collectors.groupingBy(m -> m.get("num").toString()));
+        Map<String, Object> bmap = new HashMap<>();
+        for (Map.Entry<String, List<Map<String, Object>>> entry : scoreRateListMap2.entrySet()) {
+            double avgScoreRate = entry.getValue().stream().map(m -> {
+                double rate = new BigDecimal(m.get("score").toString()).divide(new BigDecimal(m.get("full_score").toString()), 8, BigDecimal.ROUND_HALF_UP).doubleValue();
+                return rate;
+            }).collect(Collectors.averagingDouble(m -> m));
+            bmap.put(entry.getKey(), new BigDecimal(avgScoreRate).setScale(8, BigDecimal.ROUND_HALF_UP).doubleValue());
+        }
+
+        // 效度
+        Map<String, Object> finalMap = new HashMap<>();
+        for (String num : nums) {
+            double a27 = Double.parseDouble(amap.get(num).toString());
+            double b27 = Double.parseDouble(bmap.get(num).toString());
+            finalMap.put(num, a27 - b27);
+        }
+
+        System.out.println("效度:" + JSONObject.toJSONString(finalMap));
+    }
+
+    /**
+     * 效度
+     */
+    @Test
+    public void trail2() {
+        List<Map<String, Object>> studentPaperList = courseReportMapper.list1();
+
+        // 所有题卡
+        List<String> nums = studentPaperList.stream().map(m -> m.get("num").toString()).collect(Collectors.toList());
+
+        Map<String, List<Map<String, Object>>> studentMap = studentPaperList.stream().collect(Collectors.groupingBy(m -> m.get("id").toString()));
+        List<Map<String, Object>> studentScoreList = new ArrayList<>();
+        // 计算单个考生总分
+        for (Map.Entry<String, List<Map<String, Object>>> entry : studentMap.entrySet()) {
+            Map<String, Object> scoreRateMap = new HashMap<>();
+            scoreRateMap.put("id", entry.getKey());
+            double totalScore = entry.getValue().stream().map(m -> new BigDecimal(m.get("score").toString()).setScale(8, BigDecimal.ROUND_HALF_UP).doubleValue()).collect(Collectors.summingDouble(m -> m));
+            scoreRateMap.put("totalScore", totalScore);
+            studentScoreList.add(scoreRateMap);
+        }
+
+        // 计算百分等级
+        List<Map<String, Object>> newList = studentScoreList.stream().map(m -> {
+            double score = Double.parseDouble(m.get("totalScore").toString());
+            long lowCount = studentScoreList.stream().filter(m1 -> Double.parseDouble(m1.get("totalScore").toString()) < score).count();
+            long equelCount = studentScoreList.stream().filter(m1 -> Double.parseDouble(m1.get("totalScore").toString()) == score).count();
+            BigDecimal one = new BigDecimal(lowCount).add(new BigDecimal(equelCount).divide(new BigDecimal("2")));
+            int grade = one.multiply(new BigDecimal("100")).divide(new BigDecimal(studentScoreList.size()), 0, BigDecimal.ROUND_DOWN).intValue();
+            m.put("grade", grade);
+            return m;
+        }).collect(Collectors.toList());
+
+        int count27 = new BigDecimal(newList.size()).multiply(new BigDecimal("27")).divide(new BigDecimal("100"), 0, BigDecimal.ROUND_DOWN).intValue();
+        // 前27%考生
+        newList.sort((o1, o2) -> Integer.parseInt(o2.get("grade").toString()) - Integer.parseInt(o1.get("grade").toString()));
+        int beforeGrade = Integer.parseInt(newList.get(count27).get("grade").toString());
+        List<String> before27Ids = newList.stream().filter(m -> Integer.parseInt(m.get("grade").toString()) >= beforeGrade).map(m->m.get("id").toString()).collect(Collectors.toList());
+
+        List<Map<String, Object>> before27 = studentPaperList.stream().filter(m -> before27Ids.contains(m.get("id").toString())).collect(Collectors.toList());
+        Map<String, List<Map<String, Object>>> scoreRateListMap = before27.stream().collect(Collectors.groupingBy(m -> m.get("num").toString()));
+        Map<String, Object> amap = new HashMap<>();
+        for (Map.Entry<String, List<Map<String, Object>>> entry : scoreRateListMap.entrySet()) {
+            double avgScoreRate = entry.getValue().stream().map(m -> {
+                double rate = new BigDecimal(m.get("score").toString()).divide(new BigDecimal(m.get("full_score").toString()), 8, BigDecimal.ROUND_HALF_UP).doubleValue();
+                return rate;
+            }).collect(Collectors.averagingDouble(m -> m));
+            amap.put(entry.getKey(), new BigDecimal(avgScoreRate).setScale(8, BigDecimal.ROUND_HALF_UP).doubleValue());
+        }
+
+        // 后27%考生
+        newList.sort(Comparator.comparingInt(o -> Integer.parseInt(o.get("grade").toString())));
+        int afterGrade = Integer.parseInt(newList.get(count27).get("grade").toString());
+        List<String> after27Ids = newList.stream().filter(m -> Integer.parseInt(m.get("grade").toString()) <= afterGrade).map(m->m.get("id").toString()).collect(Collectors.toList());
+//        List<String> after27Ids = newList.stream().limit(count27).map(m -> m.get("id").toString()).collect(Collectors.toList());
+        List<Map<String, Object>> after27 = studentPaperList.stream().filter(m -> after27Ids.contains(m.get("id").toString())).collect(Collectors.toList());
+
+        Map<String, List<Map<String, Object>>> scoreRateListMap2 = after27.stream().collect(Collectors.groupingBy(m -> m.get("num").toString()));
+        Map<String, Object> bmap = new HashMap<>();
+        for (Map.Entry<String, List<Map<String, Object>>> entry : scoreRateListMap2.entrySet()) {
+            double avgScoreRate = entry.getValue().stream().map(m -> {
+                double rate = new BigDecimal(m.get("score").toString()).divide(new BigDecimal(m.get("full_score").toString()), 8, BigDecimal.ROUND_HALF_UP).doubleValue();
+                return rate;
+            }).collect(Collectors.averagingDouble(m -> m));
+            bmap.put(entry.getKey(), new BigDecimal(avgScoreRate).setScale(8, BigDecimal.ROUND_HALF_UP).doubleValue());
+        }
+
+        // 效度
+        Map<String, Object> finalMap = new HashMap<>();
+        for (String num : nums) {
+            double a27 = Double.parseDouble(amap.get(num).toString());
+            double b27 = Double.parseDouble(bmap.get(num).toString());
+            finalMap.put(num, a27 - b27);
+        }
+
+        System.out.println("效度:" + JSONObject.toJSONString(finalMap));
+    }
+
+}