适用场景如签到送积分、签到领取奖励等,大致需求如下:
签到1天送1积分,连续签到7天,15天送,30天以上送不同积分等。
如果连续签到中断,则重置计数,每月初重置计数。
显示用户某个月的签到次数和首次签到时间。
在日历控件上展示用户每月签到情况,可以切换年月显示……等等
@Service
public class DailyCheckInServiceImpl implements DailyCheckInService {
@Autowired
private RedisService redisTemplate;
/**
* 用户签到
*
* @param aid 用户ID
* @param date 日期
* @return 之前的签到状态
*/
@Override
public boolean doSign(Long aid, LocalDate date) {
int offset = date.getDayOfMonth() - 1;
return redisTemplate.opsForValue().setBit(buildSignKey(aid, date), offset, true);
}
/**
* 检查用户是否签到
*
* @param aid 用户ID
* @param date 日期
* @return 当前的签到状态
*/
@Override
public boolean checkSign(Long aid, LocalDate date) {
int offset = date.getDayOfMonth() - 1;
return redisTemplate.opsForValue().getBit(buildSignKey(aid, date), offset);
}
/**
* 获取用户当月签到次数
*
* @param aid 用户ID
* @param date 日期
* @return 当月的签到次数
*/
@Override
public long getSignCount(Long aid, LocalDate date) {
return redisTemplate.execute((RedisCallback<Long>) con -> con.bitCount(buildSignKey(aid, date).getBytes(StandardCharsets.UTF_8)));
}
@Override
public long getContinuousSignCount(Long aid, LocalDate date) {
int signCount = 0;
List<Long> list = redisTemplate.opsForValue().bitField(buildSignKey(aid, date), BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType
.unsigned(date.getDayOfMonth())).valueAt(0));
if (list != null && list.size() > 0) {
// 取低位连续不为0的个数即为连续签到次数,需考虑当天尚未签到的情况
long v = list.get(0) == null ? 0 : list.get(0);
for (int i = 0; i < date.getDayOfMonth(); i++) {
if (v >> 1 << 1 == v) {
// 低位为0且非当天说明连续签到中断了
if (i > 0) {
break;
}
} else {
signCount += 1;
}
v >>= 1;
}
}
return signCount;
}
/**
* 获取当月首次签到日期
*
* @param aid 用户ID
* @param date 日期
* @return 首次签到日期
*/
/**
* 获取当月首次签到日期
*/
@Override
public LocalDate getFirstSignDate(Long aid, LocalDate date) {
long bitPosition = (Long) redisTemplate.execute((RedisCallback) cbk -> cbk.bitPos(buildSignKey(aid, date).getBytes(), true));
return bitPosition < 0 ? null : date.withDayOfMonth((int) bitPosition + 1);
}
/**
* 获取当月签到情况
*
* @param aid 用户ID
* @param date 日期
* @return Key为签到日期,Value为签到状态的Map
*/
@Override
public Map<String, Boolean> getSignInfo(Long aid, LocalDate date) {
Map<String, Boolean> signMap = new HashMap<>(date.getDayOfMonth());
List<Long> list = redisTemplate.opsForValue().bitField(buildSignKey(aid, date), BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType
.unsigned(date.lengthOfMonth())).valueAt(0));
if (!CollectionUtils.isEmpty(list)) {
// 由低位到高位,为0表示未签,为1表示已签
long v = list.get(0) == null ? 0 : list.get(0);
for (int i = date.lengthOfMonth(); i > 0; i--) {
LocalDate d = date.withDayOfMonth(i);
signMap.put(formatDate(d, "yyyy-MM-dd"), v >> 1 << 1 != v);
v >>= 1;
}
}
return new TreeMap<>(signMap);
}
/**
* 构建指定类型的Redis的key:u:sign:10000:202001
*/
private static String buildSignKey(Long aid, LocalDate date) {
return String.format("u:sign:%d:%s", aid, formatDate(date));
}
/**
* 固定202205格式
*/
private static String formatDate(LocalDate date) {
return formatDate(date, "yyyyMM");
}
/**
* LocalDate按照指定格式进行转换字符串
*/
private static String formatDate(LocalDate date, String pattern) {
return date.format(DateTimeFormatter.ofPattern(pattern));
}
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)