当前位置: 首页 > news >正文

签到功能---实现签到接口

文章目录

    • 概要
    • 整体架构流程
    • 技术细节
    • 小结

概要

需求分析以及接口设计

由KEY的结构可知,要签到,就必须知道是哪一天签到,也就是两个信息:

  • 当前用户

  • 当前时间

这两个信息我们都可以自己获取,因此签到时,前端无需传递任何参数。

那么签到以后是否需要返回数据呢?

需求中说连续签到会有积分奖励,那么为了提升用户体验,在用户签到成功以后是不是应该返回连续签到天数和获取的积分奖励呢。

综上,最终签到的接口信息如下:

另外,为了便于统计,我们计划每个月为每个用户生成一个独立的KEY,因此KEY中必须包含用户信息、月份信息,长这样:

 

sign:uid:xxx:202401

 

技术细节

package com.tianji.learning.domain.vo;import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;@Data
@ApiModel(description = "签到结果")
public class SignResultVO {@ApiModelProperty("连续签到天数")private Integer signDays;@ApiModelProperty("签到得分")private Integer signPoints = 1;@ApiModelProperty("连续签到奖励积分,连续签到超过7天以上才有奖励")private Integer rewardPoints;@JsonIgnorepublic int totalPoints(){return signPoints + rewardPoints;}
}

1.Controller层

package com.tianji.learning.controller;import com.tianji.learning.domain.vo.SignResultVO;
import com.tianji.learning.service.ISignRecordService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@Api(tags = "签到相关接口")
@RestController
@RequestMapping("sign-records")
@RequiredArgsConstructor
public class SignRecordController {private final ISignRecordService recordService;@PostMapping@ApiOperation("签到功能接口")public SignResultVO addSignRecords(){return recordService.addSignRecords();}
}

2.Service层:

package com.tianji.learning.service.impl;import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.BooleanUtils;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constants.RedisConstants;
import com.tianji.learning.domain.vo.SignResultVO;
import com.tianji.learning.mq.message.SignInMessage;
import com.tianji.learning.service.ISignRecordService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;import java.time.LocalDate;
import java.util.List;@Service
@RequiredArgsConstructor
public class SignRecordServiceImpl implements ISignRecordService {private final StringRedisTemplate redisTemplate;@Overridepublic SignResultVO addSignRecords() {// 1.签到// 1.1.获取登录用户Long userId = UserContext.getUser();// 1.2.获取日期LocalDate now = LocalDate.now();// 1.3.拼接keyString key = RedisConstants.SIGN_RECORD_KEY_PREFIX+ userId+ now.format(DateUtils.SIGN_DATE_SUFFIX_FORMATTER);// 1.4.计算offsetint offset = now.getDayOfMonth() - 1;// 1.5.保存签到信息Boolean exists = redisTemplate.opsForValue().setBit(key, offset, true);if (BooleanUtils.isTrue(exists)) {throw new BizIllegalException("不允许重复签到!");}// 2.计算连续签到天数int signDays = countSignDays(key, now.getDayOfMonth());// 3.计算签到得分int rewardPoints = 0;switch (signDays) {case 7:rewardPoints = 10;break;case 14:rewardPoints = 20;break;case 28:rewardPoints = 40;break;}// TODO 4.保存积分明细记录 // 5.封装返回SignResultVO vo = new SignResultVO();vo.setSignDays(signDays);vo.setRewardPoints(rewardPoints);return vo;}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.计数器+1count++;// 5.把数字右移一位,最后一位被舍弃,倒数第二位成了最后一位num >>>= 1;}return count;}
}

http://www.dtcms.com/a/139263.html

相关文章:

  • Unity基于屏幕空间的鼠标拖动,拖动物体旋转
  • 强化学习算法系列(五):最主流的算法框架——Actor-Critic算法框架
  • 论文阅读VACE: All-in-One Video Creation and Editing
  • 用Python Pandas高效操作数据库:从查询到写入的完整指南
  • 音视频相关协议和技术内容
  • 智能体开发的范式革命:Cangjie Magic全景解读与实践思考
  • 游戏盾和高防ip有什么区别
  • CSS进度条带斑马纹动画(有效果图)
  • 云转型(cloud transformation)——不仅仅是简单的基础设施迁移
  • Java字符串处理
  • IntelliJ IDEA 2025.1 发布 ,默认 K2 模式 | Android Studio 也将跟进
  • XC7K410T‑2FFG900I 赛灵思XilinxFPGA Kintex‑7
  • BUUCTF-Web(21-40)
  • 计算机视觉——JPEG AI 标准发布了图像压缩新突破与数字图像取证的挑战及应对策略
  • HTTP 3.0 协议的特点
  • Oracle 19c部署之初始化实例(三)
  • AI编写的“黑科技风格、自动刷新”的看板页面
  • Echarts柱状图斜线环纹(图形的贴花图案)
  • 30Metrics Server的使用
  • 在VirtualBox上安装Ubuntu
  • Electron 中引入MessageChannel 大大缩短不同渲染进程和 Webview 各组件 1o1的通信链路
  • 山东大学软件学院创新项目实训开发日志(18)之对话自动生成标题设为用户第一次对话发的文字
  • C++11:模板元编程(TMP)基础
  • 深入理解C++数组:从基础到实践
  • 【已更新】2025华中杯C题数学建模网络挑战赛思路代码文章教学数学建模思路:就业状态分析与预测
  • Breeze 55A FOC 电调:无人机动力控制的高效核心方案
  • 蓝桥杯题目:二维前缀和
  • 【PyQt5】QLineEdit文本对话框点击时关联槽函数,槽函数打开文件选择对话框;并解决选择文件后闪退的问题
  • 【杂谈】-自动驾驶变革:货运革新与机器人出租车崛起
  • 非洲电商争夺战:中国闪电战遭遇本土游击队的降维打击