From 3f7cdb4c5096214765f935e9e8616730bcac1344 Mon Sep 17 00:00:00 2001
From: fuliqi <fuliqi@qq.com>
Date: 星期四, 04 七月 2024 11:04:44 +0800
Subject: [PATCH] 学习记录,引入caffeine

---
 src/main/java/com/ycl/jxkg/domain/query/WebSocketQuery.java               |    9 +
 src/main/java/com/ycl/jxkg/service/StudyRecordService.java                |   20 ++++
 src/main/java/com/ycl/jxkg/service/impl/StudyRecordServiceImpl.java       |   20 ++++
 src/main/java/com/ycl/jxkg/enums/WebsocketCommendEnum.java                |    1 
 src/main/java/com/ycl/jxkg/job/StudyRecordJob.java                        |   67 +++++++++++++
 src/main/java/com/ycl/jxkg/server/WebsocketServer.java                    |   16 +++
 src/main/java/com/ycl/jxkg/service/EducationResourceService.java          |    2 
 pom.xml                                                                   |    6 +
 src/main/java/com/ycl/jxkg/service/impl/EducationResourceServiceImpl.java |   30 +++++
 src/main/resources/mapper/StudyRecordMapper.xml                           |   21 ++++
 src/main/java/com/ycl/jxkg/domain/entity/StudyRecord.java                 |   24 ++++
 src/main/java/com/ycl/jxkg/mapper/StudyRecordMapper.java                  |   28 +++++
 src/main/java/com/ycl/jxkg/config/CaffeineConfig.java                     |   24 ++++
 13 files changed, 266 insertions(+), 2 deletions(-)

diff --git a/pom.xml b/pom.xml
index 4bf0f97..e34a11a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -42,6 +42,12 @@
 
 
     <dependencies>
+        <!-- caffeine -->
+        <dependency>
+            <groupId>com.github.ben-manes.caffeine</groupId>
+            <artifactId>caffeine</artifactId>
+            <version>3.0.5</version>
+        </dependency>
 
         <!-- rabbitmq -->
         <dependency>
diff --git a/src/main/java/com/ycl/jxkg/config/CaffeineConfig.java b/src/main/java/com/ycl/jxkg/config/CaffeineConfig.java
new file mode 100644
index 0000000..80f585e
--- /dev/null
+++ b/src/main/java/com/ycl/jxkg/config/CaffeineConfig.java
@@ -0,0 +1,24 @@
+package com.ycl.jxkg.config;
+
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.concurrent.TimeUnit;
+
+@Configuration
+public class CaffeineConfig {
+
+    @Bean
+    public Cache<String, Object> caffeineCache() {
+        return Caffeine.newBuilder()
+                // 璁剧疆鏈�鍚庝竴娆″啓鍏ユ垨璁块棶鍚庣粡杩囧浐瀹氭椂闂磋繃鏈�
+                .expireAfterWrite(600, TimeUnit.SECONDS)
+                // 鍒濆鐨勭紦瀛樼┖闂村ぇ灏�
+                .initialCapacity(100)
+                // 缂撳瓨鐨勬渶澶ф潯鏁�
+                .maximumSize(1000)
+                .build();
+    }
+}
diff --git a/src/main/java/com/ycl/jxkg/domain/entity/StudyRecord.java b/src/main/java/com/ycl/jxkg/domain/entity/StudyRecord.java
new file mode 100644
index 0000000..4c51e07
--- /dev/null
+++ b/src/main/java/com/ycl/jxkg/domain/entity/StudyRecord.java
@@ -0,0 +1,24 @@
+package com.ycl.jxkg.domain.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+@TableName("t_study_record")
+public class StudyRecord {
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+    @TableField("student_id")
+    private Integer studentId;
+    @TableField("study_time")
+    private Long studyTime;
+    //涓婁竴娆¤褰曟椂闂�
+    @TableField("last_time")
+    private Date lastTime;
+
+}
diff --git a/src/main/java/com/ycl/jxkg/domain/query/WebSocketQuery.java b/src/main/java/com/ycl/jxkg/domain/query/WebSocketQuery.java
new file mode 100644
index 0000000..d6ff159
--- /dev/null
+++ b/src/main/java/com/ycl/jxkg/domain/query/WebSocketQuery.java
@@ -0,0 +1,9 @@
+package com.ycl.jxkg.domain.query;
+
+import lombok.Data;
+
+@Data
+public class WebSocketQuery {
+    private String commend;
+    private Integer id;
+}
diff --git a/src/main/java/com/ycl/jxkg/enums/WebsocketCommendEnum.java b/src/main/java/com/ycl/jxkg/enums/WebsocketCommendEnum.java
index c0e3564..c4849d5 100644
--- a/src/main/java/com/ycl/jxkg/enums/WebsocketCommendEnum.java
+++ b/src/main/java/com/ycl/jxkg/enums/WebsocketCommendEnum.java
@@ -14,6 +14,7 @@
 
     DELAYED("delayed", "寤堕暱鑰冭瘯鏃堕棿"),
     FORCE_SUBMIT("forceSubmit", "寮哄埗鎻愪氦"),
