Переглянути джерело

merge from release_v4.0.1

deason 4 роки тому
батько
коміт
d3e819be66
18 змінених файлів з 713 додано та 233 видалено
  1. 11 3
      .gitignore
  2. 35 0
      examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/controller/StudentOperateController.java
  3. 0 125
      examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/listener/KafkaConsumerListener.java
  4. 183 2
      examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/listener/RocketMqConsumerListener.java
  5. 1 1
      examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/provider/StudentCumulativeCountCloudServiceProvider.java
  6. 36 0
      examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/provider/StudentOperateLogCloudServiceProvider.java
  7. 47 0
      examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/QueryStudentOperateReq.java
  8. 59 0
      examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/StudentOperateBean.java
  9. 16 0
      examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/OperateRepo.java
  10. 100 0
      examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/entity/OperateEntity.java
  11. 15 0
      examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/OperateService.java
  12. 129 0
      examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/impl/OperateServiceImpl.java
  13. 2 0
      examcloud-core-reports-starter/src/main/java/cn/com/qmth/examcloud/core/reports/starter/CoreReportsApp.java
  14. 1 4
      examcloud-core-reports-starter/src/main/java/cn/com/qmth/examcloud/core/reports/starter/config/MqConsumerListenerStartup.java
  15. 1 1
      examcloud-core-reports-starter/src/main/resources/application.properties
  16. 77 60
      examcloud-core-reports-starter/src/main/resources/log4j2.xml
  17. 0 19
      jenkins-dev.sh
  18. 0 18
      jenkins-test.sh

+ 11 - 3
.gitignore

@@ -1,11 +1,19 @@
+*.class
+
+# Proguard folder generated by ide
 .project
 .classpath
 .settings
 target/
 .idea/
 *.iml
-*test/
+
+# Log Files
+*.log
+*.class
+
+
 # Package Files #
 *.jar
-logs/
-
+*.war
+*.ear

+ 35 - 0
examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/controller/StudentOperateController.java

@@ -0,0 +1,35 @@
+package cn.com.qmth.examcloud.core.reports.api.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.api.commons.exchange.PageInfo;
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.core.reports.base.bean.QueryStudentOperateReq;
+import cn.com.qmth.examcloud.core.reports.base.bean.StudentOperateBean;
+import cn.com.qmth.examcloud.core.reports.service.OperateService;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+
+@RestController
+@Api(tags = "学生操作日志")
+@RequestMapping("${$rmp.ctr.reports}/" + "studentOperate")
+public class StudentOperateController extends ControllerSupport {
+	@Autowired
+	private OperateService operateService;
+
+	@GetMapping("page/{pageNo}/{pageSize}")
+	@ApiOperation(value = "项目列表分页查询")
+	public PageInfo<StudentOperateBean> queryPage(@ModelAttribute QueryStudentOperateReq req,
+			@PathVariable @ApiParam(value = "pageNo = 1,2,3...") Integer pageNo, @PathVariable Integer pageSize) {
+		User user = getAccessUser();
+		return operateService.queryPage(req, pageNo, pageSize, user.getRootOrgId());
+	}
+	
+}

+ 0 - 125
examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/listener/KafkaConsumerListener.java

