最近做了一個簽到模組的需求,主要就是簽到,根據連籤天數提供不同和獎勵並在首頁展示不同的文案。獎勵分為金幣和話費,話費也是通過金幣的形式發放,但是有效期只有1天。
簽到需求
- 每日簽到給獎勵,七日一迴圈
- 若未簽到,從第一天開始至後七天。若簽到,從第一天簽到返回至七日後。七天一迴圈
- 返回一個月內簽到記錄
- 若簽到使用者為新簽到使用者,則其連續簽到第3天和第7天分別多領取有效期為一天的話費等額金幣
- 新增首頁簽到入口文案展示,文案提示根據簽到天數和是否是新人變化
- 給一個連續簽到總天數,不是七日一迴圈。
解決方案
資料庫設計:
簽到表
欄位設計包括自增id,使用者userId,簽到當天零點日期checkDate,獎勵record,簽到天數(一週)day,連籤總天數alwaysDays
獲取最近七天簽到記錄
通過sql
select * from 簽到表 where userId = #{userId} order by date desc limit 7
優化:userId建立索引, 通過order by … limit 7可以避免全表掃描 。
查詢出結果後存入快取,快取時間只要大於24小時即可,但考慮大多數人連籤不會堅持兩天,所以可以將快取失效時間設定72到96小時。時間太短則使用者下次簽到時需多次讀取資料庫增加資料庫負載(我的簽到邏輯是簽到時先通過快取查出最近一天簽到記錄,簽到後清除快取,展示時再訪問資料庫並存入快取)。時間太長又會浪費redis空間。
簽到邏輯
獲取最近七天簽到記錄和簽到總天數
如果最近一天簽到時間等於當天,則直接返回;如果簽到時間等於昨天,則將簽到天數day和連籤總天數alwaysDays增加1,若day大於7則重新置為1.存入資料庫同時移除快取並非同步進行獎勵金幣入庫。
如何判斷是否是新簽到使用者(七天內)
若簽到總天數為0或與昨天簽到記錄day相同(若不是昨天則發生斷籤,變成老使用者,day永遠小於等於7)則確定其為新簽到使用者,在進行獎勵金幣入庫時額外增加新簽到使用者獎勵。
獲取一個月內簽到記錄
根據傳入的日期引數date(轉化為時間戳10位Integer型別)通過Calendar
類求出當月的所有日期(日期為當天零點)。
通過日期開始和結束時間範圍求出當月所有簽到記錄並通過stream流轉為以簽到當天零點日期為key的map。迴圈當月日期並與map進行比較,若不為空則表示當天有簽到,返回true;否則返回false。因該介面入口簽到後才展示且一般使用者很少點開,所以暫未加快取。
展示一週簽到記錄
通過快取查詢出最近七天簽到記錄,判斷最近一天簽到是否是昨天或今天當天,若不是則發生斷籤,一週全部返回false。若未斷籤則根據1-7迴圈與day比較,day之前為true,day之後為false。
新簽到使用者額外領取金幣過期
因新簽到使用者額外領取的金幣只有一天的使用時間,所以需要有一個過期判定。
該需求可以通過mp訊息進行過期扣減。在新使用者額外獎勵入庫時傳送一條延時24小時的訊息,訊息主體為userId+獎勵金額。
訊息消費時可根據userId和出賬Type及時間範圍查詢出使用者金幣使用期內的出賬總額。若出賬小於獎勵金額則進行扣減操作。
同時為防止訊息丟失,可以每晚執行一個定時任務,掃描出當天獲取額外獎勵的新簽到使用者及其出賬記錄,通過stream流進行比對,將異常使用者id返回(或直接在定時任務內進行處理)(因where條件均有索引且可以將定時任務設定在晚上3、4點等使用者不活躍的時間,所以對資料庫負載較小)。
因為產品要求籤到日期取整點,可以通過 Calendar類獲取當前整點時間進行運算後傳送訊息
public static Long getNowHour() { Calendar calendar = Calendar.getInstance(); calendar.add(Calendar.DATE, 0); calendar.set(Calendar.HOUR_OF_DAY, calendar.get(HOUR_OF_DAT)); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); return calendar.getTimeInMillis()/1000; }
首頁根據簽到天數展示不同文案
通過是否是新簽到使用者和是否簽到展示不同文案。
若是新簽到使用者且未簽到,可根據昨日 簽到天數day對應1-6展示不同的提示文案(若昨日也未簽到則不可能是新簽到使用者)
如果有錯誤或者更優化的解決方案,歡迎大家在評論區留言探討。
也可以給我的個人公眾號私信留言。