场景设计学习-积分系统

场景设计-积分系统

1.概念和规则

  • 积分:用户在网站的各种交互行为都可以产生积分,积分值与行为类型有关
  • 天梯榜:按照每个用户的总积分排序得到的排行榜,称为天梯榜。排名靠前的有奖励。天梯榜每个自然月为一个赛季,月初清零

具体的积分获取的细则通常如下:

积分获取规则
1. 签到规则
连续7天奖励10分  连续14天 奖励20  连续28天奖励40分, 每月签到进度当月第一天重置

2. 学习规则
每学习一小节,积分+10,每天获得上限50分

3. 交互规则(有效交互数据参与积分规则,无效数据会被删除)
- 写评价 每个课程只能评价一次,每日无上限
- 写问答 积分+5 每日获得上限为20分
- 写笔记 积分+3 每次被采集+2 每日获得上限为20分

2.页面原型

在这里插入图片描述

在这里插入图片描述

3.需要的接口统计

业务编号接口简述
签到1签到
2查询本月签到记录
积分3新增积分记录
4查询今日积分情况
排行榜5查询本赛季的积分排行榜
6查询赛季列表
7查询历史赛季积分排行榜

4.表结构设计

4.1签到

签到表主要记载,谁在什么时候,通过什么渠道进行了签到,还可以加上补签功能

 CREATE TABLE `sign_record` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
  `user_id` bigint NOT NULL COMMENT '用户id',
  `year` year NOT NULL COMMENT '签到年份',
  `month` tinyint NOT NULL COMMENT '签到月份',
  `date` date NOT NULL COMMENT '签到日期',
  `is_backup` bit(1) NOT NULL COMMENT '是否补签',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='签到记录表';

4.2积分记录

积分记录,记录谁(用户),在什么时间,通过哪种方式,获得了多少积分

CREATE TABLE IF NOT EXISTS `points_record` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '积分记录表id',
  `user_id` bigint NOT NULL COMMENT '用户id',
  `type` tinyint NOT NULL COMMENT '积分方式:1-课程学习,2-每日签到,3-课程问答, 4-课程笔记,5-课程评价',
  `points` tinyint NOT NULL COMMENT '积分值',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`) USING BTREE,
  KEY `idx_user_id` (`user_id`,`type`) USING BTREE,
  KEY `idx_create_time` (`create_time`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=41 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='学习积分记录,每个月底清零';

4.3排行榜

主体需要两个,一个是赛季表记录赛季开始时间和结束时间.另一个是排行榜,记录用户在某个赛季的排行

CREATE TABLE IF NOT EXISTS `points_board_season` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '自增长id,season标示',
  `name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '赛季名称,例如:第1赛季',
  `begin_time` date NOT NULL COMMENT '赛季开始时间',
  `end_time` date NOT NULL COMMENT '赛季结束时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC;