@@ -1,125 +0,0 @@
-package cn.com.qmth.examcloud.core.reports.api.listener;
-
-import java.time.Duration;
-import java.util.List;
-import java.util.Properties;
-
-import org.apache.kafka.clients.consumer.ConsumerRecord;
-import org.apache.kafka.clients.consumer.ConsumerRecords;
-import org.apache.kafka.clients.consumer.KafkaConsumer;
-import org.assertj.core.util.Lists;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.alibaba.fastjson.JSON;
-
-import cn.com.qmth.examcloud.core.reports.base.util.online.ActiveDataUtil;
-import cn.com.qmth.examcloud.core.reports.base.util.online.ExamStudentActive;
-import cn.com.qmth.examcloud.core.reports.base.util.online.StudentActive;
-import cn.com.qmth.examcloud.core.reports.base.util.online.StudentLogin;
-import cn.com.qmth.examcloud.core.reports.base.util.online.UserActive;
-import cn.com.qmth.examcloud.reports.commons.bean.OnlineExamStudentReport;
-import cn.com.qmth.examcloud.reports.commons.bean.OnlineStudentReport;
-import cn.com.qmth.examcloud.reports.commons.bean.OnlineUserReport;
-import cn.com.qmth.examcloud.reports.commons.enums.Topic;
-import cn.com.qmth.examcloud.web.bootstrap.PropertyHolder;
-
-public class KafkaConsumerListener {
-
-    private final static Logger logger = LoggerFactory.getLogger(KafkaConsumerListener.class);
-
-    private static Properties props;
-
-    static {
-        props = new Properties();
-        props.put("group.id", "online-count-group");
-        props.put("bootstrap.servers", PropertyHolder.getString("$kafka-bootstrap-servers"));
-        props.put("key.deserializer", PropertyHolder.getString("$kafka-key-deserializer",
-                "org.apache.kafka.common.serialization.StringDeserializer"));
-        props.put("value.deserializer", PropertyHolder.getString("$kafka-value-deserializer",
-                "org.apache.kafka.common.serialization.StringDeserializer"));
-    }
-
-    public static void start() {
-        KafkaConsumer<String, String> consumer = new KafkaConsumer<String, String>(props);
-        List<String> subs = Lists.newArrayList();
-        subs.add(Topic.STUDENT.getCode());
-        subs.add(Topic.EXAM_STUDENT.getCode());
-        subs.add(Topic.USER.getCode());
-        consumer.subscribe(subs);
-        try {
-            for (;;) {
-                // 100 是超时时间(ms),在该时间内 poll 会等待服务器返回数据
-                ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
-
-                // poll 返回一个记录列表。
-                // 每条记录都包含了记录所属主题的信息、记录所在分区的信息、记录在分区里的偏移量,以及记录的键值对。
-                for (ConsumerRecord<String, String> record : records) {
-                    if (Topic.STUDENT.getCode().equals(record.topic())) {
-                        onMessageStudent(record.value());
-                    } else if (Topic.EXAM_STUDENT.getCode().equals(record.topic())) {
-                        onMessageExamStudent(record.value());
-                    } else if (Topic.USER.getCode().equals(record.topic())) {
-                        onMessageUser(record.value());
-                    }
-                }
-            }
-        } catch (Exception e) {
-            logger.error("消息消费线程出错", e);
-        } finally {
-            // 关闭消费者,网络连接和 socket 也会随之关闭,并立即触发一次再均衡
-            consumer.close();
-        }
-    }
-
-    private static void onMessageStudent(String message) {
-        logger.debug("STUDENT message:" + message);
-        OnlineStudentReport r = JSON.parseObject(message, OnlineStudentReport.class);
-        StudentActive ac = new StudentActive();
-        ac.setActiveTime(r.getReportTime().getTime());
-        ac.setRootOrgId(r.getRootOrgId());
-        ac.setStudentId(r.getStudentId());
-        ActiveDataUtil.updateStudentActive(ac);
-        onMessageLoginStudent(r.getReportTime().getTime(), r.getRootOrgId(), r.getStudentId());
-    }
-
-    private static void onMessageExamStudent(String message) {
-        logger.debug("EXAM_STUDENT message:" + message);
-        OnlineExamStudentReport r = JSON.parseObject(message, OnlineExamStudentReport.class);
-        ExamStudentActive ac = new ExamStudentActive();
-        ac.setActiveTime(r.getReportTime().getTime());
-        ac.setRootOrgId(r.getRootOrgId());
-        ac.setStudentId(r.getStudentId());
-        ac.setExamId(r.getExamId());
-        ActiveDataUtil.updateExamStudentActive(ac);
-        onMessageStudent(r.getReportTime().getTime(), r.getRootOrgId(), r.getStudentId());
-        onMessageLoginStudent(r.getReportTime().getTime(), r.getRootOrgId(), r.getStudentId());
-    }
-
-    private static void onMessageUser(String message) {
-        logger.debug("USER message:" + message);
-        OnlineUserReport r = JSON.parseObject(message, OnlineUserReport.class);
-        UserActive ac = new UserActive();
-        ac.setActiveTime(r.getReportTime().getTime());
-        ac.setRootOrgId(r.getRootOrgId());
-        ac.setUserId(r.getUserId());
-        ActiveDataUtil.updateUserActive(ac);
-    }
-
-    private static void onMessageLoginStudent(Long reportTime, Long rootOrgId, Long studentId) {
-        StudentLogin sl = new StudentLogin();
-        sl.setActiveTime(reportTime);
-        sl.setRootOrgId(rootOrgId);
-        sl.setStudentId(studentId);
-        ActiveDataUtil.updateStudentlogin(sl);
-    }
-
-    private static void onMessageStudent(Long reportTime, Long rootOrgId, Long studentId) {
-        StudentActive ac = new StudentActive();
-        ac.setActiveTime(reportTime);
-        ac.setRootOrgId(rootOrgId);
-        ac.setStudentId(studentId);
-        ActiveDataUtil.updateStudentActive(ac);
-    }
-
-}

+ 183 - 2
examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/listener/RocketMqConsumerListener.java

@@ -1,13 +1,194 @@
 package cn.com.qmth.examcloud.core.reports.api.listener;
 
