news 2026/4/2 0:55:54

天机学堂-积分功能-day07(五)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
天机学堂-积分功能-day07(五)

接口

1.签到功能接口

参数说明
请求方式POST
请求路径/sign-records
请求参数
返回值{ "signDays": 10, // 连续签到天数 "points" : 14 // 今日签到获取的积分 }
SignRecordController.java
/** * 新增签到记录 * @return SignResultVO */@PostMapping@ApiOperation("新增签到记录")publicSignResultVOaddSignRecords(){returnsignRecordService.addSignRecords();}
ISignRecordService.java
SignResultVOaddSignRecords();
RedisConstants.java
publicinterfaceRedisConstants{/** * 签到记录的key前缀 完整格式为 sign:uid:用户id:年月 */StringSIGN_RECORD_KEY_PREFIX="sign:uid:";}
SignRecordServiceImpl.java
@OverridepublicSignResultVOaddSignRecords(){// 1.签到Longuser=UserContext.getUser();StringsignRecordKeyPrefix=RedisConstants.SIGN_RECORD_KEY_PREFIX;Stringnow=LocalDate.now().format(DateUtils.SIGN_DATE_SUFFIX_FORMATTER);//key offsetStringkey=signRecordKeyPrefix+user+now;intoffset=LocalDate.now().getDayOfMonth()-1;Booleanb=stringRedisTemplate.opsForValue().setBit(key,offset,true);// 2计算签到得分//已经签到过了if(Boolean.TRUE.equals(b)){thrownewBizIllegalException("请勿重复签到");}// 3.统计连续签到天数(传递键与当天的日期)intcontinueSignDays=countSignDays(key,offset+1);// 本月签到天数intcountedMonthDays=countMonthDays(key,offset+1);intrewardPoints=0;switch(countedMonthDays){case7:rewardPoints=5;break;case14:rewardPoints=10;break;case21:rewardPoints=15;break;}log.info("用户{}签到成功,连续签到{}天,签到天数{},签到得分{}",user,continueSignDays,countedMonthDays,rewardPoints);SignResultVOsignResultVO=newSignResultVO();//连续签到天数signResultVO.setSignDays(continueSignDays);//签到得分signResultVO.setRewardPoints(rewardPoints);returnsignResultVO;}/** * 计算签到的天数 * * @param key 缓存中的key * @param dayOfMonth 今天几号 * @return */privateintcountMonthDays(Stringkey,intdayOfMonth){// 求本月第一天到今天签到的天数 bitFiled 得到的是十进制// bitField key get u天数 0List<Long>bitField=stringRedisTemplate.opsForValue().bitField(key,BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));if(CollUtils.isEmpty(bitField)){return0;}// 本月第一天到今天的签到天数 拿到的十进制Longnum=bitField.get(0);// num转二进制 从后往前推共有多少个一intcounter=0;while((num&1)==1){counter++;num=num>>>1;}returncounter;}/** * 统计连续签到天数 * * @param key 缓存中的key * @param dayOfMonth 今天记号 * @return */privateintcountSignDays(Stringkey,intdayOfMonth){// 1.获取对应的十进制数List<Long>longs=stringRedisTemplate.opsForValue().bitField(key,BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));if(CollUtils.isEmpty(longs)){return0;}// 2.循环与运算intsignNum=longs.get(0).intValue();intcontinuousDays=0;// 遇到未签到,终止循环,判断最后一位是否为1(是否签到)while((signNum&1)!=0){// 连续签到,计数器+1continuousDays++;// 右移一位,判断前一天signNum=signNum>>1;}// 5.返回结果returncontinuousDays;}

2.查询本月签到记录

参数说明
请求方式GET
请求路径/sign-records
请求参数
返回值[ 0, // 第1天签到情况 1, // 第2天签到情况 1, // 第3天签到情况 // ... ]
SignRecordController.java
/** * 查询签到记录 * @return List<> */@GetMapping@ApiOperation("查询签到记录")publicList<Integer>querySignRecord(){returnISignRecordService.querySignRecord();}
ISignRecordService.java
List<Integer>querySignRecord();
SignRecordServiceImpl.java
@OverridepublicList<Integer>querySignRecord(){// 1.用户id与日期Longuser=UserContext.getUser();LocalDatetoday=LocalDate.now();// 2.key与偏移量Stringnow=today.format(DateUtils.SIGN_DATE_SUFFIX_FORMATTER);Stringkey=RedisConstants.SIGN_RECORD_KEY_PREFIX+user+now;intoffset=today.getDayOfMonth()-1;// 3.创建集合ArrayList<Integer>arr=newArrayList<>(40);for(inti=0;i<=offset;i++){BooleanisSign=stringRedisTemplate.opsForValue().getBit(key,i);if(Boolean.TRUE.equals(isSign)){arr.add(1);}else{arr.add(0);}}returnarr;}

3.新增积分记录

SignInMessage.java
/** * 签到消息 * * @author ax */@Data@AllArgsConstructor@NoArgsConstructorpublicclassSignInMessage{privateLonguserId;privateIntegerpoints;}
LearningPointsListener.java
packagecom.tianji.learning.mq;importcom.tianji.common.constants.MqConstants;importcom.tianji.learning.enums.PointsRecordType;importcom.tianji.learning.mq.message.SignInMessage;importcom.tianji.learning.service.IPointsRecordService;importlombok.RequiredArgsConstructor;importlombok.extern.slf4j.Slf4j;importorg.springframework.amqp.core.ExchangeTypes;importorg.springframework.amqp.rabbit.annotation.Exchange;importorg.springframework.amqp.rabbit.annotation.Queue;importorg.springframework.amqp.rabbit.annotation.QueueBinding;importorg.springframework.amqp.rabbit.annotation.RabbitListener;importorg.springframework.stereotype.Component;/** * 积分监听器 * * @author ax */@Slf4j@Component@RequiredArgsConstructorpublicclassLearningPointsListener{privatefinalIPointsRecordServicepointsRecordService;/** * 监听-写回答积分 * * @param userId */@RabbitListener(bindings=@QueueBinding(value=@Queue(name="qa.points.queue",durable="true"),exchange=@Exchange(name=MqConstants.Exchange.LEARNING_EXCHANGE,type=ExchangeTypes.TOPIC),key=MqConstants.Key.WRITE_REPLY))publicvoidlistenWriteReplyMessage(LonguserId){//用户id,累加的积分,积分类型pointsRecordService.addPointsRecord(userId,5,PointsRecordType.QA);}/** * 监听-签到积分 * * @param message */@RabbitListener(bindings=@QueueBinding(value=@Queue(name="sign.points.queue",durable="true"),exchange=@Exchange(name=MqConstants.Exchange.LEARNING_EXCHANGE,type=ExchangeTypes.TOPIC),key=MqConstants.Key.SIGN_IN))publicvoidlistenWriteSignMessage(SignInMessagemessage){//用户id,累加的积分,积分类型pointsRecordService.addPointsRecord(message.getUserId(),message.getPoints(),PointsRecordType.SIGN);}/** * 监听-被采纳笔记积分 * * @param userId */@RabbitListener(bindings=@QueueBinding(value=@Queue(name="notegathered.points.queue",durable="true"),exchange=@Exchange(name=MqConstants.Exchange.LEARNING_EXCHANGE,type=ExchangeTypes.TOPIC),key=MqConstants.Key.NOTE_GATHERED))publicvoidlistenWriteNoteMessage01(LonguserId){//用户id,累加的积分,积分类型pointsRecordService.addPointsRecord(userId,2,PointsRecordType.NOTE);}/** * 监听-写笔记积分 * * @param userId */@RabbitListener(bindings=@QueueBinding(value=@Queue(name="note.points.queue",durable="true"),exchange=@Exchange(name=MqConstants.Exchange.LEARNING_EXCHANGE,type=ExchangeTypes.TOPIC),key=MqConstants.Key.WRITE_NOTE))publicvoidlistenWriteNoteMessage02(LonguserId){//用户id,累加的积分,积分类型pointsRecordService.addPointsRecord(userId,3,PointsRecordType.NOTE);}/** * 监听-学习视频积分 * * @param userId */@RabbitListener(bindings=@QueueBinding(value=@Queue(name="video.points.queue",durable="true"),exchange=@Exchange(name=MqConstants.Exchange.LEARNING_EXCHANGE,type=ExchangeTypes.TOPIC),key=MqConstants.Key.LEARN_SECTION))publicvoidlistenWriteVideoMessage(LonguserId){//用户id,累加的积分,积分类型pointsRecordService.addPointsRecord(userId,10,PointsRecordType.LEARNING);}/** * 监听-评论积分 * * @param userId */@RabbitListener(bindings=@QueueBinding(value=@Queue(name="comment.points.queue",durable="true"),exchange=@Exchange(name=MqConstants.Exchange.LEARNING_EXCHANGE,type=ExchangeTypes.TOPIC),key=MqConstants.Key.COMMENT))publicvoidlistenWriteCommentMessage(LonguserId){//用户id,累加的积分,积分类型pointsRecordService.addPointsRecord(userId,10,PointsRecordType.COMMENT);}// /* 写回答 */// String WRITE_REPLY = "reply.new";// /* 签到 */// String SIGN_IN = "sign.in";// /* 学习视频 */// String LEARN_SECTION = "section.learned";// /* 写笔记 */// String WRITE_NOTE = "note.new";// /* 笔记被采集 */// String NOTE_GATHERED = "note.gathered";// /* 评论 */// String COMMENT = "comment.new";// LEARNING(1, "课程学习", 50),// SIGN(2, "每日签到", 0),// QA(3, "课程问答", 20),// NOTE(4, "课程笔记", 20),// COMMENT(5, "课程评价", 0),}
IPointsRecordService.java
voidaddPointsRecord(LonguserId,intpoint,PointsRecordTypepointsRecordType);
PointsRecordServiceImpl.java
@OverridepublicvoidaddPointsRecord(LonguserId,intpoint,PointsRecordTypetype){//判断该积分类型是否有上限 type.maxPoints是否大于0if(point<=0){return;}intmaxPoints=type.getMaxPoints();if(maxPoints>0){LocalDateTimenow=LocalDateTime.now();LocalDateTimedayStartTime=DateUtils.getDayStartTime(now);LocalDateTimedayEndTime=DateUtils.getDayEndTime(now);//如果有上限 查询该用户 该积分类型 今日已得积分 points_record 条件userId typeQueryWrapper<PointsRecord>wrapper=newQueryWrapper<>();wrapper.select("sum(points) as totalPoints");wrapper.eq("user_id",userId);wrapper.eq("type",type);wrapper.between("create_time",dayStartTime,dayEndTime);Map<String,Object>map=this.getMap(wrapper);//当前用户该积分类型 已得积分intcurrentPoints=0;if(map!=null&&map.containsKey("totalPoints")){BigDecimaltotalPoints=(BigDecimal)map.get("totalPoints");currentPoints=totalPoints.intValue();}//判断已得积分是否超过上限if(currentPoints>=maxPoints){//说明已得积分 达到上限return;}// 此时的point标识能得得积分if(currentPoints+point>maxPoints){point=maxPoints-currentPoints;}}//保存积分PointsRecordrecord=newPointsRecord();record.setUserId(userId);record.setType(type);record.setPoints(point);this.save(record);}

4.查询今日积分情况

参数说明
请求方式GET
请求路径/points/today
请求参数
返回值[ { "type": "每日签到", // 积分类型描述 "points" : 1, // 今日签到获取的积分 "maxPoints" : 5, // 每日积分上限 }, { "type": "回答问题", // 积分类型描述 "points" : 1, // 今日签到获取的积分 "maxPoints" : 5, // 每日积分上限 } ]
PointsRecordController.java
@ApiOperation("查询今日积分")@GetMapping("today")publicList<PointsStatisticsVO>getPointsRecord(){returnpointsRecordService.queryMyPointsToday();}
IPointsRecordService.java
List<PointsStatisticsVO>queryMyPointsToday();
PointsRecordServiceImpl.java
@OverridepublicList<PointsStatisticsVO>queryMyPointsToday(){// 1.获取用户idLonguserId=UserContext.getUser();// 2.获取当天得时间范围LocalDateTimenow=LocalDateTime.now();LocalDateTimedayStartTime=DateUtils.getDayStartTime(now);LocalDateTimedayEndTime=DateUtils.getDayEndTime(now);// 3.借助类型去分组判断每个类型今日获取得积分(在语句当中将积分和映射为userId从而便于映射实体)QueryWrapper<PointsRecord>wrapper=newQueryWrapper<>();wrapper.select("type","sum(points) as userId");wrapper.eq("user_id",userId);wrapper.between("create_time",dayStartTime,dayEndTime);wrapper.groupBy("type");List<PointsRecord>list=this.list(wrapper);if(CollUtils.isEmpty(list)){returnCollUtils.emptyList();}//封装vo返回List<PointsStatisticsVO>voList=newArrayList<>();for(PointsRecordrecord:list){PointsStatisticsVOvo=newPointsStatisticsVO();//积分类型vo.setType(record.getType().getDesc());//积分类型得分数上限vo.setMaxPoints(record.getType().getMaxPoints());// if(record.getType().getMaxPoints()==0){// vo.setMaxPoints(41);// }//今日已获得积分vo.setPoints(record.getUserId().intValue());voList.add(vo);}returnvoList;}

查询赛季列表功能

参数说明
请求方式GET
请求路径/boards/seasons/list
请求参数
返回值[ { "id": "110", // 赛季id "name": "第一赛季", // 赛季名称 "beginTime": "2023-05-01", // 赛季开始时间 "endTime": "2023-05-31", // 赛季结束时间 } ]
PointsBoardSeasonController.java
@ApiOperation("查询赛季列表")@GetMapping("list")publicList<PointsBoardSeason>queryPointBoardSeasonList(){returnpointsBoardSeasonService.list();//要返回的结果字段与po字段一样 所以可以直接调用mp中的list方法}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/25 7:28:54

无人机相关法律法规全体系梳理

无人机相关法律法规全体系梳理随着无人机产业的高速发展&#xff0c;我国已构建起以“国家行政法规为核心、部门规章为支撑、地方细则为补充”的无人机法律体系&#xff0c;覆盖无人机生产、登记、飞行、监管全链条。本梳理结合2024-2025年最新法规修订内容&#xff0c;聚焦不同…

作者头像 李华
网站建设 2026/3/29 0:49:39

(7-3-03)基于MCP实现的金融投资Agent(3)金融MCP服务器

7.3.5 金融MCP服务器文件server.py是本项目的核心服务器实现&#xff0c;基于模型上下文协议&#xff08;MCP&#xff09;搭建了一个金融数据服务平台。它整合了多个工具函数&#xff0c;提供了全面的金融数据获取与分析能力&#xff0c;包括市场动态&#xff08;涨幅榜、跌幅…

作者头像 李华
网站建设 2026/3/25 17:28:56

车联网时序数据库专业的服务商有哪些

在车联网时序数据库领域&#xff0c;有不少专业的服务商&#xff0c;各有其特点和优势&#xff0c;以下为你介绍一些&#xff1a;TDengineTDengine是涛思数据自主研发的一款专为物联网、车联网、工业互联网、IT 运维等设计的时序数据库。它具有诸多独特优势&#xff0c;测试显示…

作者头像 李华
网站建设 2026/3/29 3:33:03

C++继承机制详解

C中的继承是面向对象编程的核心概念之一&#xff0c;它允许新的类&#xff08;派生类&#xff09;获取已有类&#xff08;基类&#xff09;的属性和行为&#xff0c;从而实现代码的复用和扩展。本文将结合生动的比喻和详细的代码示例&#xff0c;全面讲解C继承的机制&#xff0…

作者头像 李华
网站建设 2026/4/1 23:52:14

NatTypeTester终极指南:快速检测网络NAT类型的完整解决方案

NatTypeTester终极指南&#xff1a;快速检测网络NAT类型的完整解决方案 【免费下载链接】NatTypeTester 测试当前网络的 NAT 类型&#xff08;STUN&#xff09; 项目地址: https://gitcode.com/gh_mirrors/na/NatTypeTester 想要彻底解决网络连接问题&#xff1f;NatTyp…

作者头像 李华
网站建设 2026/3/30 9:43:20

专业的车联网时序数据库排名是怎样的

在车联网领域&#xff0c;目前并没有官方统一的专业车联网时序数据库排名。因为不同的数据库在功能、性能、适用场景等方面各有优劣&#xff0c;排名会受到多种因素的影响&#xff0c;以下为你简要分析一些车联网中常用的时序数据库特点&#xff1a;InfluxDB这是一款广为人知的…

作者头像 李华