UserSingInService.php 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. <?php
  2. namespace App\Repositories\Service;
  3. use Hyperf\Redis\Redis;
  4. class UserSingInService
  5. {
  6. /**
  7. * @var Redis
  8. */
  9. protected $redis;
  10. public function __construct()
  11. {
  12. $this->redis = make(Redis::class);
  13. }
  14. /**
  15. * 签到
  16. *
  17. * @param string $userId
  18. * @return int
  19. */
  20. public function doSigns(string $userId): bool
  21. {
  22. $day = date('d') - 1;
  23. $date = date('Y-m');
  24. return $this->redis->setbit($this->buildSignKey($userId, $date), (int)$day, true);
  25. }
  26. /**
  27. * 统计连续签到次数
  28. * 统计的是连续的签到次数,按业务需求改为七天的
  29. *
  30. * @param string $userId
  31. * @return int
  32. */
  33. public function getContinuousSignCount(string $userId): int
  34. {
  35. $day = date('d');
  36. $lastMonthCount = 0;
  37. if ($day == '1' || $day == '01') {
  38. $lastMonthCount = $this->lastMonthContinuousSignCount($userId);
  39. }
  40. $date = date('Y-m');
  41. $key = $this->buildSignKey($userId, $date);
  42. //获取本月的二进制数据
  43. $nowContinuousStr = $this->redis->rawCommand('BITFIELD', $key, 'GET', 'u' . (int)$day, 0);
  44. //获取连续签到天数
  45. if ($nowContinuousStr) {
  46. $count = $this->countContinuousSignCount($nowContinuousStr, (int)$day);
  47. } else {
  48. $count = 0;
  49. }
  50. $count = $count + $lastMonthCount;
  51. if ($count > 7) {
  52. $count = 7;
  53. }
  54. return $count;
  55. }
  56. /**
  57. * 检查用户是否签到
  58. *
  59. * @param string $userId
  60. * @param string $date
  61. * @return int
  62. */
  63. public function checkSign(string $userId, string $date): int
  64. {
  65. $day = date('d') - 1;
  66. return $this->redis->getBit($this->buildSignKey($userId, $date), $day);
  67. }
  68. /**
  69. * 统计上个月连续签到总数
  70. *
  71. * @param string $userId
  72. * @return int
  73. */
  74. public function lastMonthContinuousSignCount(string $userId): int
  75. {
  76. $date = date('Ym', strtotime('-1 month'));
  77. //上个月最后一天
  78. $day = date("d", strtotime(-date('d') . 'day'));
  79. $key = $this->buildSignKey($userId, $date);
  80. //获取本月签到的所有二进制数据
  81. $continuousStr = $this->redis->get($key);
  82. if ($continuousStr) {
  83. $res = $this->countContinuousSignCount($continuousStr, (int)$day);
  84. } else {
  85. $res = 0;
  86. }
  87. return $res;
  88. }
  89. /**
  90. * 连续签到计算
  91. *
  92. * @param string $continuousStr
  93. * @param string $day
  94. * @return int
  95. */
  96. private function countContinuousSignCount(array $continuousStr, int $day): int
  97. {
  98. if (!empty($continuousStr)) {
  99. $v = $continuousStr[0];
  100. $count = 0;
  101. for ($i = 0; $i < $day; $i++) {
  102. if ($v >> 1 << 1 == $v) {
  103. // 低位为0且非当天说明连续签到中断了
  104. if ($i > 0) {
  105. break;
  106. }
  107. } else {
  108. $count += 1;
  109. }
  110. $v >>= 1;
  111. }
  112. } else {
  113. $count = 0;
  114. }
  115. return $count;
  116. }
  117. /**
  118. * 构建签到key
  119. *
  120. * @param string $userId
  121. * @param string $date
  122. * @return string
  123. */
  124. private function buildSignKey(string $userId, string $date): string
  125. {
  126. return "u:sign:{$userId}:{$date}";
  127. }
  128. }