+import java.util.Properties;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.alibaba.fastjson.JSON;
+import com.aliyun.openservices.ons.api.Action;
+import com.aliyun.openservices.ons.api.ConsumeContext;
+import com.aliyun.openservices.ons.api.Consumer;
+import com.aliyun.openservices.ons.api.Message;
+import com.aliyun.openservices.ons.api.MessageListener;
+import com.aliyun.openservices.ons.api.ONSFactory;
+import com.aliyun.openservices.ons.api.PropertyKeyConst;
+
+import cn.com.qmth.examcloud.core.reports.base.util.online.ActiveDataUtil;
+import cn.com.qmth.examcloud.core.reports.base.util.online.ExamStudentActive;
+import cn.com.qmth.examcloud.core.reports.base.util.online.StudentActive;
+import cn.com.qmth.examcloud.core.reports.base.util.online.StudentLogin;
+import cn.com.qmth.examcloud.core.reports.base.util.online.UserActive;
+import cn.com.qmth.examcloud.core.reports.service.OperateService;
+import cn.com.qmth.examcloud.reports.commons.bean.OnlineExamStudentReport;
+import cn.com.qmth.examcloud.reports.commons.bean.OnlineStudentReport;
+import cn.com.qmth.examcloud.reports.commons.bean.OnlineUserReport;
+import cn.com.qmth.examcloud.reports.commons.bean.OperateReport;
+import cn.com.qmth.examcloud.reports.commons.enums.Tag;
+import cn.com.qmth.examcloud.reports.commons.enums.Topic;
+import cn.com.qmth.examcloud.web.bootstrap.PropertyHolder;
+import cn.com.qmth.examcloud.web.support.SpringContextHolder;
+
 public class RocketMqConsumerListener {
 	private final static Logger logger = LoggerFactory.getLogger(RocketMqConsumerListener.class);
-	
+
+	private static OperateService operateService = SpringContextHolder.getBean(OperateService.class);
+
+	private static Properties properties = new Properties();
+	static {
+		properties.put(PropertyKeyConst.AccessKey, PropertyHolder.getString("$rocketmq-accesskey"));
+		// AccessKeySecret 阿里云身份验证,在阿里云服务器管理控制台创建。
+		properties.put(PropertyKeyConst.SecretKey, PropertyHolder.getString("$rocketmq-secretkey"));
+		// 设置发送超时时间,单位毫秒。
+		properties.setProperty(PropertyKeyConst.SendMsgTimeoutMillis, "3000");
+		// 设置 TCP 接入域名,进入控制台的实例详情页面的 TCP 协议客户端接入点区域查看。
+		properties.put(PropertyKeyConst.NAMESRV_ADDR, PropertyHolder.getString("$rocketmq-namesrv-addr"));
+		// 顺序消息消费失败进行重试前的等待时间,单位(毫秒),取值范围: 10 毫秒 ~ 30,000 毫秒
+		properties.put(PropertyKeyConst.SuspendTimeMillis, "100");
+		// 消息消费失败时的最大重试次数
+		properties.put(PropertyKeyConst.MaxReconsumeTimes, "10");
+	}
+
 	public static void start() {
-		
+		onlineExamStudent();
+		onlineStudent();
+		onlineUser();
+		operateInfo();
+	}
+
+	private static void onlineExamStudent() {
+		properties.put(PropertyKeyConst.GROUP_ID, Tag.ONLINE_EXAM_STUDENT.getGroup());
+		Consumer consumer = ONSFactory.createConsumer(properties);
+		consumer.subscribe(Topic.REPORT_TOPIC.getCode(), Tag.ONLINE_EXAM_STUDENT.getCode(), new MessageListener() {
+			@Override
+			public Action consume(Message message, ConsumeContext context) {
+				try {
+					String msg = new String(message.getBody(), "utf-8");
+					onMessageExamStudent(msg);
+					return Action.CommitMessage;
+				} catch (Exception e) {
+					logger.error("consumer failed MsgID:" + message.getMsgID(), e);
+					return Action.ReconsumeLater;
+				}
+			}
+
+		});
+
+		consumer.start();
+	}
+
+	private static void onlineStudent() {
+		properties.put(PropertyKeyConst.GROUP_ID, Tag.ONLINE_STUDENT.getGroup());
+		Consumer consumer = ONSFactory.createConsumer(properties);
+		consumer.subscribe(Topic.REPORT_TOPIC.getCode(), Tag.ONLINE_STUDENT.getCode(), new MessageListener() {
+			@Override
+			public Action consume(Message message, ConsumeContext context) {
+				try {
+					String msg = new String(message.getBody(), "utf-8");
+					onMessageStudent(msg);
+					return Action.CommitMessage;
+				} catch (Exception e) {
+					logger.error("consumer failed MsgID:" + message.getMsgID(), e);
+					return Action.ReconsumeLater;
+				}
+			}
+
+		});
+
+		consumer.start();
+	}
+
+	private static void onlineUser() {
+		properties.put(PropertyKeyConst.GROUP_ID, Tag.ONLINE_USER.getGroup());
+		Consumer consumer = ONSFactory.createConsumer(properties);
+		consumer.subscribe(Topic.REPORT_TOPIC.getCode(), Tag.ONLINE_USER.getCode(), new MessageListener() {
+			@Override
+			public Action consume(Message message, ConsumeContext context) {
+				try {
+					String msg = new String(message.getBody(), "utf-8");
+					onMessageUser(msg);
+					return Action.CommitMessage;
+				} catch (Exception e) {
+					logger.error("consumer failed MsgID:" + message.getMsgID(), e);
+					return Action.ReconsumeLater;
+				}
+			}
+
+		});
+
+		consumer.start();
 	}
 
+	private static void operateInfo() {
+		properties.put(PropertyKeyConst.GROUP_ID, Tag.OPERATE_INFO.getGroup());
+		Consumer consumer = ONSFactory.createConsumer(properties);
+		consumer.subscribe(Topic.REPORT_TOPIC.getCode(), Tag.OPERATE_INFO.getCode(), new MessageListener() {
+			@Override
+			public Action consume(Message message, ConsumeContext context) {
+				try {
+					String msg = new String(message.getBody(), "utf-8");
+					onOperateInfo(msg, message.getMsgID());
+					return Action.CommitMessage;
+				} catch (Exception e) {
+					logger.error("consumer failed MsgID:" + message.getMsgID(), e);
+					return Action.ReconsumeLater;
+				}
+			}
+
+		});
+
+		consumer.start();
+	}
+
+	private static void onMessageStudent(String message) {
+		OnlineStudentReport r = JSON.parseObject(message, OnlineStudentReport.class);
+		StudentActive ac = new StudentActive();
+		ac.setActiveTime(r.getReportTime().getTime());
+		ac.setRootOrgId(r.getRootOrgId());
+		ac.setStudentId(r.getStudentId());
+		ActiveDataUtil.updateStudentActive(ac);
+		onMessageLoginStudent(r.getReportTime().getTime(), r.getRootOrgId(), r.getStudentId());
+	}
+
+	private static void onMessageExamStudent(String message) {
+		OnlineExamStudentReport r = JSON.parseObject(message, OnlineExamStudentReport.class);
+		ExamStudentActive ac = new ExamStudentActive();
+		ac.setActiveTime(r.getReportTime().getTime());
+		ac.setRootOrgId(r.getRootOrgId());
+		ac.setStudentId(r.getStudentId());
+		ac.setExamId(r.getExamId());
+		ActiveDataUtil.updateExamStudentActive(ac);
+		onMessageStudent(r.getReportTime().getTime(), r.getRootOrgId(), r.getStudentId());
+		onMessageLoginStudent(r.getReportTime().getTime(), r.getRootOrgId(), r.getStudentId());
+	}
+
+	private static void onMessageUser(String message) {
+		OnlineUserReport r = JSON.parseObject(message, OnlineUserReport.class);
+		UserActive ac = new UserActive();
+		ac.setActiveTime(r.getReportTime().getTime());
+		ac.setRootOrgId(r.getRootOrgId());
+		ac.setUserId(r.getUserId());
+		ActiveDataUtil.updateUserActive(ac);
+	}
+
+	private static void onMessageLoginStudent(Long reportTime, Long rootOrgId, Long studentId) {
+		StudentLogin sl = new StudentLogin();
+		sl.setActiveTime(reportTime);
+		sl.setRootOrgId(rootOrgId);
+		sl.setStudentId(studentId);
+		ActiveDataUtil.updateStudentlogin(sl);
+	}
+
+	private static void onMessageStudent(Long reportTime, Long rootOrgId, Long studentId) {
+		StudentActive ac = new StudentActive();
+		ac.setActiveTime(reportTime);
+		ac.setRootOrgId(rootOrgId);
+		ac.setStudentId(studentId);
+		ActiveDataUtil.updateStudentActive(ac);
+	}
+
+	private static void onOperateInfo(String message, String msgId) {
+		OperateReport r = JSON.parseObject(message, OperateReport.class);
+		operateService.saveOperate(r,msgId);
+	}
 }

