直播平臺原始碼開發,簽到功能的實現

雲豹科技程式設計師發表於2021-12-16

1 簽到日曆週期

簽到週期: 直播平臺原始碼常用的簽到週期為一週或者一個月.我們採用的是一個月的方案.市面上的簽到日曆介面都大同小異,接下來我會給大家分享以月為週期的簽到日曆實現方案以及伴生的簽到任務實現方案.

2 展示效果以及介面分析

2.1 效果圖
在這裡插入圖片描述

2.2 需求分析

通過圖上分析,可大致把直播平臺原始碼的這個介面分成四個部分

  • 頭部的總積分部分
  • 最關鍵的簽到日曆展示部分
  • 連續簽到文案配置部分
  • 簽到任務展示部分

通過分析我把直播平臺原始碼的這個介面分成了三個介面

  • /signIn GET協議 用於查詢頭部的總積分和簽到日曆部分.
  • /signIn/configuration GET協議查詢連續簽到文案配置,如果不需要後臺可配置連續簽到獲取積分的數量和文案,此介面可省略,前端寫死.
  • /signIn/task GET協議 用於查詢簽到任務,以及直播平臺原始碼各個任務的完成狀態.

3 查詢總積分,簽到日曆介面

public ResponseResult selectSignIn(Integer userId, Integer year, Integer month) {
        boolean signFlag = Boolean.FALSE;
        String signKey = String.format(RedisKeyConstant.USER_SIGN_IN, year, userId);
        LocalDate date = LocalDate.of(year, month, 1);
        //這個方法前面的文章有介紹過.是查詢出一個偏移值區間的點陣圖集合
        List<Long> list = cacheClient.getBit(signKey, month * 100 + 1, date.lengthOfMonth());
        //查詢reids中當前使用者補籤的hash列表 (hash列表的key為補籤的日期,value存在就說明這個日期補簽了)
        String retroactiveKey = String.format(RedisKeyConstant.USER_RETROACTIVE_SIGN_IN, date.getMonthValue(), userId);
        Set<String> keys = cacheClient.hkeys(retroactiveKey);
        TreeMap<Integer, Integer> signMap = new TreeMap<>();
        if (list != null && list.size() > 0) {
            // 由低位到高位,為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);
                int type = 0;
                if (v >> 1 << 1 != v) {
                    //狀態為正常簽到
                    type = 1;
                    //這裡和當前日期對比,方便前端特殊標記今天是否簽到
                    if (d.compareTo(LocalDate.now()) == 0) {
                        signFlag = Boolean.TRUE;
                    }
                }
                if (keys.contains(d.getDayOfMonth() + "")) {
                    //狀態為補籤
                    type = 2;
                }
                //返回給前端當月的所有日期,以及籤,補籤或者未籤的狀態
                signMap.put(Integer.parseInt(d.format(DateTimeFormatter.ofPattern("dd"))), type);
                v >>= 1;
            }
        }
        ResponseResult responseResult = ResponseResult.newSingleData();
        Map<String, Object> result = new HashMap<>(2);
        //前文有介紹過這個表儲存了使用者的總積分
        UserIntegral userIntegral = userIntegralService.getOne(new LambdaQueryWrapper<UserIntegral>().eq(UserIntegral::getUserId, userId));
        //使用者總積分
        result.put("total", userIntegral.getIntegral());
        //使用者今日是否簽到
        result.put("todaySignFlag", signFlag ? 1 : 0);
        //後端返回日期是為了防止手機端直接修改系統時間導致的問題
        result.put("today", LocalDate.now().getDayOfMonth());
        //當月的簽到情況
        result.put("signCalendar", signMap);
        //返回給前端這個月的第一天是星期幾,方便前端渲染日曆圖的時候定位
        result.put("firstDayOfWeek", date.getDayOfWeek().getValue());
        //伺服器的當前月份(同上,防止手機端直接修改系統時間)
        result.put("monthValue", date.getMonthValue());
        //使用者當月補籤的次數
        result.put("retroactiveCount", keys.size());
        //日曆部分會有上月的結尾幾天的資料,所以這裡需要返回給前端上個月共有多少天
        result.put("lengthOfLastMonth", date.minusMonths(1).lengthOfMonth());
        responseResult.setData(result);
        return responseResult;
    }

因為直播平臺原始碼整體使用了Redis點陣圖的查詢,每個使用者的簽到資料都是通過key隔離開的,時間複雜度為O(1).實測百毫秒內可返回資料

4.查詢簽到任務以及任務的完成狀態

直播平臺原始碼這一部分採用的是redis和mysql結合查詢的方式.任務我們做了後臺可配置.分為只能完成一次的福利任務和每天都可以重置的每日任務.

4.1 表結構