+    RECORD_STUDY_TIME("recordStudyTime", "璁板綍瀛︿範鏃堕棿"),
     ;
 
     private final String commend;
diff --git a/src/main/java/com/ycl/jxkg/job/StudyRecordJob.java b/src/main/java/com/ycl/jxkg/job/StudyRecordJob.java
new file mode 100644
index 0000000..0167d8d
--- /dev/null
+++ b/src/main/java/com/ycl/jxkg/job/StudyRecordJob.java
@@ -0,0 +1,67 @@
+package com.ycl.jxkg.job;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.github.benmanes.caffeine.cache.Cache;
+import com.ycl.jxkg.domain.entity.StudyRecord;
+import com.ycl.jxkg.mapper.StudyRecordMapper;
+import com.ycl.jxkg.service.StudyRecordService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentMap;
+import java.util.stream.Collectors;
+
+/**
+ * @author锛歺p
+ * @date锛�2024/7/1 11:06
+ */
+@Component
+@RequiredArgsConstructor
+@Slf4j
+public class StudyRecordJob {
+
+    @Autowired
+    private Cache<String, Object> caffeineCache;
+    private final StudyRecordMapper studyRecordMapper;
+    private final StudyRecordService studyRecordService;
+
+    @Scheduled(fixedRate = 1200000) // 20鍒嗛挓鎵ц涓�娆�
+    private void updateStudyRecord() {
+        log.info("寮�濮嬪瓨瀛︿範鏃堕暱");
+        List<StudyRecord> cacheList = new ArrayList<>();
+        // 鍙栧嚭鎵�鏈夌紦瀛橀」
+        ConcurrentMap<String, Object> map = caffeineCache.asMap();
+        for (Map.Entry<String, Object> entry : map.entrySet()) {
+            String key = entry.getKey();
+            if (key.startsWith("STUDENT_")) {
+                StudyRecord studyRecord = (StudyRecord) entry.getValue();
+                cacheList.add(studyRecord);
+            }
+        }
+        List<Integer> studentIds = cacheList.stream().map(StudyRecord::getStudentId).collect(Collectors.toList());
+        if (!CollectionUtils.isEmpty(studentIds)) {
+            //鏁版嵁搴撲腑宸茬粡瀛樺湪鐨勫鐢熸暟鎹�
+            QueryWrapper<StudyRecord> wrapper = new QueryWrapper<>();
+            wrapper.in("student_id", studentIds);
+            List<StudyRecord> studyRecords = studyRecordMapper.selectList(wrapper);
+            for (StudyRecord record : studyRecords) {
+                for (StudyRecord cacheRecord : cacheList) {
+                    if (record.getStudentId().equals(cacheRecord.getStudentId())) {
+                        cacheRecord.setId(record.getId());
+                        cacheRecord.setStudyTime(record.getStudyTime() + cacheRecord.getStudyTime());
+                    }
+                }
+            }
+            studyRecordService.saveOrUpdateBatch(cacheList);
+        }
+        caffeineCache.invalidateAll(map.keySet());
+        log.info("缁撴潫瀛樺涔犳椂闀�");
+    }
+}
diff --git a/src/main/java/com/ycl/jxkg/mapper/StudyRecordMapper.java b/src/main/java/com/ycl/jxkg/mapper/StudyRecordMapper.java
new file mode 100644
index 0000000..45e14a7
--- /dev/null
+++ b/src/main/java/com/ycl/jxkg/mapper/StudyRecordMapper.java
@@ -0,0 +1,28 @@
+package com.ycl.jxkg.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ycl.jxkg.domain.entity.StudyRecord;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 浼氳琛� Mapper 鎺ュ彛
+ *
+ * @author flq
+ * @since 2024-06-17
+ */
+@Mapper
+public interface StudyRecordMapper extends BaseMapper<StudyRecord> {
+
+    /**
+     * id鏌ユ壘
+     * @param
+     * @return
+     */
+    StudyRecord getByStudentId(Integer studentId);
+
+
+    void updateBatch(List<StudyRecord> list);
+
+}
diff --git a/src/main/java/com/ycl/jxkg/server/WebsocketServer.java b/src/main/java/com/ycl/jxkg/server/WebsocketServer.java
index 3e27189..9886607 100644
--- a/src/main/java/com/ycl/jxkg/server/WebsocketServer.java
+++ b/src/main/java/com/ycl/jxkg/server/WebsocketServer.java
@@ -1,8 +1,16 @@
 package com.ycl.jxkg.server;
 
