package com.wangz.service;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import com.wangz.constant.ApiConstant;
import com.wangz.constant.PointTypesConstant;
import com.wangz.exception.ParameterException;
import com.wangz.model.domain.ResultInfo;
import com.wangz.model.vo.SignInDinerInfo;
import com.wangz.utils.AssertUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
@Service
public class SignService {
@Value("${service.name.ms-oauth-server}")
private String oauthServerName;
@Value("${service.name.ms-points-server}")
private String pointsServerName;
@Resource
private RestTemplate restTemplate;
@Resource
private RedisTemplate redisTemplate;
/**
* 用户签到
*
* @param accessToken
* @param dateStr
* @return
*/
public int doSign(String accessToken, String dateStr) {
// 获取登录用户信息
SignInDinerInfo dinerInfo = loadSignInDinerInfo(accessToken);
// 获取日期
Date date = getDate(dateStr);
// 获取日期对应的天数,多少号
int offset = DateUtil.dayOfMonth(date) - 1; // 从 0 开始
// 构建 Key user:sign:5:yyyyMM
String signKey = buildSignKey(dinerInfo.getId(), date);
// 查看是否已签到
boolean isSigned = redisTemplate.opsForValue().getBit(signKey, offset);
AssertUtil.isTrue(isSigned, "当前日期已完成签到,无需再签");
// 签到
redisTemplate.opsForValue().setBit(signKey, offset, true);
// 统计连续签到的次数
int count = getContinuousSignCount(dinerInfo.getId(), date);
// 添加签到积分并返回
int points = addPoints(count, dinerInfo.getId());
return points;
}
/**
* 获取用户签到次数
*
* @param accessToken
* @param dateStr
* @return
*/
public long getSignCount(String accessToken, String dateStr) {
// 获取登录用户信息
SignInDinerInfo dinerInfo = loadSignInDinerInfo(accessToken);
// 获取日期
Date date = getDate(dateStr);
// 构建 Key
String signKey = buildSignKey(dinerInfo.getId(), date);
// e.g. BITCOUNT user:sign:5:202011
return (Long) redisTemplate.execute(
(RedisCallback<Long>) con -> con.bitCount(signKey.getBytes())
);
}
/**
* 获取当月签到情况
*
* @param accessToken
* @param dateStr
* @return
*/
public Map<String, Boolean> getSignInfo(String accessToken, String dateStr) {
// 获取登录用户信息
SignInDinerInfo dinerInfo = loadSignInDinerInfo(accessToken);
// 获取日期
Date date = getDate(dateStr);
// 构建 Key
String signKey = buildSignKey(dinerInfo.getId(), date);
// 构建一个自动排序的 Map
Map<String, Boolean> signInfo = new TreeMap<>();
// 获取某月的总天数(考虑闰年)
int dayOfMonth = DateUtil.lengthOfMonth(DateUtil.month(date) + 1,
DateUtil.isLeapYear(DateUtil.year(date)));
// bitfield user:sign:5:202011 u30 0
BitFieldSubCommands bitFieldSubCommands = BitFieldSubCommands.create()
.get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth))
.valueAt(0);
List<Long> list = redisTemplate.opsForValue().bitField(signKey, bitFieldSubCommands);
if (list == null || list.isEmpty()) {
return signInfo;
}
long v = list.get(0) == null ? 0 : list.get(0);
// 从低位到高位进行遍历,为 0 表示未签到,为 1 表示已签到
for (int i = dayOfMonth; i > 0; i--) {
/*
签到: yyyy-MM-01 true
未签到:yyyy-MM-01 false
*/
LocalDateTime localDateTime = LocalDateTimeUtil.of(date).withDayOfMonth(i);
boolean flag = v >> 1 << 1 != v;
signInfo.put(DateUtil.format(localDateTime, "yyyy-MM-dd"), flag);
v >>= 1;
}
return signInfo;
}
/**
* 统计连续签到的次数
*
* @param dinerId
* @param date
* @return
*/
private int getContinuousSignCount(Integer dinerId, Date date) {
// 获取日期对应的天数,多少号,假设是 30
int dayOfMonth = DateUtil.dayOfMonth(date);
// 构建 Key
String signKey = buildSignKey(dinerId, date);
// bitfield user:sgin:5:202011 u30 0
BitFieldSubCommands bitFieldSubCommands = BitFieldSubCommands.create()
.get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth))
.valueAt(0);
List<Long> list = redisTemplate.opsForValue().bitField(signKey, bitFieldSubCommands);
if (list == null || list.isEmpty()) {
return 0;
}
int signCount = 0;
long v = list.get(0) == null ? 0 : list.get(0);
/**
* 高位 低位
* 1 1 1 1 1 1 0 1 0 0 1 0 1 1 1 0 0 1 0 1 1 1 1 0 0 0 0 0 0 0 0 -- 31号 2,123,837,184
* 右移以后
* 0 1 1 1 1 1 1 0 1 0 0 1 0 1 1 1 0 0 1 0 1 1 1 1 0 0 0 0 0 0 0
* 然后再左移
* 1 1 1 1 1 1 0 1 0 0 1 0 1 1 1 0 0 1 0 1 1 1 1 0 0 0 0 0 0 0 0
* 结论:右移再左移如果等于自己,则表示未签到
*
*
* 1 1 1 1 1 1 0 1 0 0 1 0 1 1 1 0 0 1 0 1 1 1 1 0 0 0 0 0 0 0 1
* 右移以后
* 0 1 1 1 1 1 1 0 1 0 0 1 0 1 1 1 0 0 1 0 1 1 1 1 0 0 0 0 0 0 0
* 然后再左移
* 1 1 1 1 1 1 0 1 0 0 1 0 1 1 1 0 0 1 0 1 1 1 1 0 0 0 0 0 0 0 0
* 结论:右移再左移如果不等于自己,则表示已签到
*
*
* 1 1 1 1 1 1 0 1 0 0 1 0 1 1 1 0 0 1 0 1 1 1 1 0 0 0 0 0 0 0 -- 30号 1,061,918,592
*
* 1 1 1 1 1 1 0 1 0 0 1 0 1 1 1 0 0 1 0 1 1 1 1 0 0 0 0 0 0 -- 29号
*
* 1 1 1 1 1 1 0 1 0 0 1 0 1 1 1 0 0 1 0 1 1 1 1 0 0 0 0 0 -- 28号
*
* 1 1 1 1 1 1 0 1 0 0 1 0 1 1 1 0 0 1 0 1 1 1 1 0 0 0 0 -- 27号
*
* 1 1 1 1 1 1 0 1 0 0 1 0 1 1 1 0 0 1 0 1 1 1 1 0 0 0 -- 26号
*
* 1 1 1 1 1 1 0 1 0 0 1 0 1 1 1 0 0 1 0 1 1 1 1 0 0 -- 25号
*
* 1 1 1 1 1 1 0 1 0 0 1 0 1 1 1 0 0 1 0 1 1 1 1 0 -- 24号
*
* 1 1 1 1 1 1 0 1 0 0 1 0 1 1 1 0 0 1 0 1 1 1 1 -- 23号
*/
for (int i = dayOfMonth; i > 0; i--) {// i 表示位移操作次数
// 右移再左移,如果等于自己说明最低位是 0,表示未签到
if (v >> 1 << 1 == v) {
// 低位 0 且非当天说明连续签到中断了
if (i != dayOfMonth) break;
} else {
signCount++;
}
// 右移一位并重新赋值,相当于把最低位丢弃一位
v >>= 1;
}
return signCount;
}
/**
* 构建 Key -- user:sign:5:yyyyMM
*
* @param dinerId
* @param date
* @return
*/
private String buildSignKey(Integer dinerId, Date date) {
return String.format("user:sign:%d:%s", dinerId,
DateUtil.format(date, "yyyyMM"));
}
没有合适的资源?快使用搜索试试~ 我知道了~
Redis实战-food-social-contact-parent.zip
共126个文件
java:93个
xml:14个
yml:8个
需积分: 0 0 下载量 194 浏览量
2023-11-07
20:45:21
上传
评论
收藏 136KB ZIP 举报
温馨提示
Redis实战-food-social-contact-parent
资源推荐
资源详情
资源评论
收起资源包目录
Redis实战-food-social-contact-parent.zip (126个子文件)
ms-seckill.iml 80B
ms-diners.iml 80B
food-social-contact-parent.iml 80B
ms-gateway.iml 80B
ms-oauth2-server.iml 80B
ms-registry.iml 80B
ms-points.iml 80B
SignService.java 11KB
SeckillService.java 9KB
DinerPointsService.java 8KB
FollowService.java 7KB
ReviewsService.java 6KB
DinersService.java 6KB
AuthorizationServerConfiguration.java 4KB
SecurityConfiguration.java 4KB
DinerPointsMapper.java 3KB
AuthGlobalFilter.java 3KB
RestaurantTest.java 3KB
UserController.java 3KB
RedisTemplateConfiguration.java 2KB
ResultInfoUtil.java 2KB
SignInIdentity.java 2KB
DinerPointsController.java 2KB
RedisLock.java 2KB
DinersController.java 2KB
RedisTemplateConfiguration.java 2KB
OAuthController.java 2KB
OAuthControllerTest.java 2KB
RedisTemplateConfiguration.java 2KB
RedisTemplateConfiguration.java 2KB
RedisTemplateConfiguration.java 2KB
RestaurantService.java 2KB
HandleException.java 2KB
SendVerifyCodeService.java 2KB
ReviewsController.java 2KB
SignController.java 2KB
MyAuthenticationEntryPoint.java 2KB
AssertUtil.java 1KB
DinersMapper.java 1KB
SeckillController.java 1KB
FollowController.java 1KB
Restaurant.java 1KB
GlobalExceptionHandler.java 1KB
GlobalExceptionHandler.java 1KB
GlobalExceptionHandler.java 1KB
GlobalExceptionHandler.java 1KB
UserService.java 1KB
ResourceServerConfig.java 1KB
VoucherOrdersMapper.java 1KB
DinerPointsControllerTest.java 1KB
FollowMapper.java 1KB
RestaurantController.java 1KB
SeckillVouchersMapper.java 1014B
VoucherOrders.java 970B
RestaurantMapper.java 968B
SeckillVouchers.java 961B
FeedsVO.java 945B
RestTemplateConfiguration.java 891B
RestTemplateConfiguration.java 891B
RestTemplateConfiguration.java 891B
RestTemplateConfiguration.java 891B
SendVerifyCodeController.java 872B
RedisKeyConstant.java 764B
SignInDinerInfo.java 759B
ParameterException.java 738B
ClientOAuth2DataConfiguration.java 704B
ResultInfo.java 648B
Reviews.java 639B
ReviewsVO.java 601B
DinersDTO.java 559B
ApiConstant.java 558B
Diners.java 539B
DinnerMapper.java 538B
ReviewsMapper.java 535B
RedisLockConfiguration.java 516B
DinerPointsRankVO.java 502B
DinerPoints.java 499B
ShortDinerInfo.java 497B
OAuth2ClientConfiguration.java 463B
RegistryApplication.java 456B
RestTemplateConfiguration.java 455B
Follow.java 445B
RestTemplateConfiguration.java 429B
Oauth2ServerApplication.java 407B
RestaurantApplication.java 403B
DinersApplication.java 395B
PointsApplication.java 395B
FollowApplication.java 394B
SeckillApplication.java 390B
OAuth2ServerApplicationTests.java 385B
HelloController.java 384B
OAuthDinerInfo.java 384B
PointsApplicationTests.java 379B
IgnoreUrlsConfig.java 364B
BaseModel.java 340B
GatewayApplication.java 316B
PointTypesConstant.java 270B
LoginDinerInfo.java 256B
RestaurantApplicationTest.java 141B
Hello.java 43B
共 126 条
- 1
- 2
资源评论
武昌库里写JAVA
- 粉丝: 3136
- 资源: 1872
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功