序
新的一年已經拉開了序幕,有的忙着離職、有的忙着請求加薪,有的忙着春運,有人忙着相親。。
對於絕大多數人來說,新的一年就是新的開始,新的開始也就是雄心壯志立flag的之時,只求不恍惚間到年尾,然后再把去年的flag,copy一次。
千里之行,始於足下。所以,我的雄心壯志,也從這簡簡單單的知識分享開始。
說到知識分享,就不能不提去年年底大火的直播答題app,不過,隨着時間的推移,貌似熱度已漸減。作為一個不算資深的程序猿,當別人為了沖關成功而欣喜,為了失敗而失落時,我覺得程序猿們應該有透過現象看本質的想法與能力,正如,不以物喜,不以己悲。
廢話不多少,好文剛出鍋,大家趁熱食用。
01
本次我分享的是小程序版好友對戰答題的實現思路與具體方法,由於篇幅問題,我將分成多個小段進行分享。首先我們先來分析下需求流程。
首先,用戶進入小程序。有兩個選擇,一,發起挑戰;二.圍觀對戰,選擇圍觀對戰則隨機進入一個正在對戰的房間,成為圍觀觀眾。選擇發起挑戰,則服務端生成一個房間號,然后觸發分享操作,攜帶生成的房間號,由用戶選擇分享給好友或者微信群。
微信用戶通過分享的卡片進入小程序后,可選擇參戰或者圍觀,如果已經選擇參戰了,則其他用戶自動成為圍觀用戶。此時,發起方的頁面狀態由等待好友響應變為等待開始。在發起方單擊開始之前,參戰的用戶可選擇退出參戰,此時,任意一個圍觀用戶則可以選擇參戰。
答題正式開始時,每道題有10s的時間回答。答錯,或者超時,均認為答題錯誤,則不加分。答對,則根據答題用時,(11-所用秒數)*10的公式進行加分,每題滿分100(留了1s時間,用戶客戶端和服務端之間的通訊耗時,以及客戶端渲染題目所需要的時間),最后一題雙倍分數。
02
下面說下程序的實現邏輯:
程序分為兩部分,小程序端和服務端。為了滿足答題的及時性以及用戶體驗,小程序與服務端間的通訊使用WebSocket。小程序端負責用戶交互,展示返回的數據。
服務端負責處理用戶創建房間、進入房間、圍觀、發彈幕的操作。
服務端的邏輯功能包括:處理圍觀用戶的請求,圍觀用戶的請求分為兩種,隨機圍觀和指定房間號圍觀,服務端根據用戶請求數據中是否包含房間號進行判斷,如果不含,則隨機進入正在進行中的對戰房間進行圍觀。
處理圍觀用戶發彈幕請求。用戶在答題過程中,圍觀用戶可進行交流評論,然后以彈幕的形式實時顯示在界面中。
處理用戶發起挑戰的請求。用戶發起挑戰,則生成一個房間號,存儲在數據庫,並返回給請求用戶。
處理應戰用戶請求。用戶點擊應戰按鈕后,設置當前房間狀態為配對成功,其他用戶則不能再發起此房間的挑戰。
處理放棄挑戰請求。與應戰操作邏輯相反。
處理房主點擊開始請求。房主點擊開始后,服務端隨機從數據庫中抽取有效的題目。並每隔11s(考慮到客戶端與服務端之間的通訊耗時和客戶端頁面渲染耗時),推送新的題目,直到答題完畢。
處理用戶提交答案請求。用戶提交答案時,從數據庫(或緩存)中匹配答案,正確時,則根據耗時,計算本輪分數,另外,需要將答題結果推送給圍觀用戶。錯誤時,則將正確答案推送給答題者。
處理用戶超時未答。當服務端在11秒內未收到提交的答題請求,則自動判斷答題超時,將正確答案推送給超時用戶。
03
數據庫中,跟題目相關的表有兩個,一個是題庫表,一個是每個對戰房間的題目表。表結構如下:
題庫表:
| 列名 |
類型 |
說明 |
| Id |
int |
主鍵,唯一標識 |
| CreateTime |
DateTime |
題目的創建時間 |
| Title |
Nvarchar |
標題 |
| Options |
Nvarchar |
答案選項 |
| Status |
int |
是否可用,默認1,可用。0為不可用 |
| Level |
int |
難易程度,值越大越難 |
試卷表:
| 列名 |
類型 |
說明 |
| Id |
int |
主鍵,唯一標識 |
| Title |
Nvarchar |
標題 |
| Options |
Nvarchar |
答案選項 |
| Answer |
int |
答案的序號。從1開始。 |
| Level |
int |
難易程度,值越大越難 |
| HomeId |
int |
房間號 |
| SubjectId |
int |
題庫編號 |
從上面的表中可以看出,題目的答案選項我是用nvarchar存儲的,這樣方便管理人員進行編輯。默認第一個為正確答案。所以,在從題庫中抽取題目時,需要將答案順序打亂,並記錄正確答案的序號。
具體實現思路是,首先隨機從題庫中抽取有效的題目存入臨時表,然后遍歷每條記錄,把選項打亂順序,並存入新表。最后刪除臨時表,將已選的題目的狀態更新為不可用。代碼如下:
CREATE PROC [dbo].[proc_getsubject](@count INT,@homeId INT) AS BEGIN IF OBJECT_ID('tempdb..#Subject_TEMP','U') IS NOT NULL DROP TABLE #Subject_TEMP --隨機從表中獲取指定條數的有效題目,並存入臨時表 SELECT TOP (@count) Title,Options,Id,Level INTO #Subject_TEMP FROM dbo.Subject WHERE Status=1 ORDER BY NEWID() --刪除試卷中,當前房間號已存在的題目 DELETE [dbo].[ActivityItems] WHERE [HomeId]=@homeId --遍歷每個題目 WHILE @count>0 BEGIN DECLARE @title NVARCHAR(500),@options NVARCHAR(2000), @level INT,--難易程度 @newoptions NVARCHAR(2000),@id INT,@optionCount INT ,--當前答案的數量 @answer INT--答案的索引,從1開始。 SET @newoptions='' --初始化 --從臨時表中,取出一條數據 SELECT TOP 1 @title=Title,@options=Options,@id=Id,@level=Level FROM #Subject_TEMP IF OBJECT_ID('tempdb..#Options_TEMP','U') IS NOT NULL DROP TABLE #Options_TEMP --使用表值函數,分割答案選項,並打亂順序。 SELECT * INTO #Options_TEMP FROM dbo.F_SplitSTR(@options,',') --獲取選項的數量 SELECT @optionCount=COUNT(1) FROM #Options_TEMP DECLARE @temp_i INT--循環里的索引 SET @temp_i =1 --循環分割后的答案,拼接成新的答案選項字符串 WHILE @optionCount>0 BEGIN DECLARE @item NVARCHAR(200),@sort INT --隨機從答案選項中選擇 SELECT TOP 1 @item=col,@sort=sort FROM #Options_TEMP ORDER BY NEWID() SET @newoptions+=@item+',' IF @sort=1 SET @answer=@temp_i SET @optionCount-=1 SET @temp_i+=1 DELETE #Options_TEMP WHERE sort=@sort END SET @count-=1 --從臨時表中刪除剛剛處理過的題目 DELETE #Subject_TEMP WHERE Id=@id --將處理后的題目信息存入試卷表 PRINT(@newoptions) INSERT [dbo].[ActivityItems] VALUES(@title,@newoptions,@answer,@level,@HomeId,@id) END /*更新已選題庫為無效狀態,保證每道題只出現1次,測試時,可不執行此代碼。這樣題目是可以重復利用的*/ --UPDATE dbo.Subject SET Status=0 WHERE Id IN (SELECT SubjectId FROM [ActivityItems] WHERE HomeId=@homeId) SELECT * FROM ActivityItems WHERE HomeId=@homeId ORDER BY [Level] END

