接口 1.签到功能接口
参数 说明 请求方式 POST 请求路径 /sign-records 请求参数 无 返回值 { "signDays": 10, // 连续签到天数 "points" : 14 // 今日签到获取的积分 }
SignRecordController.java /** * 新增签到记录 * @return SignResultVO */ @PostMapping @ApiOperation ( "新增签到记录" ) public SignResultVO addSignRecords ( ) { return signRecordService. addSignRecords ( ) ; } ISignRecordService.java SignResultVO addSignRecords ( ) ; RedisConstants.java public interface RedisConstants { /** * 签到记录的key前缀 完整格式为 sign:uid:用户id:年月 */ String SIGN_RECORD_KEY_PREFIX= "sign:uid:" ; } SignRecordServiceImpl.java @Override public SignResultVO addSignRecords ( ) { // 1.签到 Long user= UserContext . getUser ( ) ; String signRecordKeyPrefix= RedisConstants . SIGN_RECORD_KEY_PREFIX; String now= LocalDate . now ( ) . format ( DateUtils . SIGN_DATE_SUFFIX_FORMATTER) ; //key offset String key= signRecordKeyPrefix+ user+ now; int offset= LocalDate . now ( ) . getDayOfMonth ( ) - 1 ; Boolean b= stringRedisTemplate. opsForValue ( ) . setBit ( key, offset, true ) ; // 2计算签到得分 //已经签到过了 if ( Boolean . TRUE. equals ( b) ) { throw new BizIllegalException ( "请勿重复签到" ) ; } // 3.统计连续签到天数(传递键与当天的日期) int continueSignDays= countSignDays ( key, offset+ 1 ) ; // 本月签到天数 int countedMonthDays= countMonthDays ( key, offset+ 1 ) ; int rewardPoints= 0 ; switch ( countedMonthDays) { case 7 : rewardPoints= 5 ; break ; case 14 : rewardPoints= 10 ; break ; case 21 : rewardPoints= 15 ; break ; } log. info ( "用户{}签到成功,连续签到{}天,签到天数{},签到得分{}" , user, continueSignDays, countedMonthDays, rewardPoints) ; SignResultVO signResultVO= new SignResultVO ( ) ; //连续签到天数 signResultVO. setSignDays ( continueSignDays) ; //签到得分 signResultVO. setRewardPoints ( rewardPoints) ; return signResultVO; } /** * 计算签到的天数 * * @param key 缓存中的key * @param dayOfMonth 今天几号 * @return */ private int countMonthDays ( String key, int dayOfMonth) { // 求本月第一天到今天签到的天数 bitFiled 得到的是十进制 // bitField key get u天数 0 List < Long > bitField= stringRedisTemplate. opsForValue ( ) . bitField ( key, BitFieldSubCommands . create ( ) . get ( BitFieldSubCommands. BitFieldType . unsigned ( dayOfMonth) ) . valueAt ( 0 ) ) ; if ( CollUtils . isEmpty ( bitField) ) { return 0 ; } // 本月第一天到今天的签到天数 拿到的十进制 Long num= bitField. get ( 0 ) ; // num转二进制 从后往前推共有多少个一 int counter= 0 ; while ( ( num& 1 ) == 1 ) { counter++ ; num= num>>> 1 ; } return counter; } /** * 统计连续签到天数 * * @param key 缓存中的key * @param dayOfMonth 今天记号 * @return */ private int countSignDays ( String key, int dayOfMonth) { // 1.获取对应的十进制数 List < Long > longs= stringRedisTemplate. opsForValue ( ) . bitField ( key, BitFieldSubCommands . create ( ) . get ( BitFieldSubCommands. BitFieldType . unsigned ( dayOfMonth) ) . valueAt ( 0 ) ) ; if ( CollUtils . isEmpty ( longs) ) { return 0 ; } // 2.循环与运算 int signNum= longs. get ( 0 ) . intValue ( ) ; int continuousDays= 0 ; // 遇到未签到,终止循环,判断最后一位是否为1(是否签到) while ( ( signNum& 1 ) != 0 ) { // 连续签到,计数器+1 continuousDays++ ; // 右移一位,判断前一天 signNum= signNum>> 1 ; } // 5.返回结果 return continuousDays; } 2.查询本月签到记录
参数 说明 请求方式 GET 请求路径 /sign-records 请求参数 无 返回值 [ 0, // 第1天签到情况 1, // 第2天签到情况 1, // 第3天签到情况 // ... ]
SignRecordController.java /** * 查询签到记录 * @return List<> */ @GetMapping @ApiOperation ( "查询签到记录" ) public List < Integer > querySignRecord ( ) { return ISignRecordService . querySignRecord ( ) ; } ISignRecordService.java List < Integer > querySignRecord ( ) ; SignRecordServiceImpl.java @Override public List < Integer > querySignRecord ( ) { // 1.用户id与日期 Long user= UserContext . getUser ( ) ; LocalDate today= LocalDate . now ( ) ; // 2.key与偏移量 String now= today. format ( DateUtils . SIGN_DATE_SUFFIX_FORMATTER) ; String key= RedisConstants . SIGN_RECORD_KEY_PREFIX+ user+ now; int offset= today. getDayOfMonth ( ) - 1 ; // 3.创建集合 ArrayList < Integer > arr= new ArrayList < > ( 40 ) ; for ( int i= 0 ; i<= offset; i++ ) { Boolean isSign= stringRedisTemplate. opsForValue ( ) . getBit ( key, i) ; if ( Boolean . TRUE. equals ( isSign) ) { arr. add ( 1 ) ; } else { arr. add ( 0 ) ; } } return arr; } 3.新增积分记录
SignInMessage.java /** * 签到消息 * * @author ax */ @Data @AllArgsConstructor @NoArgsConstructor public class SignInMessage { private Long userId; private Integer points; } LearningPointsListener.java package com. tianji. learning. mq ; import com. tianji. common. constants. MqConstants ; import com. tianji. learning. enums. PointsRecordType ; import com. tianji. learning. mq. message. SignInMessage ; import com. tianji. learning. service. IPointsRecordService ; import lombok. RequiredArgsConstructor ; import lombok. extern. slf4j. Slf4j ; import org. springframework. amqp. core. ExchangeTypes ; import org. springframework. amqp. rabbit. annotation. Exchange ; import org. springframework. amqp. rabbit. annotation. Queue ; import org. springframework. amqp. rabbit. annotation. QueueBinding ; import org. springframework. amqp. rabbit. annotation. RabbitListener ; import org. springframework. stereotype. Component ; /** * 积分监听器 * * @author ax */ @Slf4j @Component @RequiredArgsConstructor public class LearningPointsListener { private final IPointsRecordService pointsRecordService; /** * 监听-写回答积分 * * @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) ) public void listenWriteReplyMessage ( Long userId) { //用户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) ) public void listenWriteSignMessage ( SignInMessage message) { //用户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) ) public void listenWriteNoteMessage01 ( Long userId) { //用户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) ) public void listenWriteNoteMessage02 ( Long userId) { //用户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) ) public void listenWriteVideoMessage ( Long userId) { //用户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) ) public void listenWriteCommentMessage ( Long userId) { //用户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 void addPointsRecord ( Long userId, int point, PointsRecordType pointsRecordType) ; PointsRecordServiceImpl.java @Override public void addPointsRecord ( Long userId, int point, PointsRecordType type) { //判断该积分类型是否有上限 type.maxPoints是否大于0 if ( point<= 0 ) { return ; } int maxPoints= type. getMaxPoints ( ) ; if ( maxPoints> 0 ) { LocalDateTime now= LocalDateTime . now ( ) ; LocalDateTime dayStartTime= DateUtils . getDayStartTime ( now) ; LocalDateTime dayEndTime= DateUtils . getDayEndTime ( now) ; //如果有上限 查询该用户 该积分类型 今日已得积分 points_record 条件userId type QueryWrapper < PointsRecord > wrapper= new QueryWrapper < > ( ) ; 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) ; //当前用户该积分类型 已得积分 int currentPoints= 0 ; if ( map!= null && map. containsKey ( "totalPoints" ) ) { BigDecimal totalPoints= ( BigDecimal ) map. get ( "totalPoints" ) ; currentPoints= totalPoints. intValue ( ) ; } //判断已得积分是否超过上限 if ( currentPoints>= maxPoints) { //说明已得积分 达到上限 return ; } // 此时的point标识能得得积分 if ( currentPoints+ point> maxPoints) { point= maxPoints- currentPoints; } } //保存积分 PointsRecord record = new PointsRecord ( ) ; 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" ) public List < PointsStatisticsVO > getPointsRecord ( ) { return pointsRecordService. queryMyPointsToday ( ) ; } IPointsRecordService.java List < PointsStatisticsVO > queryMyPointsToday ( ) ; PointsRecordServiceImpl.java @Override public List < PointsStatisticsVO > queryMyPointsToday ( ) { // 1.获取用户id Long userId= UserContext . getUser ( ) ; // 2.获取当天得时间范围 LocalDateTime now= LocalDateTime . now ( ) ; LocalDateTime dayStartTime= DateUtils . getDayStartTime ( now) ; LocalDateTime dayEndTime= DateUtils . getDayEndTime ( now) ; // 3.借助类型去分组判断每个类型今日获取得积分(在语句当中将积分和映射为userId从而便于映射实体) QueryWrapper < PointsRecord > wrapper= new QueryWrapper < > ( ) ; 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) ) { return CollUtils . emptyList ( ) ; } //封装vo返回 List < PointsStatisticsVO > voList= new ArrayList < > ( ) ; for ( PointsRecord record : list) { PointsStatisticsVO vo= new PointsStatisticsVO ( ) ; //积分类型 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) ; } return voList; } 查询赛季列表功能 参数 说明 请求方式 GET 请求路径 /boards/seasons/list 请求参数 无 返回值 [ { "id": "110", // 赛季id "name": "第一赛季", // 赛季名称 "beginTime": "2023-05-01", // 赛季开始时间 "endTime": "2023-05-31", // 赛季结束时间 } ]
PointsBoardSeasonController.java @ApiOperation ( "查询赛季列表" ) @GetMapping ( "list" ) public List < PointsBoardSeason > queryPointBoardSeasonList ( ) { return pointsBoardSeasonService. list ( ) ; //要返回的结果字段与po字段一样 所以可以直接调用mp中的list方法 }