微服务的编程测评系统19-我的消息功能-竞赛排名功能

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • [1. 我的消息功能](#1. 我的消息功能)
    • [1.1 业务分析](#1.1 业务分析)
    • [1.2 消息发送](#1.2 消息发送)
    • [1.3 消息列表展示](#1.3 消息列表展示)
    • [1.4 前端开发](#1.4 前端开发)
  • [2. 竞赛排名功能](#2. 竞赛排名功能)
  • 总结8

前言

1. 我的消息功能

1.1 业务分析

这个是站内通信

我的消息功能

站内信:网站内部的一种通信方式。

1.用户和用户之间的通信。(点对点)

2.管理员/系统 和 某个用户之间的通信。(点对点) ===》竞赛结果的通信消息

  1. 管理员/系统 和 某个用户群(指的是满足某一条件的用户的群体)之间的通信。(点对面)

而且每个用户的消息都不一样

这个是给用户群发消息,福利信息每个用户的消息都一样---》点对面

因为每个用户消息不一样---》点对点

消息的话我们还要设计数据库

因为如果是消息群的话,那么就会把相同的消息发给多个人,所以我们可以设计两个表,一个消息内容表,一个是消息和用户的对应表,这样就不会相同消息发给多个人了

java 复制代码
消息内容表
create table tb_message_text(
text_id  bigint unsigned NOT NULL COMMENT '消息内容id(主键)',
message_title varchar(10)  NOT NULL COMMENT '消息标题',
message_content varchar(200)  NOT NULL COMMENT '消息内容',
create_by    bigint unsigned not null  comment '创建人',
create_time  datetime not null comment '创建时间',
update_by    bigint unsigned  comment '更新人',
update_time  datetime comment '更新时间',
primary key (text_id)
)
java 复制代码
# 消息表
create table tb_message(
message_id  bigint unsigned NOT NULL COMMENT '消息id(主键)',
text_id  bigint unsigned NOT NULL COMMENT '消息内容id(主键)',
send_id  bigint unsigned NOT NULL COMMENT '消息发送人id',
rec_id  bigint unsigned NOT NULL COMMENT '消息接收人id',
create_by    bigint unsigned not null  comment '创建人',
create_time  datetime not null comment '创建时间',
update_by    bigint unsigned  comment '更新人',
update_time  datetime comment '更新时间',
primary key (message_id)
);

消息如何产生---》竞赛结果通知消息--》凌晨统计排名,对当天结束的竞赛进行排名的统计---》产生消息---》竞赛结束时间不能超过晚上十点--->把消息存在数据库中,然后查询就可以了

然后消息也要存在redis中,不然还是太慢了

一个是user:message:list:userId,存储list,每个元素是消息id

还有一个是message:detail:textId,存储的是JSON,消息详情

1.2 消息发送

通过定时任务生成消息--.>存储到数据库和缓存中,获取消息列表的时候就可以从缓存中获取了,注意生成消息的时候只存在缓存中

缓存没有修改和删除

java 复制代码
@Data
public class UserScore {
    private Long examId;
    private Long userId;
    private Integer score;
}

这个是新增加的类

java 复制代码
<?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.ck.job.mapper.user.UserSubmitMapper">

    <select id="selectUserScoreList" resultType="com.ck.job.domain.user.UserScore">
        SELECT
        user_id,
        exam_id,
        sum(score) as score
        FROM
        tb_user_submit
        <where>
            <foreach collection="examIdSet" open="exam_id in (" close=")" item="examId" separator="," > 
                #{examId}
            </foreach>
        </where>
        GROUP BY 
            user_id , exam_id
        ORDER BY
            score DESC
    </select>
</mapper>

这个是使用的xml

java 复制代码
@Service
public class MessageServiceImpl extends ServiceImpl<MessageMapper, Message> implements IMessageService{
    @Override
    public boolean batchInsert(List<Message> messageList){
        return saveBatch(messageList);
    }
}
java 复制代码
@Service
public class MessageTextServiceImpl extends ServiceImpl<MessageTextMapper, MessageText> implements IMessageTextService {
    @Override
    public boolean batchInsert(List<MessageText> messageTextList){
        return saveBatch(messageTextList);
    }
}

这个是批量插入的service方法

java 复制代码
    public static final Long SYSTEM_USER_ID = 1L;

因为createBy无法获取用户Id,所以我们提前要设置好

java 复制代码
    public static final String USER_MESSAGE_LIST_USERID = "user:message:list:";
    public static final String MESSAGE_DETAIL_MESSAGEID = "message:detail:";

这个是存入缓存的结构

java 复制代码
@Data
public class MessageCacheVO {
    private String messageTitle;

    private String messageContent;
}

这个是存入信息详细数据的类

最后展示定时器的代码

java 复制代码
    @XxlJob("examResultHandler")
    public void examResultHandler(){
        log.info("*****examResultHandler:凌晨统计排名*****");
        //先从数据库中获取所有已经结束的竞赛列表
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime minusDays = now.minusDays(1);//获取小于一天的时间
        List<Exam> examList = examMapper.selectList(new LambdaQueryWrapper<Exam>()
                .select(Exam::getExamId,Exam::getTitle)
                .ge(Exam::getEndTime, minusDays)
                .le(Exam::getEndTime, now)//小于等于当前时间
                .eq(Exam::getStatus, Constants.TRUE));//已经发布)
        if(CollectionUtil.isEmpty(examList)){
            return;
        }
        Set<Long> examIdSet = examList.stream().map(Exam::getExamId).collect(Collectors.toSet());
        //然后根据examIdSet获取所有用户的分数
        List<UserScore> userScoreList = userSubmitMapper.selectUserScoreList(examIdSet);
        Map<Long, List<UserScore>> examIdUserScoreMap = userScoreList.stream().collect(Collectors.groupingBy(UserScore::getExamId));
        //按照examId分组,那么分数排名就已经OK了
        saveMessage(examList,examIdUserScoreMap);
    }

    private void saveMessage(List<Exam> examList, Map<Long, List<UserScore>> examIdUserScoreMap ) {
        List<Message> messageList = new ArrayList<>();//插入msg与用户对应信息数据库
        List<MessageText> messageTextList = new ArrayList<>();//插入数据库,msg详细信息
        for(Exam exam: examList){
            Long examId = exam.getExamId();
            List<UserScore> userScoreList = examIdUserScoreMap.get(examId);
            int userTotal = userScoreList.size();
            int rank  = 1;
            for (UserScore userScore : userScoreList){
                String msgTitle = exam.getTitle()+"------排名情况";
                String msgContent = "你参加的竞赛:"+ exam.getTitle()+",你的分数为:"
                        +userScore.getScore()+",总人数:+"+userTotal +",你的排名为:"+rank;
                rank++;
                MessageText messageText = new MessageText();
                messageText.setMessageTitle(msgTitle);
                messageText.setMessageContent(msgContent);
                messageText.setCreateBy(Constants.SYSTEM_USER_ID);
                messageTextList.add(messageText);
                Message message = new Message();
                message.setSendId(Constants.SYSTEM_USER_ID);
                message.setRecId(userScore.getUserId());
                message.setCreateBy(Constants.SYSTEM_USER_ID);
                messageList.add(message);
            }
        }
        messageTextService.batchInsert(messageTextList);
        //给messageList添加messageId
        Map<String, MessageCacheVO> messageCacheVOMap = new HashMap<>();//存入redis,信息详细数据
        for(int i=0;i<messageTextList.size();i++){
            MessageText messageText = messageTextList.get(i);
            Message message = messageList.get(i);
            message.setTextId(messageText.getTextId());

            MessageCacheVO messageCacheVO = new MessageCacheVO();
            messageCacheVO.setMessageContent(messageText.getMessageContent());
            messageCacheVO.setMessageTitle(messageText.getMessageTitle());
            String messageDetailKey = getMessageDetailKey(messageText.getTextId());
            messageCacheVOMap.put(messageDetailKey,messageCacheVO);
        }
        redisService.multiSet(messageCacheVOMap);
        messageService.batchInsert(messageList);
        //存入缓存,用户的信息列表---》那么就要把信息按照userId进行分组了
        Map<Long, List<Message>> userMsgMap = messageList.stream().collect(Collectors.groupingBy(Message::getRecId));
        Iterator<Map.Entry<Long, List<Message>>> iterator = userMsgMap.entrySet().iterator();
        while(iterator.hasNext()){
            Map.Entry<Long, List<Message>> entry = iterator.next();
            Long userId = entry.getKey();
            String userMessageListKey = getUserMessageListKey(userId);
            List<Message> userMessageList = entry.getValue();
            List<Long> userMsgIdList = userMessageList.stream().map(Message::getTextId).toList();
            redisService.rightPushAll(userMessageListKey,userMsgIdList);
        }
//        for (Map.Entry<Long, List<Message>> entry : userMsgMap.entrySet()) {
//            Long userId = entry.getKey();
//            String userMessageListKey = getUserMessageListKey(userId);
//            List<Message> userMessageList = entry.getValue();
//            List<Long> userMsgIdList = userMessageList.stream().map(Message::getTextId).toList();
//            redisService.rightPushAll(userMessageListKey, userMsgIdList);
//        }
    }
    private String getUserMessageListKey(Long userId) {
        return CacheConstants.USER_MESSAGE_LIST_USERID + userId;
    }

    private String getMessageDetailKey(Long messageTextId) {
        return CacheConstants.MESSAGE_DETAIL_MESSAGEID + messageTextId;
    }

我们使用的都是批量插入

在 Java 中,Iterator(迭代器)初始化后,其初始状态是指向集合中第一个元素的「前面」

1.3 消息列表展示

创建一个新的controller,UserMessageController

java 复制代码
@RestController
@RequestMapping("/user/message")
@Tag(name = "C端用户信息接口")
@Slf4j
public class UserMessageController {
    @Autowired
    private IUserMessageService userMessageService;
    @GetMapping("/list")
    @Operation(description = "获取用户接收到的信息")
    public TableDataInfo list(PageQueryDTO dto){
        log.info("获取用户接收到的信息,PageQueryDTO:{}", dto);
        return userMessageService.list(dto);
    }
}

直接拷贝以前获取竞赛列表的代码,然后改吧改吧

java 复制代码
@Data
public class MessageCacheVO {
    private Long textId;
    private String messageTitle;

    private String messageContent;
}

这个类要完善一下,因为从数据库中可以查询出这个类,然后刷新缓存就要用到messageTextId,记得定时器存入缓存的时候,这个字段也要完善

java 复制代码
<?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.ck.friend.mapper.message.MessageTextMapper">
    <select id="selectUserMsgList" resultType="com.ck.friend.domain.message.vo.MessageCacheVO">
        SELECT
        text_id,
        message_title,
        message_content,
        FROM
        tb_message m
        JOIN
        tb_message_text tm
        ON
        m.text_id = tm.text_id
        <where>
            m.rec_id = #{userId}
        </where>
        ORDER BY
        t.create_time DESC
    </select>
</mapper>

这个是根据userId查询它的所有的信息的xml

java 复制代码
    @Override
    public TableDataInfo list(PageQueryDTO dto) {
        Long userId= ThreadLocalUtil.get(Constants.USER_ID,Long.class);
        Long listSize = userMessageCacheManager.getListSize(userId);
        List<MessageCacheVO> list ;
        if(listSize==null||listSize==0){
            //说明缓存中没有数据,所以要先从数据库中获取数据,然后存入redis
            PageHelper.startPage(dto.getPageNum(), dto.getPageSize());
            list = messageTextMapper.selectUserMsgList(userId);
            userMessageCacheManager.refreshCache(userId);
            long total = new PageInfo<>(list).getTotal();
            return TableDataInfo.success(list, total);
        }else{
            //直接从redis中获取数据
            list = userMessageCacheManager.getUserMsgList(dto,userId);
            listSize = userMessageCacheManager.getListSize(userId);
            return TableDataInfo.success(list, listSize);
        }
    }

这是service代码

然后是userMessageCacheManager的代码

java 复制代码
@Component
public class UserMessageCacheManager {
    @Autowired
    private RedisService redisService;
    @Autowired
    private MessageTextMapper messageTextMapper;
    public Long getListSize(Long userId) {
        String userMessageListKey = getUserMessageListKey(userId);
        return redisService.getListSize(userMessageListKey);
    }

    public void refreshCache(Long userId) {
        List<MessageCacheVO> messageCacheVOList = new ArrayList<>();
        messageCacheVOList = messageTextMapper.selectUserMsgList(userId);//没有分页
        List<Long> userMsgIdList = messageCacheVOList.stream().map(MessageCacheVO::getTextId).toList();
        if (CollectionUtil.isEmpty(messageCacheVOList)) {
            return;
        }
        redisService.rightPushAll(getUserMessageListKey(userId), userMsgIdList);      //刷新列表缓存
        //刷新信息详情缓存
        Map<String, MessageCacheVO> messageCacheVOMap = new HashMap<>();
        for (MessageCacheVO messageCacheVO : messageCacheVOList) {
            messageCacheVOMap.put(getMessageDetailKey(messageCacheVO.getTextId()),messageCacheVO);
        }
        redisService.multiSet(messageCacheVOMap);  //刷新详情缓存
    }

    public List<MessageCacheVO> getUserMsgList(PageQueryDTO dto, Long userId) {
        int start = (dto.getPageNum() - 1) * dto.getPageSize();
        int end = start + dto.getPageSize() - 1; //下标需要 -1
        String userMessageListKey = getUserMessageListKey(userId);
        List<Long> messageIdList = redisService.getCacheListByRange(userMessageListKey, start, end, Long.class);
        List<MessageCacheVO> messageCacheVOList = assembleExamVOList(messageIdList);//从缓存中加载详情
        if (CollectionUtil.isEmpty(messageCacheVOList)) {
            //说明redis中数据可能有问题 从数据库中查数据并且重新刷新缓存
            messageCacheVOList = getMessageVOListByDB(dto,userId); //从数据库中获取数据
            refreshCache(userId);
        }
        return messageCacheVOList;
    }

    private List<MessageCacheVO> getMessageVOListByDB(PageQueryDTO dto, Long userId) {
        PageHelper.startPage(dto.getPageNum(), dto.getPageSize());
        return messageTextMapper.selectUserMsgList(userId);
    }

    private List<MessageCacheVO> assembleExamVOList(List<Long> messageIdList) {
        if (CollectionUtil.isEmpty(messageIdList)) {
            //说明redis当中没数据 从数据库中查数据并且重新刷新缓存
            return null;
        }
        //拼接redis当中key的方法 并且将拼接好的key存储到一个list中
        List<String> detailKeyList = new ArrayList<>();
        for (Long messageTextId : messageIdList) {
            detailKeyList.add(getMessageDetailKey(messageTextId));
        }
        List<MessageCacheVO> messageCacheVOList = redisService.multiGet(detailKeyList, MessageCacheVO.class);
        CollUtil.removeNull(messageCacheVOList);
        if (CollectionUtil.isEmpty(messageCacheVOList) || messageCacheVOList.size() != messageIdList.size()) {
            //说明redis中数据有问题 从数据库中查数据并且重新刷新缓存
            return null;
        }
        return messageCacheVOList;
    }

    private String getUserMessageListKey(Long userId) {
        return CacheConstants.USER_MESSAGE_LIST_USERID + userId;
    }

    private String getMessageDetailKey(Long messageTextId) {
        return CacheConstants.MESSAGE_DETAIL_MESSAGEID + messageTextId;
    }
}

1.4 前端开发

创建文件UserMessage.vue

java 复制代码
<template>
    <div class="message-list">
        <div class="message-list-block">
            <div class="message-list-header">
                <span class="ms-title">我的消息</span>
                <span class="message-list-back" @click="goBack()">返回</span>
            </div>
            <div class="mesage-list-content" v-for="(item, index) in messageList" :key="index">
                <img src="@/assets/message/notice.png" width="50px" class="image" />
                <div class="message-content">
                    <div class="title-box">
                        <div class="title">
                            {{ item.messageTitle }}
                        </div>
                    </div>
                    <div class="content">{{ item.messageContent }}</div>
                </div>
                <el-button class="mesage-button" type="text" @click.stop="handlerDelete(item)">删除</el-button>
            </div>
            <div class="message-pagination">
                <!-- 增加分页展示器 -->
                <el-pagination background layout="total, sizes, prev, pager, next, jumper" :total="total"
                    v-model:current-page="params.pageNum" v-model:page-size="params.pageSize"
                    :page-sizes="[5, 10, 15, 20]" @size-change="handleSizeChange"
                    @current-change="handleCurrentChange" />
            </div>
        </div>
    </div>
</template>

<script setup>
import { getMessageListService } from "@/apis/message"
import router from "@/router"
import { reactive, ref } from "vue"

const messageList = ref([]) //消息列表

const total = ref(0)
const params = reactive({
    pageNum: 1,
    pageSize: 10,
})

//消息列表
async function getMessageList() {
    const ref = await getMessageListService(params)
    messageList.value = ref.rows
    total.value = ref.total
}
getMessageList()

const goBack = () => {
    router.go(-1)
}
// 分页
function handleSizeChange(newSize) {
    params.pageNum = 1
    getMessageList()
}

function handleCurrentChange(newPage) {
    getMessageList()
}
</script>
java 复制代码
import service from "@/utils/request";

export function getMessageListService(params) {
  return service({
    url: "/user/message/list",
    method: "get",
    params,
  });
}

然后就可以测试了

我们创建三个用户来测试一下

java 复制代码
    <select id="selectUserMsgList" resultType="com.ck.friend.domain.message.vo.MessageCacheVO">
        SELECT
        tm.text_id,
        tm.message_title,
        tm.message_content
        FROM
        tb_message m
        JOIN
        tb_message_text tm
        ON
        m.text_id = tm.text_id
        WHERE
            m.rec_id = #{userId}
        ORDER BY
        m.create_time DESC
    </select>

然后发现xml文件有问题

修改一下

成功了

2. 竞赛排名功能

就是历史竞赛那里的排名功能

未完赛没有查看排名功能

排名和得分和用户id都有了

但是得分和排名还没有存储---》tb_user_exam有排名的得分字段

----》不用重新统计了--》直接获取---》存入数据库,在定时器的时候

然后还要存入缓存--》key为exam:rank:list:examId

value为userId?不是,第一我们是为了防止一个数据存储多份,所以才存id,但是这里的排名是不会存储多份的,因为不同竞赛的排名和分数是不一样的,而且排名和分数是不能修改的

所以value就是需要什么存什么--》json-->examRank,nickName,score,其中examRank和score在不同竞赛中一般是不同的

但是nickName是会重复的,而且用户修改nickname还要改redis---》所以可以存userId,然后由userId获取redis中的nickname

我们现在定时器那里,修改tb_user_exam表,完善score和exam_rank字段

然后往redis中存入排名数据

java 复制代码
@Data
public class UserScore {
    private Long examId;
    private Long userId;
    private Integer score;
    private Integer examRank;
}

完善一下这个类,这个是可以直接存入数据库中,什么都有了

java 复制代码
<?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.ck.job.mapper.user.UserExamMapper">

    <update id="updateScoreAndExamRank">
        <foreach collection="userScoreList" item="item" separator=";">
            UPDATE
            tb_user_exam
            SET
            score = #{item.score}, exam_rank = #{item.examRank}
            WHERE
            exam_id = #{item.examId} AND user_id = #{item.userId}
        </foreach>
    </update>
</mapper>

这个是修改数据库tb_user_exam的xml语句

java 复制代码
    public static final String EXAM_RANK_LIST_EXAMID = "exam:rank:list:";
java 复制代码
    private String getExamRankListKey(Long examId) {
        return CacheConstants.EXAM_RANK_LIST_EXAMID + examId;
    }

这个是redis的key

在savemessage方法中

java 复制代码
        for(Exam exam: examList){
            Long examId = exam.getExamId();
            List<UserScore> userScoreList = examIdUserScoreMap.get(examId);
            int userTotal = userScoreList.size();
            int rank  = 1;
            for (UserScore userScore : userScoreList){
                String msgTitle = exam.getTitle()+"------排名情况";
                String msgContent = "你参加的竞赛:"+ exam.getTitle()+",你的分数为:"
                        +userScore.getScore()+",总人数:"+userTotal +",你的排名为:"+rank;
                userScore.setExamRank(rank);
                rank++;
                MessageText messageText = new MessageText();
                messageText.setMessageTitle(msgTitle);
                messageText.setMessageContent(msgContent);
                messageText.setCreateBy(Constants.SYSTEM_USER_ID);
                messageTextList.add(messageText);
                Message message = new Message();
                message.setSendId(Constants.SYSTEM_USER_ID);
                message.setRecId(userScore.getUserId());
                message.setCreateBy(Constants.SYSTEM_USER_ID);
                messageList.add(message);
            }
            userExamMapper.updateScoreAndExamRank(userScoreList);
            redisService.rightPushAll(getExamRankListKey(examId),userScoreList);
        }

然后是创建查询的controller

java 复制代码
    @GetMapping("/rank/list")
    public TableDataInfo rankList(RankQueryDTO rankQueryDTO){
        log.info("获取竞赛排名列表信息,rankQueryDTO:{}", rankQueryDTO);
        return examService.rankList(rankQueryDTO);
    }
java 复制代码
@Data
public class RankQueryDTO extends PageQueryDTO {
    private Long examId;
}

然后service

java 复制代码
@Data
public class ExamRankCacheVO {
    private String nickName;
    private Long userId;
    private Integer score;
    private Integer examRank;
}

这个类是从缓存中要获取的数据,其中只用获取后面三个字段,第一个字段是再次获取的,再次从redis中获取

java 复制代码
    <select id="selectExamRankCacheVOList" resultType="com.ck.friend.domain.exam.vo.ExamRankCacheVO">
        SELECT
            user_id,
            score,
            exam_rank
        FROM
            tb_user_exam
        WHERE
            exam_id = #{examId}
        ORDER BY
            exam_rank 
    </select>

这个是从数据库中获取排名信息的xml语句,根据examId

java 复制代码
    @Override
    public TableDataInfo rankList(RankQueryDTO rankQueryDTO) {
        Long listSize = examCacheManager.getExamRankListSize(rankQueryDTO.getExamId());
        List<ExamRankCacheVO> list;
        if(listSize==null||listSize==0){
            //说明缓存中没有数据,所以要先从数据库中获取数据,然后存入redis
            PageHelper.startPage(rankQueryDTO.getPageNum(), rankQueryDTO.getPageSize());
            list = examMapper.selectExamRankCacheVOList(rankQueryDTO.getExamId());
            examCacheManager.refreshExamRankListCache(rankQueryDTO.getExamId());
            listSize  = new PageInfo<>(list).getTotal();
        }else{
            //直接从redis中获取数据
            list = examCacheManager.getExamRankList(rankQueryDTO);
        }
        assembleExamRankList(list);
        return TableDataInfo.success(list, listSize);
    }

    private void assembleExamRankList(List<ExamRankCacheVO> list) {
        if(CollectionUtil.isEmpty(list)){
            return;
        }
        for (ExamRankCacheVO examRankCacheVO : list) {
            UserVO user = userCacheManager.getUserById(examRankCacheVO.getUserId());
            examRankCacheVO.setNickName(user.getNickName());
        }
    }

然后是examCacheManager中的方法

java 复制代码
    //竞赛排名
    public Long getExamRankListSize(Long examId) {
        return redisService.getListSize(getExamRankListKey(examId));
    }

    private String getExamRankListKey(Long examId) {
        return CacheConstants.EXAM_RANK_LIST_EXAMID + examId;
    }

    public void refreshExamRankListCache(Long examId) {
        //没有分页查询
        List<ExamRankCacheVO> examRankCacheVOList = examMapper.selectExamRankCacheVOList(examId);
        redisService.rightPushAll(getExamRankListKey(examId),examRankCacheVOList);
    }


    public List<ExamRankCacheVO> getExamRankList(RankQueryDTO rankQueryDTO) {
        int start = (rankQueryDTO.getPageNum() - 1) * rankQueryDTO.getPageSize();
        int end = start + rankQueryDTO.getPageSize() - 1; //下标需要 -1
        return redisService.getCacheListByRange(getExamRankListKey(rankQueryDTO.getExamId()),start,end,ExamRankCacheVO.class);
    }

这样就OK了

然后拷贝前端代码到exam.vue

java 复制代码
  <el-dialog v-model="dialogVisible" width="600px" top="30vh" :show-close="true" :close-on-click-modal="false"
    :close-on-press-escape="false" class="oj-login-dialog-centor" center>
    <el-table :data="examRankList">
      <el-table-column label="排名" prop="examRank" />
      <el-table-column label="用户昵称" prop="nickName" />
      <el-table-column label="用户得分" prop="score" />
    </el-table>
    <el-pagination class="range_page" background layout="total, sizes, prev, pager, next, jumper" :total="rankTotal"
      v-model:current-page="rankParams.pageNum" v-model:page-size="rankParams.pageSize" :page-sizes="[5, 10, 15, 20]"
      @size-change="handleRankSizeChange" @current-change="handleRankCurrentChange" />
  </el-dialog>
java 复制代码
//竞赛排名

const rankParams = reactive({
  examId:'',
  pageNum: 1,
  pageSize: 9,
})
const examRankList = ref([])
const rankTotal = ref(0)

// 分页
function handleRankSizeChange(newSize) {
  rankParams.pageNum = 1
  getExamRankList()
}

function handleRankCurrentChange(newPage) {
  getExamRankList()
}

const dialogVisible = ref(false)

async function getExamRankList() {
  const result = await getExamRankListService(rankParams)
  examRankList.value = result.rows
  rankTotal.value = result.total
}

function togglePopover(examId) {
  dialogVisible.value = true
  rankParams.examId = examId
  getExamRankList()
}
java 复制代码
export function getExamRankListService(params) {
  return service({
    url: "/exam/rank/list",
    method: "get",
    params,
  });
}

然后就可以进行测试了

但是有一个要注意的点就是

我们的sql是不支持批量update的

要加上allowMultiQueries=true才可以

这样就成功了

总结8

相关推荐
一嘴一个橘子1 分钟前
spring-aop 的 基础使用 - 4 - 环绕通知 @Around
java
小毅&Nora18 分钟前
【Java线程安全实战】⑨ CompletableFuture的高级用法:从基础到高阶,结合虚拟线程
java·线程安全·虚拟线程
冰冰菜的扣jio18 分钟前
Redis缓存中三大问题——穿透、击穿、雪崩
java·redis·缓存
PyHaVolask21 分钟前
SQL注入漏洞原理
数据库·sql
小璐猪头30 分钟前
专为 Spring Boot 设计的 Elasticsearch 日志收集 Starter
java
ptc学习者31 分钟前
黑格尔时代后崩解的辩证法
数据库
代码游侠35 分钟前
应用——智能配电箱监控系统
linux·服务器·数据库·笔记·算法·sqlite
ps酷教程1 小时前
HttpPostRequestDecoder源码浅析
java·http·netty
闲人编程1 小时前
消息通知系统实现:构建高可用、可扩展的企业级通知服务
java·服务器·网络·python·消息队列·异步处理·分发器
!chen1 小时前
EF Core自定义映射PostgreSQL原生函数
数据库·postgresql