庖丁解牛-小程序版好友对战答题项目实践(一)

            

 

 

        新的一年已经拉开了序幕,有的忙着离职、有的忙着请求加薪,有的忙着春运,有人忙着相亲。。

        对于绝大多数人来说,新的一年就是新的开始,新的开始也就是雄心壮志立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的开发与配置。

欲知后事如何,倾听下回分解。

本文首发公众号:微兔码农说,欢迎关注,分享。

 

 

有的朋友要源码。所有的源码在整个系列发布完成后,会打包,供大家下载。如需文章中提到的数据库文件,请扫描下方二维码,关注微信公众号,回复答题数据库,即可获取数据库地址。

 

 

 

 

posted @ 2018-03-01 20:08  billsking  阅读(3344)  评论(3编辑  收藏  举报