+import com.alibaba.fastjson.JSONObject;
+import com.google.gson.JsonObject;
+import com.ycl.jxkg.domain.entity.Message;
+import com.ycl.jxkg.domain.query.WebSocketQuery;
+import com.ycl.jxkg.enums.WebsocketCommendEnum;
+import com.ycl.jxkg.service.EducationResourceService;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
+import javax.servlet.http.HttpSession;
 import javax.websocket.OnClose;
 import javax.websocket.OnMessage;
 import javax.websocket.OnOpen;
@@ -23,6 +31,8 @@
 @ServerEndpoint("/websocket/{userId}")
 public class WebsocketServer {
 
+    @Autowired
+    private EducationResourceService educationResourceService;
     /**
      * 绾跨▼瀹夊叏鐨勬棤搴忕殑闆嗗悎
      */
@@ -56,6 +66,12 @@
 
     @OnMessage
     public void onMessage(String message) {
+        WebSocketQuery webSocketQuery = JSONObject.parseObject(message, WebSocketQuery.class);
+        String commend = webSocketQuery.getCommend();
+        Integer userId = webSocketQuery.getId();
+        if(WebsocketCommendEnum.RECORD_STUDY_TIME.getCommend().equals(commend)){
+            educationResourceService.recordTime(userId);
+        }
         log.info("銆怶ebSocket娑堟伅銆戞敹鍒板鎴风娑堟伅锛�" + message);
     }
 
diff --git a/src/main/java/com/ycl/jxkg/service/EducationResourceService.java b/src/main/java/com/ycl/jxkg/service/EducationResourceService.java
index 304f10c..8864d9c 100644
--- a/src/main/java/com/ycl/jxkg/service/EducationResourceService.java
+++ b/src/main/java/com/ycl/jxkg/service/EducationResourceService.java
@@ -37,4 +37,6 @@
      * @return
      */
     Result typeList();
+
+    Result recordTime(Integer userId);
 }
diff --git a/src/main/java/com/ycl/jxkg/service/StudyRecordService.java b/src/main/java/com/ycl/jxkg/service/StudyRecordService.java
new file mode 100644
index 0000000..9c24875
--- /dev/null
+++ b/src/main/java/com/ycl/jxkg/service/StudyRecordService.java
@@ -0,0 +1,20 @@
+package com.ycl.jxkg.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ycl.jxkg.base.Result;
+import com.ycl.jxkg.domain.entity.Meet;
+import com.ycl.jxkg.domain.entity.StudyRecord;
+import com.ycl.jxkg.domain.form.MeetForm;
+import com.ycl.jxkg.domain.query.MeetQuery;
+
+import java.util.List;
+
+/**
+ * 瀛︿範璁板綍 鏈嶅姟绫�
+ *
+ * @author flq
+ * @since 2024-06-17
+ */
+public interface StudyRecordService extends IService<StudyRecord> {
+
+}
diff --git a/src/main/java/com/ycl/jxkg/service/impl/EducationResourceServiceImpl.java b/src/main/java/com/ycl/jxkg/service/impl/EducationResourceServiceImpl.java
index 374f718..c29ac89 100644
--- a/src/main/java/com/ycl/jxkg/service/impl/EducationResourceServiceImpl.java
+++ b/src/main/java/com/ycl/jxkg/service/impl/EducationResourceServiceImpl.java
@@ -1,20 +1,22 @@
 package com.ycl.jxkg.service.impl;
 
 import com.alibaba.fastjson.JSON;
