大話(huà)業(yè)務(wù)場(chǎng)景與解決方案-做任務(wù)
背景
多數(shù)的移動(dòng)端APP都會(huì)有做任務(wù)領(lǐng)取獎(jiǎng)勵(lì)的功能模塊玻蝌,這類(lèi)需求的目的是培養(yǎng)用戶(hù)使用習(xí)慣蟹肘,提升用戶(hù)活躍性词疼,用戶(hù)完成任務(wù)獲得積分獎(jiǎng)勵(lì),通過(guò)積分兌換商品或者充值話(huà)費(fèi)帘腹,微信體現(xiàn)等贰盗。
擬定需求場(chǎng)景(如圖↓),概要:APP底部導(dǎo)航中新增小任務(wù)Tab阳欲,點(diǎn)擊Tab可查看任務(wù)完成進(jìn)度和領(lǐng)取情況舵盈,點(diǎn)擊去完成跳轉(zhuǎn)到做任務(wù)的業(yè)務(wù)界面,當(dāng)用戶(hù)完成任務(wù)并且滿(mǎn)足領(lǐng)取條件的時(shí)候球化,任務(wù)Tab需要紅點(diǎn)提醒用戶(hù)當(dāng)前有獎(jiǎng)勵(lì)可領(lǐng)取秽晚,用戶(hù)領(lǐng)取后并且當(dāng)前沒(méi)有待領(lǐng)取獎(jiǎng)勵(lì)小紅點(diǎn)消失,任務(wù)完成進(jìn)度和領(lǐng)取狀態(tài)僅保持當(dāng)天筒愚,隔天刷新爆惧。
業(yè)務(wù)分析
在開(kāi)發(fā)前需要對(duì)需求進(jìn)行整理,對(duì)細(xì)節(jié)進(jìn)行確認(rèn)锨能,然后設(shè)計(jì)解決方案,預(yù)估開(kāi)發(fā)時(shí)間芍耘,這里將對(duì)于業(yè)務(wù)中核心的內(nèi)容進(jìn)行梳理:
用戶(hù)想要完成任務(wù)址遇,需要去操作其他業(yè)務(wù)功能,如:評(píng)論成功后需要完成每日評(píng)論任務(wù)斋竞,關(guān)注主題后完成關(guān)注新手任務(wù)倔约,這里就涉及核心問(wèn)題,任務(wù)需要依賴(lài)于其他業(yè)務(wù)
為了保障后續(xù)拓展性坝初,任務(wù)需要支持后臺(tái)管理浸剩,配置任務(wù)名,描述鳄袍,任務(wù)類(lèi)型(每日绢要,新手,活動(dòng))拗小,完成次數(shù)重罪,獎(jiǎng)勵(lì)積分?jǐn)?shù)量,去完成跳轉(zhuǎn)uri 等
用戶(hù)完成任務(wù)后不用自動(dòng)領(lǐng)取獎(jiǎng)勵(lì)哀九,需要進(jìn)入到任務(wù)列表點(diǎn)擊領(lǐng)取操作剿配,可領(lǐng)取時(shí)導(dǎo)航Tab需要小紅點(diǎn)提醒,和產(chǎn)品確認(rèn)任務(wù)的完成和提醒的用戶(hù)體驗(yàn) 可以接受短時(shí)間延遲
用戶(hù)多次操作業(yè)務(wù)阅束,或者出現(xiàn)重復(fù)操作(惡意并發(fā)請(qǐng)求刷積分)呼胚,保證任務(wù)只能完成一次并且只能領(lǐng)取一次獎(jiǎng)勵(lì),需要保證冪等性
方案設(shè)計(jì)
核心目標(biāo):
- 任務(wù)依賴(lài)其他業(yè)務(wù)息裸,需要進(jìn)行解耦蝇更,不影響其他業(yè)務(wù)的功能和性能
- 設(shè)計(jì)后臺(tái)可管理沪编,便于后續(xù)拓展
- 抽象任務(wù)模塊,代碼抽象開(kāi)發(fā)
- 完成任務(wù)和領(lǐng)取需要保證冪等性
- 高可用
名詞定義:
事件
- 任務(wù)中涉及依賴(lài)其他業(yè)務(wù)簿寂,這里需要抽象出一個(gè)概念漾抬,用戶(hù)通過(guò)操作業(yè)務(wù),完成任務(wù)的這個(gè)操作常遂,我們把這個(gè)過(guò)程定義為用戶(hù)完成任務(wù)事件觸發(fā)完成纳令,如:評(píng)論事件,點(diǎn)贊事件克胳,關(guān)注事件平绩,等
解決方案:
在實(shí)現(xiàn)方案上,采用異步消耗隊(duì)列的方式漠另,依賴(lài)業(yè)務(wù)接口埋入事件上報(bào)捏雌,將用戶(hù)成功操作業(yè)務(wù)的任務(wù)事件上報(bào)到隊(duì)里中,然后開(kāi)發(fā)消息消耗的腳本程序笆搓,對(duì)消息中用戶(hù)觸發(fā)的任務(wù)事件進(jìn)行業(yè)務(wù)邏輯處理和DB操作性湿,更新用戶(hù)任務(wù)進(jìn)度和可領(lǐng)取狀態(tài),響應(yīng)給用戶(hù)(完成任務(wù)紅點(diǎn)提醒)满败,設(shè)計(jì)圖:
- 依賴(lài)業(yè)務(wù)解耦
- 依賴(lài)業(yè)務(wù)將操作成功用戶(hù)的任務(wù)事件上報(bào)到消息隊(duì)列肤频,然后程序進(jìn)行異步消耗
- 方案解決了依賴(lài)業(yè)務(wù)之間的強(qiáng)耦合,并且基本不影響現(xiàn)有依賴(lài)業(yè)務(wù)的接口性能
- 高可用:
- 通過(guò)調(diào)度系統(tǒng)啟動(dòng)多進(jìn)程對(duì)隊(duì)列進(jìn)行消耗
- 進(jìn)程守護(hù)系統(tǒng)算墨,守護(hù)進(jìn)程毕模活,奔潰重啟净嘀,可對(duì)執(zhí)行日志進(jìn)行記錄與查看
- 如: gocron
- 消息隊(duì)列監(jiān)控报咳,無(wú)法及時(shí)消耗進(jìn)行預(yù)警,保障即時(shí)性挖藏,避免長(zhǎng)時(shí)間的延遲
- rabbitmq
- redis list
- ... ...
- 容錯(cuò)與補(bǔ)償
- 隊(duì)列消耗失敗需要進(jìn)行記錄暑刃,并可根據(jù)業(yè)務(wù)場(chǎng)景,通過(guò)另外程序進(jìn)行補(bǔ)充處理
- 用戶(hù)操作業(yè)務(wù)上報(bào)任務(wù)事件不限制次數(shù)膜眠,以免用戶(hù)沒(méi)完成任務(wù)稍走,允許用戶(hù)重新嘗試去做任務(wù),程序消耗需要控制任務(wù)只能完成一次
- 通過(guò)調(diào)度系統(tǒng)啟動(dòng)多進(jìn)程對(duì)隊(duì)列進(jìn)行消耗
表結(jié)構(gòu):
-- 任務(wù)表
CREATE TABLE `task` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增',
`icon` varchar(300) NOT NULL DEFAULT '' COMMENT '圖標(biāo)',
`title` varchar(30) NOT NULL DEFAULT '' COMMENT '任務(wù)標(biāo)題',
`type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '任務(wù)類(lèi)型柴底,新手任務(wù)=1,每日任務(wù)=2',
`event` int(11) NOT NULL DEFAULT '0' COMMENT '事件',
`des` varchar(30) NOT NULL DEFAULT '' COMMENT '任務(wù)描述',
`target_num` int(11) NOT NULL DEFAULT '0' COMMENT '目標(biāo)數(shù)量',
`points` int(11) NOT NULL DEFAULT '0' COMMENT '金幣',
`sort` int(11) NOT NULL DEFAULT '0' COMMENT '排序',
`status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '狀態(tài) 0=下線(xiàn)婿脸,1=上線(xiàn),-1=刪除',
`app_version` varchar(15) NOT NULL DEFAULT '' COMMENT 'app版本號(hào)',
`app_version_compare` varchar(10) NOT NULL DEFAULT '' COMMENT 'app版本號(hào)比較運(yùn)算符',
`operator` varchar(10) NOT NULL DEFAULT '' COMMENT '操作人',
`jump_uri` varchar(300) NOT NULL DEFAULT '' COMMENT '跳轉(zhuǎn)協(xié)議',
`create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時(shí)間',
`update_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新時(shí)間',
`event_begin` timestamp NOT NULL DEFAULT '1970-01-02 00:00:00' COMMENT '事件開(kāi)始時(shí)間',
`event_end` timestamp NOT NULL DEFAULT '1970-01-02 00:00:00' COMMENT '事件結(jié)束時(shí)間',
`task_begin` timestamp NOT NULL DEFAULT '1970-01-02 00:00:00' COMMENT '任務(wù)開(kāi)始時(shí)間',
`task_end` timestamp NOT NULL DEFAULT '1970-01-02 00:00:00' COMMENT '任務(wù)結(jié)束時(shí)間',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 用戶(hù)任務(wù)情況表
CREATE TABLE `user_task_case` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用戶(hù)任務(wù)情況',
`user_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '用戶(hù)ID',
`task_id` int(11) NOT NULL DEFAULT '0' COMMENT '任務(wù)id',
`task_type` int(11) NOT NULL DEFAULT '0' COMMENT '任務(wù)類(lèi)型',
`event` int(11) NOT NULL DEFAULT '0' COMMENT '事件',
`task_uni` varchar(30) NOT NULL DEFAULT '' COMMENT '任務(wù)唯一標(biāo)識(shí)(唯一約束) ',
`target_num` int(11) NOT NULL DEFAULT '0' COMMENT '目標(biāo)數(shù)量',
`finish_num` int(11) NOT NULL DEFAULT '0' COMMENT '完成數(shù)量',
`points` int(11) NOT NULL DEFAULT '0' COMMENT '可領(lǐng)取金幣數(shù)量',
`status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '狀態(tài) 0=待完成柄驻,1=待領(lǐng)取狐树,2=已經(jīng)領(lǐng)取',
`finish_at` timestamp NOT NULL DEFAULT '1970-01-02 00:00:00' COMMENT '完成任務(wù)時(shí)間',
`get_at` timestamp NOT NULL DEFAULT '1970-01-02 00:00:00' COMMENT '領(lǐng)取時(shí)間',
`create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時(shí)間',
PRIMARY KEY (`id`),
UNIQUE KEY `uni_user_id_task_uni` (`user_id`,`task_uni`) USING BTREE,
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用戶(hù)任務(wù)情況表';
更新語(yǔ)句:
-- 更新領(lǐng)取狀態(tài),注意:WHERE條件鸿脓,強(qiáng)校驗(yàn)
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
表重點(diǎn)字段說(shuō)明:
- task_uni
- 任務(wù)唯一標(biāo)識(shí)
- user_id和task_uni 組合唯一約束索引
- 每日任務(wù):
- 任務(wù)id_任務(wù)類(lèi)型_日期(task_id_type_date)
- 默認(rèn)都是只做一次(新手任務(wù)/活動(dòng)任務(wù)):
- 任務(wù)id(task_id)
- 每日任務(wù):
- 冪等性
- user_task_case 表中 user_id和task_uni 組合唯一約束索引抑钟,通過(guò)mysql的唯一約束涯曲,保證了多進(jìn)程并行消耗事件隊(duì)列的情況下,每日任務(wù)和一次性任務(wù)不能重復(fù)INSERT
- 通過(guò)UPDATE的WHERE條件校驗(yàn)保障領(lǐng)取的冪等性
管理后臺(tái):
產(chǎn)品在規(guī)劃需求的時(shí)候會(huì)設(shè)計(jì)出相關(guān)后臺(tái)在塔,但是不一定設(shè)計(jì)的合理幻件,所以這里需要根據(jù)確認(rèn)的解決方案協(xié)助產(chǎn)品對(duì)于管理后臺(tái)進(jìn)行調(diào)整,保障后續(xù)的拓展性
代碼層面:
- 面向抽象開(kāi)發(fā)蛔溃,合理使用設(shè)計(jì)模式绰沥,便于后續(xù)的拓展
話(huà)外篇:
談近一年的感悟,近一年參與了新APP項(xiàng)目的開(kāi)發(fā)贺待,從0開(kāi)始搭建項(xiàng)目徽曲,看著DAU一點(diǎn)點(diǎn)兒的漲起來(lái),還是挺有成就感的麸塞。
角色上產(chǎn)生了變化秃臣,現(xiàn)在感覺(jué)自己更像是一個(gè)項(xiàng)目的參與者,而不是任務(wù)的執(zhí)行人哪工,完成業(yè)務(wù)開(kāi)發(fā)的同時(shí)也會(huì)對(duì)產(chǎn)品上有根深了解奥此。
空閑時(shí)間也會(huì)對(duì)競(jìng)品調(diào)研以及用戶(hù)使用意見(jiàn)或者問(wèn)題進(jìn)行跟進(jìn),站在用戶(hù)角度提供產(chǎn)品上的一些建議雁比。
后續(xù)會(huì)把新項(xiàng)目開(kāi)發(fā)過(guò)程中遇到問(wèn)題或者常見(jiàn)的業(yè)務(wù)場(chǎng)景下的解決方案進(jìn)行梳理出來(lái)進(jìn)行博文分享得院。
首發(fā)于Github : ??《大話(huà)WEB開(kāi)發(fā)》,WEB開(kāi)發(fā)相關(guān)經(jīng)驗(yàn)總結(jié)分享章贞,歡迎大家Star一波,后續(xù)想看不迷路 ??
發(fā)布時(shí)間:2020-04-06