fuliqi
2024-07-04 3f7cdb4c5096214765f935e9e8616730bcac1344
学习记录,引入caffeine
5个文件已修改
8个文件已添加
268 ■■■■■ 已修改文件
pom.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ycl/jxkg/config/CaffeineConfig.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ycl/jxkg/domain/entity/StudyRecord.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ycl/jxkg/domain/query/WebSocketQuery.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ycl/jxkg/enums/WebsocketCommendEnum.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ycl/jxkg/job/StudyRecordJob.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ycl/jxkg/mapper/StudyRecordMapper.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ycl/jxkg/server/WebsocketServer.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ycl/jxkg/service/EducationResourceService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ycl/jxkg/service/StudyRecordService.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ycl/jxkg/service/impl/EducationResourceServiceImpl.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ycl/jxkg/service/impl/StudyRecordServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/StudyRecordMapper.xml 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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>
src/main/java/com/ycl/jxkg/config/CaffeineConfig.java
New file
@@ -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();
    }
}
src/main/java/com/ycl/jxkg/domain/entity/StudyRecord.java
New file
@@ -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;
}
src/main/java/com/ycl/jxkg/domain/query/WebSocketQuery.java
New file
@@ -0,0 +1,9 @@
package com.ycl.jxkg.domain.query;
import lombok.Data;
@Data
public class WebSocketQuery {
    private String commend;
    private Integer id;
}
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;
src/main/java/com/ycl/jxkg/job/StudyRecordJob.java
New file
@@ -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:xp
 * @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("结束存学习时长");
    }
}
src/main/java/com/ycl/jxkg/mapper/StudyRecordMapper.java
New file
@@ -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);
}
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("【WebSocket消息】收到客户端消息:" + message);
    }
src/main/java/com/ycl/jxkg/service/EducationResourceService.java
@@ -37,4 +37,6 @@
     * @return
     */
    Result typeList();
    Result recordTime(Integer userId);
}
src/main/java/com/ycl/jxkg/service/StudyRecordService.java
New file
@@ -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> {
}
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();
    }
}
src/main/java/com/ycl/jxkg/service/impl/StudyRecordServiceImpl.java
New file
@@ -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 {
}
src/main/resources/mapper/StudyRecordMapper.xml
New file
@@ -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>