+import com.github.benmanes.caffeine.cache.Cache;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import com.ycl.jxkg.base.Result;
 import com.ycl.jxkg.context.WebContext;
 import com.ycl.jxkg.domain.entity.EducationResource;
 import com.ycl.jxkg.domain.entity.Subject;
+import com.ycl.jxkg.domain.entity.StudyRecord;
 import com.ycl.jxkg.domain.vo.admin.education.EducationResourceVO;
-import com.ycl.jxkg.domain.vo.student.education.StudentOnlineVO;
 import com.ycl.jxkg.mapper.ClassesUserMapper;
 import com.ycl.jxkg.mapper.EducationResourceMapper;
 import com.ycl.jxkg.mapper.SubjectMapper;
 import com.ycl.jxkg.service.EducationResourceService;
 import lombok.RequiredArgsConstructor;
 import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.util.CollectionUtils;
 
@@ -35,7 +37,8 @@
     private final SubjectMapper subjectMapper;
     private final WebContext webContext;
     private final ClassesUserMapper classesUserMapper;
-
+    @Autowired
+    private Cache<String, Object> caffeineCache;
     @Override
     public Result add(EducationResourceVO form) {
         EducationResource educationResource = new EducationResource();
@@ -109,4 +112,27 @@
         List<Subject> subjects = subjectMapper.allSubject();
         return Result.ok(subjects);
     }
+    //璁板綍瑙嗛鏃堕暱
+    @Override
+    public Result recordTime(Integer userId) {
+        if (userId ==null){
+            throw new RuntimeException("鐢ㄦ埛id涓虹┖");
+        }
+        StudyRecord studyRecord = (StudyRecord) caffeineCache.getIfPresent("STUDENT_"+userId);
+        if (studyRecord != null) {
+            //瀛樺湪缂撳瓨
+            Date lastTime = studyRecord.getLastTime();
+            Date now = new Date();
+            studyRecord.setStudyTime(studyRecord.getStudyTime()+(now.getTime()-lastTime.getTime())/1000);
+            studyRecord.setLastTime(now);
+        }else {
+            //涓嶅瓨鍦ㄧ紦瀛�
+            studyRecord = new StudyRecord();
+            studyRecord.setStudentId(userId);
+            studyRecord.setStudyTime(0L);
+            studyRecord.setLastTime(new Date());
+        }
+        caffeineCache.put("STUDENT_" + userId, studyRecord);
+        return Result.ok();
+    }
 }
diff --git a/src/main/java/com/ycl/jxkg/service/impl/StudyRecordServiceImpl.java b/src/main/java/com/ycl/jxkg/service/impl/StudyRecordServiceImpl.java
new file mode 100644
index 0000000..56602cd
--- /dev/null
+++ b/src/main/java/com/ycl/jxkg/service/impl/StudyRecordServiceImpl.java
@@ -0,0 +1,20 @@
+package com.ycl.jxkg.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ycl.jxkg.domain.entity.StudyRecord;
+import com.ycl.jxkg.mapper.StudyRecordMapper;
+import com.ycl.jxkg.service.StudyRecordService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+/**
+ * 瀛︿範璁板綍 鏈嶅姟瀹炵幇绫�
+ *
+ * @author flq
+ * @since 2024-06-17
+ */
+@Service
+@RequiredArgsConstructor
+public class StudyRecordServiceImpl extends ServiceImpl<StudyRecordMapper, StudyRecord> implements StudyRecordService {
+
+}
diff --git a/src/main/resources/mapper/StudyRecordMapper.xml b/src/main/resources/mapper/StudyRecordMapper.xml
new file mode 100644
index 0000000..3b371cb
--- /dev/null
+++ b/src/main/resources/mapper/StudyRecordMapper.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ycl.jxkg.mapper.StudyRecordMapper">
+
+    <!-- 閫氱敤鏌ヨ鏄犲皠缁撴灉 -->
+    <resultMap id="BaseResultMap" type="com.ycl.jxkg.domain.entity.StudyRecord">
+        <result column="id" property="id"/>
+        <result column="student_id" property="studentId"/>
+        <result column="study_time" property="studyTime"/>
+        <result column="last_time" property="lastTime"/>
+    </resultMap>
+
+
+    <select id="getByStudentId" resultMap="BaseResultMap">
+        SELECT *
+        from t_study_record
+        WHERE student_id = #{studentId}
+    </select>
+
+
+</mapper>

--
Gitblit v1.8.0