-
需求评审
》》》》》》》》》》》》》》》》》》》》》》》》》
-
详细设计:
-
背景和目标
紧跟热点事件,通过卡券、周边等激励用户参与答题,根据不同的娱乐热点,配合影视综宣发,上线不同的答题主题,将答题作为一个日常可复用的小程序通用模板
-
排期 (10人日)
1.小程序登录 端上通过code,请求webpage,webpage通过开放平台换取openid 和sessionKey,后续通过openid换取uid,进行缓存 高炜炜 0.5
2.活动的配置 ( 活动id 开始时间 结束时间 活动的名字 活动的标题 活动首图 单题的倒计时 单题的分数 每天的答题次数 每次能答几道题 成绩的结果页配置 张宁 0.5
3. 题库的导入,运营同学提供excel文件 ,我收到转换成csv文件,格式如下:
开发同学通过脚本解析csv文件,判断格式是否正确,读入缓存之后,在写入数据库 和 redis中 ,数据库格式看存储设计 ,redis用于后续题目的分配使用, 以及开发题目的修复脚本,张洪铭1天
4.首页接口 0.5天 张洪铭
返回活动题目,活动的名字,答题次数(需要判断分享+1)以及是否登录的判断
5.请求题目的接口 1.5天 张洪铭
1. 端上需要传过来activity_id,后端通过activity_id,去查找这个活动对应的题目,分别从redis中取出两种类型的题号,进行比例随机抽取分配,同时生成group_id代表本次的答题组,并把题号通过group_id保存在redis中,用于后续查询把group_id,activty_id,problem_id,is_next_problem(是否是最后一题)题目的其他信息返回给端上用于展示。group_id和problem_id可以定位下一题的题号
2.去重逻辑,端上答完题会把题号写入redis的集合中,只需要每次把答过的题号和redis中存入的总题号做diff即可 张洪铭 1.5天
6.判题的得分的接口 1.5天 张洪铭
1.得分是以时间为基础算得,为了用户的体验,端上传递时间,需要加入签名进行保证时间不被修改
2. 端上需要传过来activity_id,group_id,problem_id,消耗的时间,分值,答案,server进行比较判题,把结果进行存储,同时返回给端上本题的正确答案,和本题是否正确的字段
3.如果本题是错误的回答,进行任务的创建,同时分配任务类型和生成任务id。把这些信息一起返回给端上,用于进行复活
4.同时判断本次得分是不是目前最高的一次,最高的一次需要写入排行榜的redis中
7. 任务接口,点击复活调用相应的发文和关注的任务接口,任务成功回执状态,同时更新数据库中的任务状态和本题的分数(以及判断分数的状态是否需要写入排行榜的redis中) 高炜炜 2天
8.单次成绩结果接口 0.5天 张洪铭
9.排行榜的接口 0.5天 张洪铭
写入,修改用户分数 zadd 键 activity_id + rank_list 分数 score 用户标识 uid
查看用户分数 zscore 键 activity_id + rank_list 用户标识 uid
查看排行榜的名次 zrevrange 键 activity_id + rank_list 0 99 前一百
查看用户的排名 zrevrank 键 activity_id + rank_list 用户标识uid
10.个人奖品页:
通过pbcms上传名单,和所得奖品的文字,图片(运营提供)和 排行榜进行匹配,生成 0.5天 张洪铭
11.分享回调
端上传给后端分享是否成功,后端判断是第几次,好进行次数的判端 高炜炜 1天
存储设计Mysql
活动信息的配置
pbcms |
|
|
|
---|---|---|---|
activity_id | int | 活动id | |
activity_start | int | 开始时间 | |
activity_end | int | 结束时间 | |
activity_name | varchar(255) | 活动的名字 | |
activity_title | varchar(255) | 活动的标题 | |
activity_first_image | varchar(255) | //活动首图 | |
activity_single_countdown | int | 单题的倒计时 | |
activity_single_score | int | 单题的分数 | |
activity_day_answer_num | int | 每天的答题次数 | |
activity_single_answer_num | int | 每次能答几道题 | |
activity_result_page | varchar(255) | 成绩的结果页配置 | |
activity_ext | varchar(255) | 活动的扩展字端 |
题库: mysql
mysql
answer_problem_store |
|
|
|
---|---|---|---|
activity_id | int | 和活动id对应 | |
problem_id | 自增 int | 问题id 唯一主键 | |
problem_title | varchar(255) | 问题的题目 | |
problem_image | varchar(255) | 图片 | |
problem_option | varchar(255) | 选项 | {"范冰冰","李冰冰","朱一龙","冯绍峰"} |
problem_answer | varchar(255) | 答案 | 0 索引 |
problem_type | int | 问题类型 | 1选择2选字 |
problem_status | int | 题目的状态 | 生效,失效 |
problem_ext | varchar(255) | 扩展字端 | {} |
Table: answer_problem_store
Create Table: CREATE TABLE `answer_problem_store` (
`problem_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '问题ID',
`activity_id` int(11) NOT NULL DEFAULT '0' COMMENT '活动id',
`problem_title` varchar(255) NOT NULL DEFAULT ' ' COMMENT '问题描述',
`problem_image` varchar(255) NOT NULL DEFAULT ' ' COMMENT '问题图片',
`problem_option` varchar(255) NOT NULL DEFAULT ' ' COMMENT '问题选项',
`problem_answer` varchar(255) NOT NULL DEFAULT ' ' COMMENT '问题答案',
`problem_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '问题类型1选择2选字',
`problem_ext` varchar(512) NOT NULL DEFAULT ' ' COMMENT '问题的扩展字段',
`problem_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0生效1失效',
PRIMARY KEY (`problem_id`),
KEY `idx_activity_type` (`activity_id`,`problem_type`)
) ENGINE=InnoDB AUTO_INCREMENT=458 DEFAULT CHARSET=utf8 COMMENT='问题的存储表'
1 row in set (0.00 sec)
每组答题题目记录 mysql + redis
mysql
answer_group_record |
|
|
|
---|---|---|---|
group_id | int | 自增id | |
activity_id | int | 活动id | |
uid | int | 用户id | |
allot_problem_group | varchar(255) | 分配的问题 | {8,10,3,90,23} |
total_score | int | 总得分 | 1000 |
group_status | int | 本组状态 | 1 进行中 2完成 |
group_problemid | int | 回答到哪个问题 | |
is_share | int | 是否分享 | 1,0 |
create_time | int | 创建时间 | |
update_time | int | 更新时间 |
Table: answer_group_record
Create Table: CREATE TABLE `answer_group_record` (
`group_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '问题组id',
`activity_id` int(11) NOT NULL DEFAULT '0' COMMENT '活动id',
`uid` bigint(20) NOT NULL DEFAULT '0' COMMENT '用户id',
`allot_problem_group` varchar(255) NOT NULL DEFAULT '' COMMENT '分配的问题组',
`total_score` int(11) NOT NULL DEFAULT '0' COMMENT '总分',
`group_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '本组状态 0是开始 1 进行中 2完成',
`group_problemid` int(11) NOT NULL DEFAULT '0' COMMENT '回答到哪个问题',
`is_share` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否分享 1分享0是没有',
`create_time` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间',
`update_time` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间',
PRIMARY KEY (`group_id`),
KEY `idx_activity_uid_time` (`activity_id`,`uid`,`create_time`)
) ENGINE=InnoDB AUTO_INCREMENT=573 DEFAULT CHARSET=utf8 COMMENT='问题组表'
用户已答题记录 mysql + redis
mysql
answer_record |
|
|
|
---|---|---|---|
activity_id | int | 活动id | |
problem_id | int | 问题id | |
group_id | int | 组id | |
uid | bigint(20) | 谁回答的 | |
answer_result | varchar(255) | 填写的答案 | |
is_correct | tinyint | 回答是否正确 | |
task_type | tinyint | 1是社区发文2是批量关注', | |
is_task_success | tinyint(4) | 1是成功0是没有 | |
task_ext | varchar(512) | 任务扩展字段 | |
get_ score | int | 本次得分 | |
use_time | int | 耗时s | |
create_time | int | 创建时间 | |
update_time | int | 更新时间 |
Table: answer_record
Create Table: CREATE TABLE `answer_record` (
`answer_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '回答id',
`activity_id` int(11) NOT NULL DEFAULT '0' COMMENT '活动id',
`problem_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '问题id',
`group_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '问题组id',
`uid` bigint(20) NOT NULL DEFAULT '0' COMMENT '用户id',
`answer_result` varchar(255) NOT NULL DEFAULT '' COMMENT '回答',
`is_correct` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0是没有答题1是正确2是错误',
`task_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '1是社区发文2是批量关注',
`is_task_success` tinyint(4) NOT NULL DEFAULT '0' COMMENT '1是成功0是没有',
`task_ext` varchar(512) NOT NULL DEFAULT '0' COMMENT '任务扩展字段',
`get_score` int(11) NOT NULL DEFAULT '0' COMMENT '本次得分',
`use_time` int(11) NOT NULL DEFAULT '0' COMMENT '耗时',
`create_time` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间',
`update_time` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间',
PRIMARY KEY (`answer_id`),
KEY `idx_activity_uid` (`activity_id`,`uid`),
KEY `idx_group_problem` (`group_id`,`problem_id`)
) ENGINE=InnoDB AUTO_INCREMENT=480 DEFAULT CHARSET=utf8 COMMENT='回答问题记录'
存储设计Redis
活动所有选择问题id的存储key
数据结构 | 集合 |
|
|
---|---|---|---|
key | answer_choice_problemid_store."_".$activity_id; | ||
value | problem_id |
活动所有选字问题id的存储key
数据结构 | 集合 |
|
|
---|---|---|---|
key | answer_word_problemid_store."_".$activity_id; | ||
value | problemid |
用户已经回答问题的key
数据结构 | 集合 |
|
|
---|---|---|---|
key | answer_already_problemid_store . "_".$activity_id."_".$uid | ||
value | problemid |
用户组的信息key
数据结构 | string |
|
|
---|---|---|---|
key | answer_group_info."_".$group_id | ||
value | 数据组分配问题id信息的json串 |
题目信息的存储
数据结构 | string |
|
|
---|---|---|---|
key | answer_problem_info."_".$problem_id; | ||
value | 题目信息 |
每天已经答题的次数
数据结构 | lock |
|
|
---|---|---|---|
key | answer_perday_answertimes."_".$activity_id."_".date("Ymd")."_".$uid; | ||
value | 锁 |
排行榜记录
redis
数据结构 | 有序集合 |
|
|
---|---|---|---|
key | answer_ranklist."_".$activity_id; | ||
value | 分数,成员 |
写入,修改用户分数 zadd 键 activity_id + rank_list 分数 score 用户标识 uid
查看用户分数 zscore 键 activity_id + rank_list 用户标识 uid
查看排行榜的名次 zrevrange 键 activity_id + rank_list 0 99 前一百
查看用户的排名 zrevrank 键 activity_id + rank_list 用户标识uid
全景图
时序图
接口梳理
- 1.小程序的登录接口 高炜炜 0.5
- 2.答题入口页的接口 张洪铭 0.5
- 3.请求题目的接口 张洪铭 1.5
- 4.判题得分接口 张洪铭 1.5
- 5.社区发文任务接口 高炜炜 1
- 6.关注任务接口 高炜炜 1
- 7.单次成绩结果接口 张洪铭 0.5
- 8.排行榜接口 张洪铭 0.5
- 9.个人奖品页 张洪铭 0.5
- 10.分享成功回调接口 高炜炜 1
- 11. 题库的倒入脚本 张洪铭 0.5
- 12 pbcms的设置 张宁 1
依赖服务整理
关注:
社区发文:
小程序登录: (appkey的申请)
详细接口设计
GET公参
字段 | 值 | 备注 |
---|---|---|
activity_id | 2 | 活动id |
smartAppName | answer | 小程序标识,用于小程序相关功能通用处理 |
答题首页入口
/webpage?type=yuleactivity&action=answerindex&smartAppName=answer&activity_id=2&smartAppName=answer
参数:activity_id=2
返回结构
|
请求题目接口
/webpage?type=yuleactivity&action=answerrequestproblem&smartAppName=answer&activity_id=1&group_id=61
参数
没有group_id代表创建问题组
有group_id代表从问题组中获取下一道题
|
判题接口
接口返回
|
成绩页接口
webpage?type=yuleactivity&action=answerresultpage&activity_id=1&group_id=61
|
排行榜接口
webpage?type=yuleactivity&action=answerranklist&activity_id=1
|
奖品页接口
/webpage?type=yuleactivity&action=answergetaward&activity_id=1
|
复活任务完成接口
Request:
GET:
社区发文:
/webpage?type=yuleactivity&action=answerrevive&activity_id=2&smartAppName=answer&answer_id=123&task_type=1
关注任务:
/webpage?type=yuleactivity&action=answerrevive&activity_id=2&smartAppName=answer&answer_id=123&task_type=2&followtype=superinterest&third_id=1001,1002,10003
参数:
字段 | 值 | 备注 |
---|---|---|
answer_id | int | 答题id |
task_type | 1|2 | 1:社区发文 2:关注 |
follow_type | 关注资源type | superinterest |
third_id | 关注id | 要关注的社区id,支持批量,用英文逗号连接 |
Response:
{
"errno" : 0, //失败errno非0
"errmsg" : "succ",
"data" : {//关注任务返回data为空
//发文任务返回示例:
"url" : "http://xiongyang.baidu.com:8910/interest?id=%s&activefrom=%s&yule={\"refreshMetaId\":12345,\"toast\":{\"message\":\"文案\",\"buttonText\":\"返回\",\"type\":1,\"duration\":6}}" // 跳转url
}
}
复活任务完成后返回答案接口
Request:
GET: /webpage?type=yuleactivity&action=answerrevivedone&activity_id=2&smartAppName=answer&answer_id=123
参数:
字段 | 值 | 备注 |
---|---|---|
answer_id | int | 答题id |
Response:
{
"errno" : 0, //非法调用errno非0
"errmsg" : "succ",
"data" : {
"answer" : "正确答案"
}
}
分享接口弹窗
Request:
GET: /webpage?type=yuleactivity&action=answersharepopup&smartAppName=answer&activity_id=2&group_id=123
参数:
字段 | 值 | 备注 |
---|---|---|
group_id |
Response:
{
"errno" : 0,
"errmsg" : "succ",
"data" : {
"popup" : 0|1 //0:不展示弹窗;1:展示弹窗
}
}
分享中间h5页
Request:
GET: /webpage?type=yuleactivity&action=answersharepage&smartAppName=answer&activity_id=2
Response:
{
"errno" : 0, //非法调用errno非0
"errmsg" : "succ",
"data" : {
"activityId" : 2 //备用:如果后续不同小程序展示中间页不一样的话
}
}
注意的点
1.端上传过来的时间和回答有可能是空,判断的时候需要注意
2.server控制时间,端上的时间用于展示,小程序进入后台,重新进入需要在请求一次接口同步server的时间 is_try
3.答案判断,采取的是索引判断,不是文字比较
4.权限相关,需要判断用户不能答其他人的题,问题组id,问题id,活动id userid 需要一致
5.答题次数判断,需要加锁,但是只在生成问题组的时候才加,后续的请求问题,不需要判断锁
6.请求下一题的时候,还需要判断是否复活成功,没有成功是不能请求下一题的,一直到成功为止
7.请求相同的一道题不应该是返回错误,而应该是返回之前的答案
8.排行榜按照时间排序,分数做为整数 + (活动结束时间-答题时间做为小数)这个有问题,10.9 和10.448比较就有问题了,应该是448比9更加早开始答题的,但是取数据的时候就有问题了,解决办法是将时间进行
9.当前的答题分数算百分比,算出当前分数在排行榜中所占的排名,除以总数
10.答题小程序的名字只能修改5次
11.排行榜的计算中有关时间的计算不应该以活动结束时间来算,一旦更改活动结束时间,就会导致排行榜不是按照时间排序了,因为后面的有可能时间长比前面的大。