CREATE TABLE IF NOT EXISTS `points_board` (
  `id` bigint NOT NULL COMMENT '榜单id',
  `user_id` bigint NOT NULL COMMENT '学生id',
  `points` int NOT NULL COMMENT '积分值',
  `rank` tinyint NOT NULL COMMENT '名次,只记录赛季前100',
  `season` smallint NOT NULL COMMENT '赛季,例如 1,就是第一赛季,2-就是第二赛季',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `idx_season_user` (`season`,`user_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='学霸天梯榜';

5.实现思路

5.1签到功能

使用bitmap,每个月为每个用户生成一个独立的KEY,因此KEY中必须包含用户信息、月份信息,长这样sign:uid:xxx:20XX01

签到

把这一天的下位列表设置成true,关键代码如下:

LocalDate now = LocalDate.now(); 		
int offset = now.getDayOfMonth() - 1;
Boolean exists = redisTemplate.opsForValue().setBit(key, offset, true);

计算连续签到

  private int countSignDays(String key, int len) {
        // 1.获取本月从第一天开始,到今天为止的所有签到记录
        List<Long> result = redisTemplate.opsForValue()
                .bitField(key, BitFieldSubCommands.create().get(
                        BitFieldSubCommands.BitFieldType.unsigned(len)).valueAt(0));
        if (CollUtils.isEmpty(result)) {
            return 0;
        }
        int num = result.get(0).intValue();
        // 2.定义一个计数器
        int count = 0;
        // 3.循环,与1做与运算,得到最后一个bit,判断是否为0,为0则终止,为1则继续
        while ((num & 1) == 1) {
            // 4.计数器+1
            count++;
            // 5.把数字右移一位,最后一位被舍弃,倒数第二位成了最后一位
            num >>>= 1;
        }
        return count;
    }

具体解释:

  1. .bitField

    • Redis 的 BITFIELD 命令允许对 Bitmap 中的位域进行灵活的批量操作。
    • 它可以高效地读取、修改和操作指定范围的位数据。
  2. BitFieldSubCommands.create().get(...).valueAt(0)

    • create():创建 BitFieldSubCommands 对象,用于构造 Redis BITFIELD 命令。

    • get

      获取 Bitmap 中的位域值。

      • BitFieldType.unsigned(len):读取一个无符号整数,长度为 len 位。
    • valueAt(0):从偏移量 0 开始读取。

目的: 将从第 1 位到第 len 位的所有位数据读取出来,作为一个整体返回。例如,如果一个月有 31 天的签到数据,这里会读取连续 31 位并将其转为一个 Long 值。

根据连续签到天数计算奖励积分值:

可以定义一个枚举,避免用swtich case

public enum SignReward {
    SEVEN_DAYS(7, 10),
    FOURTEEN_DAYS(14, 20),
    TWENTY_EIGHT_DAYS(28, 40);

    private final int requiredDays;
    private final int points;

    SignReward(int requiredDays, int points) {
        this.requiredDays = requiredDays;
        this.points = points;
    }

    public static int calculateReward(int signDays) {
        // 按天数降序排列,优先匹配高奖励
        for (SignReward reward : values()) {
            if (signDays==reward.requiredDays) {
                return reward.points;
            }
        }
        return 0;
    }
}
查询签到记录

核心代码

   Byte[] arr = new Byte[dayOfMonth];
   int pos = dayOfMonth - 1;
   while (pos >= 0){
        arr[pos--] = (byte)(num & 1);
        // 把数字右移一位,抛弃最后一个bit位,继续下一个bit位
        num >>>= 1;
    }
    return arr;

使用Redis保存签到记录,那如果Redis宕机怎么办?

我们可以给Redis添加数据持久化机制,比如使用AOF持久化。这样宕机后也丢失的数据量不多,可以接受。

或者呢,我们可以搭建Redis主从集群,再结合Redis哨兵。主节点会把数据持续的同步给从节点,宕机后也会有哨兵重新选主,基本不用担心数据丢失问题。

当然,如果对于数据的安全性要求非常高。肯定还是要用传统数据库来实现的。但是为了解决签到数据量较大的问题,我们可能就需要对数据做分表处理了。或者及时将历史数据存档。

总的来说,签到数据使用Redis的BitMap无论是安全性还是数据内存占用情况,都是可以接受的。但是具体是选择Redis还是数据库方案,最终还是要看具体的要求来选择。

我个人觉得,使用bitmap优势是可以快速的统计,降低数据库的压力,查询操作可以由bitmap完成,而持久化操作可以异步入库

5.2积分功能

积分功能通常是其他业务进行操作,来通知积分系统进行添加积分操作,使用MQ来实现异步解耦。

如签到功能,在计算完积分之后,通知新增积分入库操作

但是入库并不是无脑进行入库,根据不同的业务类型,我们首先要查询当日积分上限,满足条件才能进行积分增加

这里有发现一个之前没用过的小技巧:

MyBatis-Plus 的确支持将 QueryWrapperLambdaQueryWrapper 作为参数传递到自定义的 SQL 方法中,这是一个非常实用的小技巧,可以大大简化动态查询条件的处理逻辑。

@Select("SELECT SUM(points) FROM points_record ${ew.customSqlSegment}")
Integer queryUserPointsByTypeAndDate(@Param(Constants.WRAPPER) QueryWrapper<PointsRecord> wrapper);

${ew.customSqlSegment}

  • 这是 MyBatis-Plus 提供的占位符,用于插入 QueryWrapper 动态生成的 SQL 条件。
  • 它会将 QueryWrapper 中构建的条件自动拼接到 SQL 中。

@Param(Constants.WRAPPER)

  • MyBatis-Plus 约定,QueryWrapper 的参数必须使用 @Param(Constants.WRAPPER) 标记,Constants.WRAPPER 是 MyBatis-Plus 提供的默认常量,值为 "ew"

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/960675.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ML基础3-sklearn中的1个简单的分类器例子

Scikit-learn&#xff08;通常缩写为sklearn&#xff09;是一个流行的Python机器学习库&#xff0c;用于数据挖掘和数据分析任务。它建立在NumPy、SciPy和matplotlib等科学计算/可视化库的基础上&#xff0c;提供了丰富的工具和算法&#xff0c;用于处理各种机器学习问题&#…

The Simulation技术浅析(二):模型技术

一、物理模型(Physical Models) 1. 概述 物理模型基于物理定律和原理,通过模拟现实世界中物理系统的行为和相互作用来构建模型。物理模型通常用于工程、物理和化学等领域,用于预测系统在不同条件下的表现。 2. 关键技术 力学定律:例如牛顿运动定律,用于模拟物体的运动…

006 mybatis关联查询(一对一、一对多)

文章目录 一对一查询SQL语句方法一&#xff1a;resultType方法二&#xff1a;resultMap创建扩展po类Mapper映射文件Mapper接口测试代码小结 一对多查询SQL语句修改po类Mapper映射文件Mapper接口测试代码 注意&#xff1a;因为一个订单信息只会是一个人下的订单&#xff0c;所以…

linux asio网络编程理论及实现

最近在B站看了恋恋风辰大佬的asio网络编程&#xff0c;质量非常高。在本章中将对ASIO异步网络编程的整体及一些实现细节进行完整的梳理&#xff0c;用于复习与分享。大佬的博客&#xff1a;恋恋风辰官方博客 Preactor/Reactor模式 在网络编程中&#xff0c;通常根据事件处理的触…

渗透测试之WAF规则触发绕过规则之规则库绕过方式

目录 Waf触发规则的绕过 特殊字符替换空格 实例 特殊字符拼接绕过waf Mysql 内置得方法 注释包含关键字 实例 Waf触发规则的绕过 特殊字符替换空格 用一些特殊字符代替空格&#xff0c;比如在mysql中%0a是换行&#xff0c;可以代替空格 这个方法也可以部分绕过最新版本的…

环境搭建--vscode

vscode官网下载合适版本 安装vscode插件 安装 MinGW 配置环境变量 把安装目录D&#xff1a;\mingw64 配置在用户的环境变量path里即可 选择用户环境变量path 点确定保存后开启cmd输入g&#xff0c;如提示no input files 则说明Mingw64 安装成功&#xff0c;如果提示g 不是内…

橙河网络:市场调研都会用到哪些工具?

一般市场调研会用到多种工具&#xff0c;以获取全面、准确的市场信息。以下是一些常用的市场调研工具&#xff1a; 一、在线调查平台 问卷星&#xff1a;提供在线问卷编制、分发和数据分析功能&#xff0c;适用于大规模的市场调研。 SurveyMonkey&#xff1a;可用于市场调查…

996引擎 - NPC-添加NPC引擎自带形象

996引擎 - NPC-添加NPC引擎自带形象 截图参考添加NPC参考资料截图参考 添加NPC 编辑NPC表:Envir\DATA\cfg_npclist.xls 1.1. 需要临时隐藏NPC时可以在id前加 // 1.2. 如果NPC朝向不对,可以调整dir 列。(按8方向,上是0顺时针数。我这里给的4) 1.3. 形象代码:NPC代码、怪物…

登录Ubuntu论坛报错:Your username cannot be shorter than 1000 characters.

网址 Ubuntu Forums 报错 The following errors occurred during your registration:Your username cannot be shorter than 1000 characters. 截图

【Linux基础指令】第三期

近期更新的基础指令链接&#xff1a; 【Linux基础指令】第一期-CSDN博客 【Linux基础指令】第二期-CSDN博客 本期博客的主题依旧是 "基础指令" &#xff1b;话不多说&#xff0c;正文开始。 一、Linux的指令 1.zip / unzip 功能&#xff1a;打包压缩 命令格式&…

深入浅出 AB 压测工具:从入门到精通

AB&#xff08;ApacheBench&#xff09;是一款轻量级、易用且功能强大的 HTTP 服务器性能测试工具。它能够模拟多个并发用户对服务器发起请求&#xff0c;并统计服务器的响应时间、吞吐量等性能指标&#xff0c;帮助开发者评估服务器性能、发现性能瓶颈。本文将深入探讨 AB 压测…

如何将电脑桌面默认的C盘设置到D盘?详细操作步骤!

将电脑桌面默认的C盘设置到D盘的详细操作步骤&#xff01; 本博文介绍如何将电脑桌面&#xff08;默认为C盘&#xff09;设置在D盘下。 首先&#xff0c;在D盘建立文件夹Desktop&#xff0c;完整的路径为D:\Desktop。winR&#xff0c;输入Regedit命令。&#xff08;或者单击【…

网络工程师 (3)指令系统基础

一、寻址方式 &#xff08;一&#xff09;指令寻址 顺序寻址&#xff1a;通过程序计数器&#xff08;PC&#xff09;加1&#xff0c;自动形成下一条指令的地址。这是计算机中最基本、最常用的寻址方式。 跳跃寻址&#xff1a;通过转移类指令直接或间接给出下一条指令的地址。跳…

Games104——高级动画技术:动画树、IK和表情动画

这里写目录标题 动画混合数学混合&#xff1a;线性插值混合空间Blend Space1D Blend Space2D Blend SpaceSkeleton Masked Blending骨骼遮罩混合Addictive Blending 动画状态机Animation State Machine&#xff08;ASM&#xff09;动画状态机定义节点&#xff1a;分为clip和ble…

【Rust自学】15.6. RefCell与内部可变性:“摆脱”安全性限制

题外话&#xff0c;这篇文章一共4050字&#xff0c;是截止到目前为止最长的文章&#xff0c;如果你能坚持读完并理解&#xff0c;那真的很强&#xff01; 喜欢的话别忘了点赞、收藏加关注哦&#xff08;加关注即可阅读全文&#xff09;&#xff0c;对接下来的教程有兴趣的可以…

lightweight-charts-python 包 更新 lightweight-charts.js 的方法

lightweight-charts-python 是 lightweight-charts.js 的 python 包装&#xff0c;非常好用 lightweight-charts 更新比较频繁&#xff0c;导致 lightweight-charts-python 内置的 lightweight-charts 经常不是最新的。 新的 lightweight-charts 通常可以获得性能改进和bug修复…

2025多目标优化创新路径汇总

多目标优化是当下非常热门且有前景的方向&#xff01;作为AI领域的核心技术之一&#xff0c;其专注于解决多个相互冲突的目标的协同优化问题&#xff0c;核心理念是寻找一组“不完美但均衡”的“帕累托最优解”。在实际中&#xff0c;几乎处处都有它的身影。 但随着需求场景的…

Python从0到100(八十五):神经网络-使用迁移学习完成猫狗分类

前言: 零基础学Python:Python从0到100最新最全教程。 想做这件事情很久了,这次我更新了自己所写过的所有博客,汇集成了Python从0到100,共一百节课,帮助大家一个月时间里从零基础到学习Python基础语法、Python爬虫、Web开发、 计算机视觉、机器学习、神经网络以及人工智能…

第4章 神经网络【1】——损失函数

4.1.从数据中学习 实际的神经网络中&#xff0c;参数的数量成千上万&#xff0c;因此&#xff0c;需要由数据自动决定权重参数的值。 4.1.1.数据驱动 数据是机器学习的核心。 我们的目标是要提取出特征量&#xff0c;特征量指的是从输入数据/图像中提取出的本质的数 …

深度学习|表示学习|卷积神经网络|详细推导每一层的维度变化|14

如是我闻&#xff1a; 一个经典的卷积神经网络&#xff08;CNN&#xff09;架构&#xff0c;呈现的是输入图像通过多个卷积层、池化层以及全连接层&#xff0c;最终输出分类结果的过程。整个过程的核心是理解输入特征图的尺寸如何在每一层发生变化&#xff0c;我们可以通过卷积…