其中,用於分割選項的方法代碼如下:
CREATE FUNCTION [dbo].[f_splitSTR]( @s VARCHAR(8000), --待分拆的字符串 @split VARCHAR(10) --數據分隔符 )RETURNS @re TABLE(col VARCHAR(100),sort INT) AS BEGIN DECLARE @splitlen INT,@sort INT SET @sort=0 SET @splitlen=LEN(@split+'a')-2 WHILE CHARINDEX(@split,@s)>0 BEGIN SET @sort+=1 INSERT @re VALUES(LEFT(@s,CHARINDEX(@split,@s)-1),@sort) SET @s=STUFF(@s,1,CHARINDEX(@split,@s)+@splitlen,'') END INSERT @re VALUES(@s,@sort+1) RETURN END
未完待續
是不是有種戛然而止的感覺,沒錯,后面的正在整理中,整個系列將完整實現一個對戰答題的小程序,包含服務端和小程序端的代碼實現,以及服務端wss的開發與配置。
欲知后事如何,傾聽下回分解。
本文首發公眾號:微兔碼農說,歡迎關注,分享。
有的朋友要源碼。所有的源碼在整個系列發布完成后,會打包,供大家下載。如需文章中提到的數據庫文件,請掃描下方二維碼,關注微信公眾號,回復答題數據庫,即可獲取數據庫地址。

