实现一个类似于这样的签到功能 (参考的百度网盘签到页面)
数据表是一个用户只有一条签到记录的设计, 没有使用签到一次表中加一条数据, 那样的话数据太多了
表结构:
CREATE TABLE `sign_in` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`user_id` bigint(20) NOT NULL COMMENT '签到用户id',
`continue_days` int(3) NOT NULL DEFAULT '1' COMMENT '连续签到天数',
`update_time` datetime DEFAULT NULL COMMENT '更新日期, 最后签到日期',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='用户签到表';
实体对象:
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
* 用户签到表
*
* @author lixx
* @version 1.0
* @date 2020-06-10 10:36
*/
@Data
@NoArgsConstructor
public class SignIn {
/**
* 主键
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 签到用户id
*/
private Long userId;
/**
* 连续签到天数
*/
private Integer continueDays;
/**
* 更新日期, 最后签到日期
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
@TableField(fill = FieldFill.UPDATE)
private LocalDateTime updateTime;
public SignIn(Long userId, LocalDateTime updateTime) {
this.userId = userId;
this.updateTime = updateTime;
}
}
签到接口:
/**
* 用户签到
*
* @param userId 用户id
* @return
*/
public JsonResult signIn(Long userId) {
// 查询用户是否签过到
SignIn signIn = signInMapper.selectById(userId);
/*没有签过到, 直接新增*/
if (null == signIn) {
signInMapper.insert(new SignIn(userId, LocalDateTime.now()));
} else {/*签过到*/
// 判断最后签到日期与当前日期是否超过一天
LocalDate signInTime = signIn.getUpdateTime().toLocalDate();
LocalDate currTime = LocalDate.now();
long daysDiff = ChronoUnit.DAYS.between(signInTime, currTime);
if (daysDiff <= 0) {
return JsonResult.buildSuccess("重复签到");
}
if (daysDiff > 1) {
// 1, 超过一天, 把连续签到的天数重置为 1
signIn.setContinueDays(1);
} else {
// 2, 没有超过一天, 把连续签到的天数+1
signIn.setContinueDays(signIn.getContinueDays() + 1);
}
signIn.setUpdateTime(LocalDateTime.now());
signInMapper.updateById(signIn);
}
return JsonResult.buildSuccess();
}
这样签到功能就做好了, 数据库数据如下:
签到列表接口:
需要增加一个接口返回的dto类型(大家用map也行)
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* @author lixx
* @version 1.0
* @date 2020-06-10 15:09
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SignInDto implements Serializable {
/**
* 表示签到的天数
*/
private Integer day;
/**
* 1表示已经签到, 0表示未签到
*/
private Integer flag;
// 如果还有其他业务字段就加在后面, 如积分, 如金币
}
签到列表接口逻辑代码:
/**
* 用户签到列表
*
* @param userId 用户id
* @return
*/
public JsonResult signInList(Long userId) {
SignIn signIn = signInMapper.selectById(userId);
List list = new ArrayList<>(7);
if (null == signIn) {
// 没有签过到
for (int i = 1; i < 8; i++) {
list.add(new SignInDto(i, 0));
}
} else {
// 签过到
Integer continueDays = signIn.getContinueDays();
// 1, 前六天的 flag是要固定的
if (continueDays <= 6) {
for (int i = 1; i < 8; i++) {
if (i <= continueDays) {
list.add(new SignInDto(i, 1));
} else {
list.add(new SignInDto(i, 0));
}
}
} else {
// 2, 6天后的签到天数要跟随日期增加
for (int i = 5; i > -2; i--) {
if (i > -1) {
list.add(new SignInDto(continueDays - i, 1));
} else {
list.add(new SignInDto(continueDays + 1, 0));
}
}
}
}
return JsonResult.buildSuccess(list);
}
效果如下:
1:签到第五天的时候
2:签到第7天的时候
3:签到第N天的时候
ps: 每天凌晨要把未连续签到用户的连续签到天数(sign_in表的continue_days字段)设置为0, 我这里使用的是数据库定时任务, 大家也可以使用代码的定时任务,看个人选择
/*定时任务*/
-- 每天凌晨重置超过时间未签到的连续签到天数为0
CREATE EVENT IF NOT EXISTS reset_expired_signins
ON SCHEDULE EVERY 1 DAY STARTS DATE_ADD(DATE_ADD(CURDATE(), INTERVAL 1 DAY), INTERVAL 5 MINUTE)
ON COMPLETION PRESERVE ENABLE
-- 更新语句
DO UPDATE sign_in SET continue_days = 0 WHERE id IN ( SELECT a.id FROM ( SELECT id FROM sign_in WHERE DATEDIFF(CURDATE(), update_time) > 1 ) AS a )