設計這張任務表的時候,總要就是型別和跳轉方式需要注意.因為直播平臺原始碼中不同的任務有不同的功能劃分.用jump_type去區分各自的功能區域.jump_source可以是H5地址也可以是手機端的路由地址.可以做到靈活調控.前端呼叫完成任務的介面傳入任務對應的task_tag就可以完成指定的任務

CREATE TABLE `t_user_integral_task` (
  `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `task_type` tinyint(4) DEFAULT '1' COMMENT '任務型別 1.每日任務 2福利任務',
  `task_tag` varchar(100) DEFAULT NULL COMMENT '任務前端標識(大寫字母組合)',
  `task_title` varchar(100) DEFAULT NULL COMMENT '任務標題',
  `icon` varchar(255) DEFAULT NULL COMMENT '小圖示',
  `task_copy` varchar(100) DEFAULT NULL COMMENT '任務文案',
  `integral` int(16) DEFAULT '0' COMMENT '任務贈送積分數',
  `jump_type` tinyint(4) DEFAULT NULL COMMENT '跳轉方式 1.跳轉指定商品 2.跳轉連結 3.跳轉指定介面,4:跳轉隨機商品',
  `jump_source` text COMMENT '跳轉或分享的地址',
  `sort` tinyint(2) DEFAULT '0' COMMENT '排序號',
  `delete_flag` tinyint(2) DEFAULT '0' COMMENT '刪除/隱藏,0:未刪除/未隱藏,1:已刪除/已隱藏',
  `create_time` datetime DEFAULT NULL COMMENT '建立時間',
  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '修改時間',
  PRIMARY KEY (`id`) USING BTREE) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='使用者任務表'

4.2 任務查詢

因為直播平臺原始碼的每日任務和福利任務大概也就十條左右,所以mysql查詢是非常快速的.然後完成狀態儲存在redis中,時間複雜度為O(1)

public ResponseResult selectSignInTask(Integer userId) {
        ResponseResult responseResult = ResponseResult.newSingleData();
        //先查出簽到任務的mysql記錄.
        List<UserIntegralTask> userIntegralTaskList = list(new LambdaQueryWrapper<UserIntegralTask>()
                .orderByDesc(UserIntegralTask::getTaskType).orderByAsc(UserIntegralTask::getSort));
        //建立一個map,key為任務的task_tag,value存在則是完成了該任務.
        //每日任務和福利任務分為兩個reids hash儲存.每日任務的key中包含當天日期,過期時間為一天.福利任務則是永久儲存
        Map<String, String> completeFlagMap = new HashMap<>(userIntegralTaskList.size());
        Map<String, String> welfareMap = cacheClient.hgetAll(String.format(RedisKeyConstant.USER_SIGN_WELFARE_TASK, userId));
        if (CollUtil.isNotEmpty(welfareMap)) completeFlagMap.putAll(welfareMap);
        Map<String, String> dailyMap = cacheClient.hgetAll(String.format(RedisKeyConstant.USER_SIGN_DAILY_TASK, LocalDate.now().getDayOfMonth(), userId));
        //把兩個hash合併
        if (CollUtil.isNotEmpty(dailyMap)) completeFlagMap.putAll(dailyMap);
        //迴圈庫中的任務列表,並用hash的get方法查詢是否完成,然後給到前端
        userIntegralTaskList.forEach(task -> {
            task.setCreateTime(null);
            task.setUpdateTime(null);
            task.setIntegral(null);
            String value = completeFlagMap.get(task.getTaskTag());
            if (null == value) {
                task.setCompleteFlag(0);
            } else {
                task.setCompleteFlag(1);
            }
        });
        responseResult.setData(userIntegralTaskList);
        return responseResult;
    }

4.3 完成任務

完成任務的方法.設定為一個公共方法.傳入對應的task_tag標識去完成指定任務.也就只需要判斷一下他是直播平臺原始碼的每日任務還是福利任務.分別寫入不同的redis hash裡.

   //虛擬碼
    public ResponseResult saveSignInTask(Integer userId, String tag) {
       //查詢出mysql中對應的tag任務,獲取關鍵資訊.(`integral`)
       ....
       //寫入積分記錄表.對應當前任務title的記錄
       ...
       //在redis裡寫入當前使用者的這個任務完成狀態(這裡要注意如果是每日任務要給hash 列表給一天的過期時間,防止髒資料長時間不被清理,佔用redis的記憶體空間)
    }

至此一個以redis點陣圖方案的簽到功能就在直播平臺原始碼中實現完畢了.實現內容大致簽到,補籤,積分增減,可配置任務功能,希望以上分享對大家有幫助。

本文轉載自網路,轉載僅為分享乾貨知識,如有侵權歡迎聯絡雲豹科技進行刪除處理
原文連結:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69996194/viewspace-2848131/,如需轉載,請註明出處,否則將追究法律責任。

相關文章