背景
多數的移動端APP都會有做任務領取獎勵的功能模組,這類需求的目的是培養使用者使用習慣,提升使用者活躍性,使用者完成任務獲得積分獎勵,通過積分兌換商品或者充值話費,微信體現等。
擬定需求場景(如圖↓),概要:APP底部導航中新增小任務Tab,點選Tab可檢視任務完成進度和領取情況,點選去完成跳轉到做任務的業務介面,當使用者完成任務並且滿足領取條件的時候,任務Tab需要紅點提醒使用者當前有獎勵可領取,使用者領取後並且當前沒有待領取獎勵小紅點消失,任務完成進度和領取狀態僅保持當天,隔天重新整理。
業務分析
在開發前需要對需求進行整理,對細節進行確認,然後設計解決方案,預估開發時間,這裡將對於業務中核心的內容進行梳理:
-
使用者想要完成任務,需要去操作其他業務功能,如:評論成功後需要完成每日評論任務,關注主題後完成關注新手任務,這裡就涉及核心問題,任務需要依賴於其他業務
-
為了保障後續擴充性,任務需要支援後臺管理,配置任務名,描述,任務型別(每日,新手,活動),完成次數,獎勵積分數量,去完成跳轉uri 等
-
使用者完成任務後不用自動領取獎勵,需要進入到任務列表點選領取操作,可領取時導航Tab需要小紅點提醒,和產品確認任務的完成和提醒的使用者體驗 可以接受短時間延遲
-
使用者多次操作業務,或者出現重複操作(惡意併發請求刷積分),保證任務只能完成一次並且只能領取一次獎勵,需要保證冪等性
方案設計
核心目標:
- 任務依賴其他業務,需要進行解耦,不影響其他業務的功能和效能
- 設計後臺可管理,便於後續擴充
- 抽象任務模組,程式碼抽象開發
- 完成任務和領取需要保證冪等性
- 高可用
名詞定義:
事件
- 任務中涉及依賴其他業務,這裡需要抽象出一個概念,使用者通過操作業務,完成任務的這個操作,我們把這個過程定義為使用者完成任務事件觸發完成,如:評論事件,點贊事件,關注事件,等
解決方案:
在實現方案上,採用非同步消耗佇列的方式,依賴業務介面埋入事件上報,將使用者成功操作業務的任務事件上報到隊裡中,然後開發訊息消耗的指令碼程式,對訊息中使用者觸發的任務事件進行業務邏輯處理和DB操作,更新使用者任務進度和可領取狀態,響應給使用者(完成任務紅點提醒),設計圖:
- 依賴業務解耦
- 依賴業務將操作成功使用者的任務事件上報到訊息佇列,然後程式進行非同步消耗
- 方案解決了依賴業務之間的強耦合,並且基本不影響現有依賴業務的介面效能
- 高可用:
- 通過排程系統啟動多程式對佇列進行消耗
- 程式守護系統,守護程式保活,奔潰重啟,可對執行日誌進行記錄與檢視
- 如: gocron
- 訊息佇列監控,無法及時消耗進行預警,保障即時性,避免長時間的延遲
- rabbitmq
- redis list
- ... ...
- 容錯與補償
- 佇列消耗失敗需要進行記錄,並可根據業務場景,通過另外程式進行補充處理
- 使用者操作業務上報任務事件不限制次數,以免使用者沒完成任務,允許使用者重新嘗試去做任務,程式消耗需要控制任務只能完成一次
- 通過排程系統啟動多程式對佇列進行消耗
表結構:
-- 任務表
CREATE TABLE `task` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增',
`icon` varchar(300) NOT NULL DEFAULT '' COMMENT '圖示',
`title` varchar(30) NOT NULL DEFAULT '' COMMENT '任務標題',
`type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '任務型別,新手任務=1,每日任務=2',
`event` int(11) NOT NULL DEFAULT '0' COMMENT '事件',
`des` varchar(30) NOT NULL DEFAULT '' COMMENT '任務描述',
`target_num` int(11) NOT NULL DEFAULT '0' COMMENT '目標數量',
`points` int(11) NOT NULL DEFAULT '0' COMMENT '金幣',
`sort` int(11) NOT NULL DEFAULT '0' COMMENT '排序',
`status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '狀態 0=下線,1=上線,-1=刪除',
`app_version` varchar(15) NOT NULL DEFAULT '' COMMENT 'app版本號',
`app_version_compare` varchar(10) NOT NULL DEFAULT '' COMMENT 'app版本號比較運算子',
`operator` varchar(10) NOT NULL DEFAULT '' COMMENT '操作人',
`jump_uri` varchar(300) NOT NULL DEFAULT '' COMMENT '跳轉協議',
`create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '建立時間',
`update_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新時間',
`event_begin` timestamp NOT NULL DEFAULT '1970-01-02 00:00:00' COMMENT '事件開始時間',
`event_end` timestamp NOT NULL DEFAULT '1970-01-02 00:00:00' COMMENT '事件結束時間',
`task_begin` timestamp NOT NULL DEFAULT '1970-01-02 00:00:00' COMMENT '任務開始時間',
`task_end` timestamp NOT NULL DEFAULT '1970-01-02 00:00:00' COMMENT '任務結束時間',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 使用者任務情況表
CREATE TABLE `user_task_case` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '使用者任務情況',
`user_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '使用者ID',
`task_id` int(11) NOT NULL DEFAULT '0' COMMENT '任務id',
`task_type` int(11) NOT NULL DEFAULT '0' COMMENT '任務型別',
`event` int(11) NOT NULL DEFAULT '0' COMMENT '事件',
`task_uni` varchar(30) NOT NULL DEFAULT '' COMMENT '任務唯一標識(唯一約束) ',
`target_num` int(11) NOT NULL DEFAULT '0' COMMENT '目標數量',
`finish_num` int(11) NOT NULL DEFAULT '0' COMMENT '完成數量',
`points` int(11) NOT NULL DEFAULT '0' COMMENT '可領取金幣數量',
`status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '狀態 0=待完成,1=待領取,2=已經領取',
`finish_at` timestamp NOT NULL DEFAULT '1970-01-02 00:00:00' COMMENT '完成任務時間',
`get_at` timestamp NOT NULL DEFAULT '1970-01-02 00:00:00' COMMENT '領取時間',
`create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '建立時間',
PRIMARY KEY (`id`),
UNIQUE KEY `uni_user_id_task_uni` (`user_id`,`task_uni`) USING BTREE,
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='使用者任務情況表';
複製程式碼
更新語句:
-- 更新領取狀態,注意:WHERE條件,強校驗
UPDATE user_task_case
SET `status`=2,finish_at=CURRENT_TIMESTAMP
WHERE id=:id AND user_id=:user_id AND `status`=1 AND finish_num>=target_num
複製程式碼
表重點欄位說明:
-
task_uni
- 任務唯一標識
-
user_id和task_uni 組合唯一約束索引
- 每日任務:
- 任務id_任務型別_日期(task_id_type_date)
- 預設都是隻做一次(新手任務/活動任務):
- 任務id(task_id)
- 每日任務:
-
冪等性
- user_task_case 表中 user_id和task_uni 組合唯一約束索引,通過mysql的唯一約束,保證了多程式並行消耗事件佇列的情況下,每日任務和一次性任務不能重複INSERT
- 通過UPDATE的WHERE條件校驗保障領取的冪等性
管理後臺:
產品在規劃需求的時候會設計出相關後臺,但是不一定設計的合理,所以這裡需要根據確認的解決方案協助產品對於管理後臺進行調整,保障後續的擴充性
程式碼層面:
- 面向抽象開發,合理使用設計模式,便於後續的擴充
話外篇:
談近一年的感悟,近一年參與了新APP專案的開發,從0開始搭建專案,看著DAU一點點兒的漲起來,還是挺有成就感的。
角色上產生了變化,現在感覺自己更像是一個專案的參與者,而不是任務的執行人,完成業務開發的同時也會對產品上有根深瞭解。
空閒時間也會對競品調研以及使用者使用意見或者問題進行跟進,站在使用者角度提供產品上的一些建議。
後續會把新專案開發過程中遇到問題或者常見的業務場景下的解決方案進行梳理出來進行博文分享。
首發於Github : ?《大話WEB開發》,WEB開發相關經驗總結分享,歡迎大家Star一波,後續想看不迷路 ?
釋出時間:2020-04-06