+ 1 - 1
examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/provider/StudentCumulativeCountCloudServiceProvider.java

@@ -14,7 +14,7 @@ import io.swagger.annotations.ApiOperation;
 
 @RestController
 @Api(tags = "学生累计在线人数接口")
-@RequestMapping("${$rmp.cloud.reports}"+"studentCumulativeCount")
+@RequestMapping("${$rmp.cloud.reports}/studentCumulativeCount")
 public class StudentCumulativeCountCloudServiceProvider extends ControllerSupport implements StudentCumulativeCountCloudService {
 
 	

+ 36 - 0
examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/provider/StudentOperateLogCloudServiceProvider.java

@@ -0,0 +1,36 @@
+package cn.com.qmth.examcloud.core.reports.api.provider;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.core.reports.api.StudentOperateLogCloudService;
+import cn.com.qmth.examcloud.core.reports.api.response.CleanStudentOperateLogResp;
+import cn.com.qmth.examcloud.core.reports.service.OperateService;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+@RestController
+@Api(tags = "学生操作日志")
+@RequestMapping("${$rmp.cloud.reports}"+"studentOperate")
+public class StudentOperateLogCloudServiceProvider extends ControllerSupport implements StudentOperateLogCloudService {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 5830037529546749521L;
+	@Autowired
+	private OperateService operateService;
+
+
+	@ApiOperation(value = "清理过期数据")
+    @PostMapping("/clean")
+	@Override
+	public CleanStudentOperateLogResp clean() {
+		CleanStudentOperateLogResp res=new CleanStudentOperateLogResp();
+		operateService.clean();
+		return res;
+	}
+}

+ 47 - 0
examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/QueryStudentOperateReq.java

@@ -0,0 +1,47 @@
+package cn.com.qmth.examcloud.core.reports.base.bean;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import io.swagger.annotations.ApiModelProperty;
+
+public class QueryStudentOperateReq implements JsonSerializable {
+
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 5321340311113876457L;
+    @ApiModelProperty(value = "studentId")
+	private Long studentId;
+    @ApiModelProperty(value = "operate")
+	private String operate;
+    @ApiModelProperty(value = "startTime")
+	private String startTime;
+    @ApiModelProperty(value = "endTime")
+	private String endTime;
+	public Long getStudentId() {
+		return studentId;
+	}
+	public void setStudentId(Long studentId) {
+		this.studentId = studentId;
+	}
+	public String getOperate() {
+		return operate;
+	}
+	public void setOperate(String operate) {
+		this.operate = operate;
+	}
+	public String getStartTime() {
+		return startTime;
+	}
+	public void setStartTime(String startTime) {
+		this.startTime = startTime;
+	}
+	public String getEndTime() {
+		return endTime;
+	}
+	public void setEndTime(String endTime) {
+		this.endTime = endTime;
+	}
+
+	
+}

+ 59 - 0
examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/StudentOperateBean.java

@@ -0,0 +1,59 @@
+package cn.com.qmth.examcloud.core.reports.base.bean;
+
+import java.util.Date;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import io.swagger.annotations.ApiModelProperty;
+
+public class StudentOperateBean implements JsonSerializable {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 8576153787955491094L;
+	@ApiModelProperty(value = "操作时间")
+	private Date operateTime;
+	
+	@ApiModelProperty(value = "ip")
+	private String operateIp;
+	
+	@ApiModelProperty(value = "操作端")
+	private String operateClient;
+	
+	@ApiModelProperty(value = "操作内容")
+	private String operate;
+
+	public Date getOperateTime() {
+		return operateTime;
+	}
+
+	public void setOperateTime(Date operateTime) {
+		this.operateTime = operateTime;
+	}
+
+	public String getOperateIp() {
+		return operateIp;
+	}
+
+	public void setOperateIp(String operateIp) {
+		this.operateIp = operateIp;
+	}
+
+	public String getOperateClient() {
+		return operateClient;
+	}
+
+	public void setOperateClient(String operateClient) {
+		this.operateClient = operateClient;
+	}
+
+	public String getOperate() {
+		return operate;
+	}
+
+	public void setOperate(String operate) {
+		this.operate = operate;
+	}
+	
+	
+}

+ 16 - 0
examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/OperateRepo.java

@@ -0,0 +1,16 @@
+package cn.com.qmth.examcloud.core.reports.dao;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+
+import cn.com.qmth.examcloud.core.reports.dao.entity.OperateEntity;
+
+public interface OperateRepo extends JpaRepository<OperateEntity, Long>,
+		JpaSpecificationExecutor<OperateEntity> {
+	
+	@Modifying
+	@Query(value = "delete from  ec_r_operate  where operate_time<=DATE_SUB(now(),interval 1 MONTH)", nativeQuery = true)
+	public void clean();
+}

+ 100 - 0
examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/entity/OperateEntity.java

@@ -0,0 +1,100 @@
+package cn.com.qmth.examcloud.core.reports.dao.entity;
+
+import java.util.Date;
+
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.Index;
+import javax.persistence.Table;
+import javax.validation.constraints.NotNull;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.UserType;
+import cn.com.qmth.examcloud.core.reports.dao.entity.share.IdEntity;
+
+@Entity
+@Table(name = "ec_r_operate", indexes = {
+		@Index(name = "IDX_R_OPERATE_01", columnList = "rootOrgId,studentId", unique = false),
+		@Index(name = "IDX_R_OPERATE_02", columnList = "rootOrgId,studentId,operateTime", unique = false),
+		@Index(name = "IDX_R_OPERATE_03", columnList = "msgId", unique = true)})
+public class OperateEntity extends IdEntity {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -5964824217953870271L;
+	@NotNull
+	private Long rootOrgId;
+	private Long operateUserId;
+	@NotNull
+	private Long studentId;
+	private Long examStudentId;
+	private String operateIp;
+	@NotNull
+	@Enumerated(EnumType.STRING)
+	private UserType operateUserType;
+	@NotNull
+	private Date operateTime;
+	@NotNull
+	private String operate;
+	@NotNull
+	private String msgId;
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+	public String getOperateIp() {
+		return operateIp;
+	}
+	public void setOperateIp(String operateIp) {
+		this.operateIp = operateIp;
+	}
+	
+	public UserType getOperateUserType() {
+		return operateUserType;
+	}
+	public void setOperateUserType(UserType operateUserType) {
+		this.operateUserType = operateUserType;
+	}
+	public Date getOperateTime() {
+		return operateTime;
+	}
+	public void setOperateTime(Date operateTime) {
+		this.operateTime = operateTime;
+	}
+	public String getOperate() {
+		return operate;
+	}
+	public void setOperate(String operate) {
+		this.operate = operate;
+	}
+	public String getMsgId() {
+		return msgId;
+	}
+	public void setMsgId(String msgId) {
+		this.msgId = msgId;
+	}
+	public Long getOperateUserId() {
+		return operateUserId;
+	}
+	public void setOperateUserId(Long operateUserId) {
+		this.operateUserId = operateUserId;
+	}
+	public Long getStudentId() {
+		return studentId;
+	}
+	public void setStudentId(Long studentId) {
+		this.studentId = studentId;
+	}
+	public Long getExamStudentId() {
+		return examStudentId;
+	}
+	public void setExamStudentId(Long examStudentId) {
+		this.examStudentId = examStudentId;
+	}
+
+	
+
+}

+ 15 - 0
examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/OperateService.java

@@ -0,0 +1,15 @@
+package cn.com.qmth.examcloud.core.reports.service;
+
+import cn.com.qmth.examcloud.api.commons.exchange.PageInfo;
+import cn.com.qmth.examcloud.core.reports.base.bean.QueryStudentOperateReq;
+import cn.com.qmth.examcloud.core.reports.base.bean.StudentOperateBean;
+import cn.com.qmth.examcloud.reports.commons.bean.OperateReport;
+
+public interface OperateService {
+	public void saveOperate(OperateReport r, String msgId);
+	
+	public PageInfo<StudentOperateBean> queryPage(QueryStudentOperateReq req,Integer pageNo,Integer pageSize,Long rootOrgId);
+
+	public void clean();
+
+}

+ 129 - 0
examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/impl/OperateServiceImpl.java

@@ -0,0 +1,129 @@
+package cn.com.qmth.examcloud.core.reports.service.impl;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import javax.persistence.criteria.Predicate;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DuplicateKeyException;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.domain.Sort.Direction;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.google.common.collect.Lists;
+
+import cn.com.qmth.examcloud.api.commons.exchange.PageInfo;
+import cn.com.qmth.examcloud.api.commons.security.bean.UserType;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.commons.util.DateUtil;
+import cn.com.qmth.examcloud.core.reports.base.bean.QueryStudentOperateReq;
+import cn.com.qmth.examcloud.core.reports.base.bean.StudentOperateBean;
+import cn.com.qmth.examcloud.core.reports.dao.OperateRepo;
+import cn.com.qmth.examcloud.core.reports.dao.entity.OperateEntity;
+import cn.com.qmth.examcloud.core.reports.service.OperateService;
+import cn.com.qmth.examcloud.reports.commons.bean.OperateReport;
+
+@Service
+public class OperateServiceImpl implements OperateService {
+	@Autowired
+	private OperateRepo operateRepo;
+	
+	
+	@Transactional
+	@Override
+	public void saveOperate(OperateReport r, String msgId) {
+		OperateEntity e=new OperateEntity();
+		e.setMsgId(msgId);
+		e.setRootOrgId(r.getRootOrgId());
+		e.setStudentId(r.getStudentId());
+		e.setOperateUserId(r.getOperateUserId());
+		e.setOperateUserType(r.getOperateUserType());;
+		e.setOperateTime(r.getReportTime());
+		e.setOperateIp(r.getRemoteHost());
+		e.setOperate(r.getOperate());
+		e.setExamStudentId(r.getExamStudentId());
+		e.setCreationTime(new Date());
+		try {
+			operateRepo.save(e);
+		} catch (DuplicateKeyException e1) {
+			//忽略;
+		}
+	}
+
+
+	@Override
+	public PageInfo<StudentOperateBean> queryPage(QueryStudentOperateReq req, Integer pageNo, Integer pageSize,
+			Long rootOrgId) {
+		Long studentId = req.getStudentId();
+		if(studentId==null) {
+			throw new StatusException("10001", "studentId不能为空");
+		}
+		String operate = req.getOperate();
+		String startTime=req.getStartTime();
+		String endTime=req.getEndTime();
+		Specification<OperateEntity> specification = (root, query, cb) -> {
+			List<Predicate> predicates = new ArrayList<>();
+			predicates.add(cb.equal(root.get("rootOrgId"), rootOrgId));
+			predicates.add(cb.equal(root.get("studentId"), studentId));
+
+			if(StringUtils.isNotBlank(operate)) {
+				predicates.add(cb.like(root.get("operate"), toSqlSearchPattern(operate)));
+			}
+			if (StringUtils.isNotBlank(startTime)) {
+				Date start=DateUtil.parse(startTime, "yyyy/MM/dd HH:mm:ss");
+				predicates.add(cb.greaterThanOrEqualTo(root.get("operateTime"), start));
+			}
+			if (StringUtils.isNotBlank(endTime)) {
+				Date end=DateUtil.parse(endTime, "yyyy/MM/dd HH:mm:ss");
+				predicates.add(cb.lessThanOrEqualTo(root.get("operateTime"), end));
+			}
+
+			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+		};
+
+		PageRequest pageRequest = PageRequest.of(pageNo - 1, pageSize, new Sort(Direction.DESC, "operateTime"));
+
+		Page<OperateEntity> data = operateRepo.findAll(specification, pageRequest);
+
+		List<StudentOperateBean> ret = Lists.newArrayList();
+		String studentClient="考生端";
+		String adminClient="管理端";
+		for (OperateEntity cur : data) {
+			StudentOperateBean bean = new StudentOperateBean();
+			bean.setOperate(cur.getOperate());
+			bean.setOperateIp(cur.getOperateIp());
+			bean.setOperateTime(cur.getOperateTime());
+			if(UserType.COMMON.equals(cur.getOperateUserType())) {
+				bean.setOperateClient(adminClient);
+			}
+			if(UserType.STUDENT.equals(cur.getOperateUserType())) {
+				bean.setOperateClient(studentClient);
+			}
+			ret.add(bean);
+		}
+		return new PageInfo<StudentOperateBean>(data, ret);
+	}
+	private String toSqlSearchPattern(String column) {
+		if (StringUtils.isBlank(column)) {
+			return "%";
+		} else {
+			column = column.trim().replaceAll("\\s", "%");
+			column = "%" + column + "%";
+			return column;
+		}
+	}
+
+
+	@Transactional
+	@Override
+	public void clean() {
+		operateRepo.clean();
+	}
+}

+ 2 - 0
examcloud-core-reports-starter/src/main/java/cn/com/qmth/examcloud/core/reports/starter/CoreReportsApp.java

@@ -6,6 +6,7 @@ import org.springframework.boot.autoconfigure.domain.EntityScan;
 import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
 import org.springframework.cloud.openfeign.EnableFeignClients;
 import org.springframework.context.annotation.ComponentScan;
+import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
 import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
 
 import cn.com.qmth.examcloud.web.bootstrap.AppBootstrap;
@@ -14,6 +15,7 @@ import cn.com.qmth.examcloud.web.bootstrap.AppBootstrap;
 @ComponentScan(basePackages = { "cn.com.qmth" })
 @EntityScan(basePackages = { "com.qmth.commons", "cn.com.qmth" })
 @SpringBootApplication
+@EnableJpaAuditing
 @EnableEurekaClient
 @EnableFeignClients
 @EnableJpaRepositories(basePackages = {"cn.com.qmth"})

+ 1 - 4
examcloud-core-reports-starter/src/main/java/cn/com/qmth/examcloud/core/reports/starter/config/MqConsumerListenerStartup.java

@@ -7,7 +7,6 @@ import org.springframework.boot.ApplicationRunner;
 import org.springframework.core.annotation.Order;
 import org.springframework.stereotype.Component;
 
-import cn.com.qmth.examcloud.core.reports.api.listener.KafkaConsumerListener;
 import cn.com.qmth.examcloud.core.reports.api.listener.RocketMqConsumerListener;
 import cn.com.qmth.examcloud.reports.commons.enums.MqType;
 import cn.com.qmth.examcloud.web.bootstrap.PropertyHolder;
@@ -23,9 +22,7 @@ public class MqConsumerListenerStartup implements ApplicationRunner {
 		Boolean reportEnable = PropertyHolder.getBoolean("$report.enable", false);
 
 		if (reportEnable) {
-			if(MqType.KAFKA.getCode().equals(mqType)) {
-				KafkaConsumerListener.start();
-			}else if(MqType.ROCKETMQ.getCode().equals(mqType)) {
+			if(MqType.ROCKETMQ.getCode().equals(mqType)) {
 				RocketMqConsumerListener.start();
 			}else {
 				logger.error("no $report.mq-type property config!");

+ 1 - 1
examcloud-core-reports-starter/src/main/resources/application.properties

@@ -1,7 +1,7 @@
 spring.profiles.active=dev
 
 examcloud.startup.startupCode=8015
-examcloud.startup.configCenterHost=config-host
+examcloud.startup.configCenterHost=192.168.10.39
 examcloud.startup.configCenterPort=9999
 examcloud.startup.appCode=R
 

+ 77 - 60
examcloud-core-reports-starter/src/main/resources/log4j2.xml

@@ -1,69 +1,86 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Configuration status="WARN" monitorInterval="30">
 
-	<Properties>
-		<Property name="commonLevel" value="${sys:log.commonLevel}" />
-	</Properties>
+    <Properties>
+        <Property name="commonLevel" value="${sys:log.commonLevel}"/>
+        <Property name="logPattern">
+            %d{yyyy-MM-dd HH:mm:ss.SSS} | %clr{%level} | %X{TRACE_ID} %X{CALLER} | %clr{%c{1.1}:%L}{cyan} | %m%n
+        </Property>
+    </Properties>
 
-	<Appenders>
-		<!-- 控制台 日志 -->
-		<Console name="Console" target="SYSTEM_OUT">
-			<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS}| %level | %X{TRACE_ID} - %X{CALLER} | %m | %l%n" />
-		</Console>
-		<!-- debug 日志 -->
-		<RollingFile name="DEBUG_APPENDER" fileName="./logs/debug/debug.log"
-			filePattern="./logs/debug/debug-%d{yyyy.MM.dd.HH}-%i.log">
-			<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS}| %level | %X{TRACE_ID} - %X{CALLER} | %m | %l%n" />
-			<Policies>
-				<TimeBasedTriggeringPolicy interval="1" />
-				<SizeBasedTriggeringPolicy size="100MB" />
-			</Policies>
-			<DefaultRolloverStrategy max="10">
-				<Delete basePath="./logs/debug" maxDepth="1">
-					<IfFileName glob="debug-*.log" />
-					<IfLastModified age="24H" />
-				</Delete>
-			</DefaultRolloverStrategy>
-		</RollingFile>
-		<!-- 接口日志 -->
-		<RollingFile name="INTERFACE_APPENDER" fileName="./logs/interface/interface.log"
-			filePattern="./logs/interface/interface-%d{yyyy.MM.dd.HH}-%i.log">
-			<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS}| %level | %X{TRACE_ID} - %X{CALLER} | %m%n" />
-			<Policies>
-				<TimeBasedTriggeringPolicy interval="1" />
-				<SizeBasedTriggeringPolicy size="100MB" />
-			</Policies>
-			<DefaultRolloverStrategy max="10">
-				<Delete basePath="./logs/interface" maxDepth="1">
-					<IfFileName glob="interface-*.log" />
-					<IfLastModified age="24H" />
-				</Delete>
-			</DefaultRolloverStrategy>
-		</RollingFile>
-	</Appenders>
+    <Appenders>
+        <!-- 控制台 日志 -->
+        <Console name="Console" target="SYSTEM_OUT">
+            <PatternLayout pattern="${logPattern}" charset="UTF-8"/>
+        </Console>
 
-	<Loggers>
-		<Logger name="cn.com.qmth" level="${commonLevel}" additivity="false">
-			<AppenderRef ref="DEBUG_APPENDER" />
-			<AppenderRef ref="Console" />
-		</Logger>
+        <!-- debug 日志 -->
+        <RollingFile name="DEBUG_APPENDER"
+                     fileName="./logs/debug/debug.log"
+                     filePattern="./logs/debug/debug-%d{yyyy.MM.dd.HH}-%i.log">
+            <PatternLayout pattern="${logPattern}" charset="UTF-8"/>
+            <Policies>
+                <TimeBasedTriggeringPolicy interval="1" modulate="false"/>
+                <SizeBasedTriggeringPolicy size="100 MB"/>
+            </Policies>
+            <DefaultRolloverStrategy max="1000">
+                <Delete basePath="./logs/debug" maxDepth="1">
+                    <IfFileName glob="debug-*.log">
+                        <IfAccumulatedFileSize exceeds="2 GB"/>
+                    </IfFileName>
+                </Delete>
+            </DefaultRolloverStrategy>
+        </RollingFile>
 
-		<Logger name="INTERFACE_LOGGER" level="INFO" additivity="false">
-			<AppenderRef ref="INTERFACE_APPENDER" />
-			<AppenderRef ref="Console" />
-		</Logger>
+        <!-- 接口日志 -->
+        <RollingFile name="INTERFACE_APPENDER" fileName="./logs/interface/interface.log"
+                     filePattern="./logs/interface/interface-%d{yyyy.MM.dd.HH}-%i.log">
+            <PatternLayout pattern="${logPattern}" charset="UTF-8"/>
+            <Policies>
+                <TimeBasedTriggeringPolicy interval="1" modulate="false"/>
+                <SizeBasedTriggeringPolicy size="100 MB"/>
+            </Policies>
+            <DefaultRolloverStrategy max="1000">
+                <Delete basePath="./logs/interface" maxDepth="1">
+                    <IfFileName glob="interface-*.log">
+                        <IfAccumulatedFileSize exceeds="10 GB"/>
+                    </IfFileName>
+                </Delete>
+            </DefaultRolloverStrategy>
+        </RollingFile>
+    </Appenders>
 
-		<Logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="${commonLevel}" />
-		<Logger name="org.hibernate.type.descriptor.sql.BasicExtractor" level="${commonLevel}" />
-		<Logger name="org.hibernate.SQL" level="${commonLevel}" />
-		<Logger name="org.hibernate.type" level="${commonLevel}" />
-		<Logger name="org.hibernate.engine.QueryParameters" level="${commonLevel}" />
-		<Logger name="org.hibernate.engine.query.HQLQueryPlan" level="${commonLevel}" />
+    <Loggers>
+        <logger name="springfox.documentation" level="ERROR"/>
+        <logger name="org.springframework" level="ERROR"/>
+        <logger name="org.hibernate" level="ERROR"/>
+        <logger name="org.apache" level="ERROR"/>
+        <logger name="org.quartz" level="ERROR"/>
+        <logger name="org.docx4j" level="ERROR"/>
+        <logger name="cn.afterturn" level="ERROR"/>
+        <logger name="com.netflix" level="ERROR"/>
+        <logger name="com.aliyun" level="ERROR"/>
+        <logger name="io.lettuce" level="ERROR"/>
+        <logger name="io.netty" level="ERROR"/>
 
-		<Root level="INFO">
-			<AppenderRef ref="Console" />
-			<AppenderRef ref="DEBUG_APPENDER" />
-		</Root>
-	</Loggers>
+        <!--<logger name="org.springframework.jdbc.core.JdbcTemplate" level="DEBUG"/>-->
+        <!--<logger name="org.springframework.data.mongodb" level="DEBUG"/>-->
+        <!--<logger name="org.springframework.data.redis" level="DEBUG"/>-->
 
-</Configuration>
+        <Logger name="cn.com.qmth" level="${commonLevel}" additivity="false">
+            <AppenderRef ref="DEBUG_APPENDER"/>
+            <AppenderRef ref="Console"/>
+        </Logger>
+
+        <Logger name="INTERFACE_LOGGER" level="${commonLevel}" additivity="false">
+            <AppenderRef ref="INTERFACE_APPENDER"/>
+            <AppenderRef ref="Console"/>
+        </Logger>
+
+        <Root level="${commonLevel}">
+            <AppenderRef ref="Console"/>
+            <AppenderRef ref="DEBUG_APPENDER"/>
+        </Root>
+    </Loggers>
+
+</Configuration>

+ 0 - 19
jenkins-dev.sh

@@ -1,19 +0,0 @@
-#!/bin/bash
-pwd
-
-rm -rf ~/project/examcloud/examcloud-core-reports-distribution.zip
-rm -rf ~/project/examcloud/examcloud-core-reports/lib/
-rm -rf ~/project/examcloud/examcloud-core-reports/config/
-
-cp examcloud-core-reports-starter/target/examcloud-core-reports-distribution.zip ~/project/examcloud/
-
-cd  ~/project/examcloud/
-unzip -o examcloud-core-reports-distribution.zip
-
-cd examcloud-core-reports
-echo "--spring.profiles.active=dev --examcloud.startup.configCenterHost=localhost" > start.args
-echo "-server -Xms512m -Xmx512m  -XX:-UseGCOverheadLimit" > start.vmoptions
-
-bash stop.sh
-BUILD_ID=DONTKILLME
-bash start.sh jenkins

+ 0 - 18
jenkins-test.sh

@@ -1,18 +0,0 @@
-#!/bin/bash
-pwd
-
-rm -rf ~/project/examcloud/examcloud-core-reports-distribution.zip
-rm -rf ~/project/examcloud/examcloud-core-reports/lib/
-
-cp examcloud-core-reports-starter/target/examcloud-core-reports-distribution.zip ~/project/examcloud/
-
-cd  ~/project/examcloud/
-unzip -o examcloud-core-reports-distribution.zip
-
-cd examcloud-core-reports
-echo "--spring.profiles.active=test --examcloud.startup.configCenterHost=localhost" > start.args
-echo "-server -Xms512m -Xmx512m  -XX:-UseGCOverheadLimit" > start.vmoptions
-
-bash stop.sh
-BUILD_ID=DONTKILLME
-bash start.sh jenkins