发现不会签到,求问老滴滴司机 扫码签到签到怎么弄

不敢开车的老司机 - 推酷
不敢开车的老司机
不敢开车的老司机
常说车开多了胆子会越来越小,写代码也是。其实不是老司机胆子小了,而是新手无知无畏罢了。
最近一个很简单的功能,我做了2-3天,要是在我刚毕业的是时候把这个任务交给我,啪啪啪,不是我吹牛,2-3小时我就搞定了!
直接看产出的结果可能没觉得怎么样,甚至还会觉得这么做不对,但我觉得其中的思考过程还是非常有价值的,所以想在这记录下来。
这个任务的需求简单到一句话就可以描述了:做一个每日签到系统,连续签到会有额外的积分奖励。
这功能,见的太多了吧?我分分钟就把表结构和 API 设计好了!
+------------------------+------------------+------+-----+---------+-------+
| Null | Key | Default | Extra |
+------------------------+------------------+------+-----+---------+-------+
| int(10) unsigned | NO
| PRI | NULL
| check_in_time
| timestamp
| PRI | NULL
+------------------------+------------------+------+-----+---------+-------+
API 就不用说了吧?太简单了,每次签到的时候检查一下当天有没有签到过就行了。
你看吧,我就说交给刚毕业的我,2-3小时就搞定了。
但真的这么简单吗?老司机一眼就看出了其中的各种问题!
时区问题:我们的 App 是一个国际化的 App,如何处理时区问题?
高并发问题:高并发的情况下会不会出现一天签到多次的问题?如何解决?
性能问题:需求中需要知道连续签到天数,按照这样的表结构如何查询才能最高效?
问题都列在这了,开始一个个解决吧。
第一个要面临的就是时区问题。
考虑不周的情况下,很多人会直接用当地时间或者 UTC 时间来解决。
因为如果在国内做开发,可能你的系统只要处理中国标准时间就够了,完全不需要考虑时区问题。
遥想当年做系统的时候,数据库里存的全部是本地时间… But it works well!
签到的体验应该是怎么样的?
在国际化的背景下,签到的体验应该是怎么样的?
如果一个人一辈子呆在一个地方,那么他每日签到的时候就应该用他的当地时间作为节点。每天过午夜0点的时候,就可以再次签到了。
解决这点很简单啊!我们在签到的接口中,加入了
的最小颗粒度是分钟,所以我们的参数是分钟级别的。
local_now = utc_now + timezone * 60
local_today_start = local_now - local_now % (24 * 60 * 60)
local_today_start_in_utc = local_today_start - timezone * 60
上述代码会根据用户传入的时区,找到他的时区中对应的一天开始时间。
然后 SQL 语句可以是这样的:
SELECT * FROM check_in WHERE user_id = {user_id} AND check_in_time &= {local_today_start_in_utc}
这样就可以判断这个用户是不是在“今天”签到过了。
恶意重复签到和高并发下的重复签到问题
上面的方案看似完美,但是眼尖的老司机们又发现了问题!
是系统时间,用户无法篡改,但
是用户传上来的,它完全可以伪造请求或者手动修改手机时区,服务器根本不可能判断这个参数是否真实。
那么就会出现如下场景:
,他在当地时间
签到,相当于在在 UTC 时间
此时,用户强制修改自己的时区为
,在当地时间
签到,相当于在在 UTC 时间
根据上面的设计,用户是可以签到成功的,他可以利用这个方式,每天签到多次,这样也就可以获得大量的积分。系统统计连续签到天数的时候,也会出现错乱。
不仅如此,如果用户恶意快速请求接口,2次请求同时判断当天无签到,然后又同时写入了数据,也会出现重复签到问题。
少用事务,多用唯一键索引
如何解决这两个问题呢?
| UTC 10-25 | UTC 10-26 | UTC 10-27 |
---+-----------+-----------+-----------+---
| LOC 10-26 |
先画个时间轴看看,假设用户的时区是 +12,那么他比 UTC 时间早了12个小时。
此时,他的一天中可能会对应到 UTC 时间的10月25日,也可能会对应到UTC时间的10月26日。
想要避免他重复签到,最理想的就是利用数据库唯一键索引或者是主键。那这里的联合主键其实就是用户 ID 和 UTC 日期了。
UTC 日期计算方法就是:
int(utc_now/24/60/60)
,也就是说,当地日期可能会跨2个UTC日期,那么默认取前一个。
表结构也要改一下:
+------------------------+------------------+------+-----+---------+-------+
| Null | Key | Default | Extra |
+------------------------+------------------+------+-----+---------+-------+
| int(10) unsigned | NO
| PRI | NULL
| check_in_date
| timestamp
| PRI | NULL
| check_in_time
| timestamp
+------------------------+------------------+------+-----+---------+-------+
这里加了一个
check_in_date
字段,并且,把
check_in_date
做成了联合主键。
这样无论用户怎么高并发,配合
INSERT IGNORE
语句,并在每次执行的时候检查影响行数,就可以知道是否插入成功了。
插入成功后再去增加积分就可以了。
忘了时区问题?
等等,时区问题是不是漏了?
刚才说,如果一个用户瞬间到了另一个地方,时区变了一点点,理论上他是可以再度过一次0点的。
当地日期可能会跨2个 UTC 日期,那么默认取前一个。如果,发现他垮了时区,在当前时区下的“今天”没签到过,那么允许他再一次签到,写入数据库的就是跨2个 UTC 日期的后一个。
直接说太生涩,举个例子:
,他在当地时间
签到,相当于在在 UTC 时间
写入的数据是这样的:
+---------+---------------+---------------------+
| user_id | check_in_date | check_in_time
+---------+---------------+---------------------+
16:00:01 |
+---------+---------------+---------------------+
接下来,他改时区了:
用户强制修改自己的时区为
,在当地时间
签到,相当于在在 UTC 时间
根据这条 SQL 语句,查询到的数据是0条:
SELECT * FROM check_in WHERE user_id = 1 AND check_in_time &= ' 17:00:00'
也就是说他可以签到,先尝试这样的数据:
+---------+---------------+---------------------+
| user_id | check_in_date | check_in_time
+---------+---------------+---------------------+
16:00:01 |
17:00:02 |
+---------+---------------+---------------------+
很明显,主键冲突了,第二条数据是写不进去的,那么此时就尝试
check_in_date
+---------+---------------+---------------------+
| user_id | check_in_date | check_in_time
+---------+---------------+---------------------+
16:00:01 |
17:00:02 |
+---------+---------------+---------------------+
再接下来,厉害了 Word 哥,他又改了时区:
用户强制修改自己的时区为
,在当地时间
签到,相当于在在 UTC 时间
此时根据,“今天”他还是没有签到数据,但当他尝试插入
check_in_date =
check_in_date =
的时候都失败了!
至此,解决了用户换时区后多次签到的问题。
如何高效地运算连续签到天数和今天是否已经签到
当我面临这个问题的时候,各种算法,数据结构浮现在我脑中。
这种需求最先想到的就是二分查找发。
查找的步骤大概是这样的:
先搜索出某个人最近的10天的数据,大部分人不会连续签到这么久
在内存中判断他是否连续签到了,他今天有没有签到
如果这10的数据中有漏掉的天数,那么就可以直接返回他的连续签到天数和今天是否已经签到了
如果他这10天全部签到了,那么就要开始查找以前的数据了,这时不需要找到所有数据,只要 COUNT 记录行数,对比一下天数就知道是否漏掉了
先找20天前的数据,如果签到次数是20,那么继续找40天的数据,再找80天,以此类推。
直到发现,例如160天的签到数据小于160,那么说明他的连续签到天数在80-160之间。
二分查找发开始了,先判断120天的签到数据,如果是齐的,那么找120-160之前,一次类推最后会确认连续签到天数
当这段代码跑起来的时候,我不经为自己鼓起了掌!:clap::clap::clap::clap::clap:
然后,我还为此写了详细的注释:
# check_offset_upper_bound = [
# check_offset_lower_bound = ]
# query_offset = ^
# Init status
# ?|?|?|?|?|?|?|?|?|?|?|?|?|*|*|*|*|*|*|
# All check in
# ?|?|?|?|?|?|?|*|*|*|*|*|*|*|*|*|*|*|*|
# Not all check in
# x|x|?|?|?|?|?|*|*|*|*|*|*|*|*|*|*|*|*|
# All check in
# x|x|?|?|*|*|*|*|*|*|*|*|*|*|*|*|*|*|*|
# All check in
# x|x|?|*|*|*|*|*|*|*|*|*|*|*|*|*|*|*|*|
# Not all check in
# x|x|x|*|*|*|*|*|*|*|*|*|*|*|*|*|*|*|*|
感觉自己就要走向人生巅峰了!
空间换时间
正当我沾沾自喜的时候,还是感觉有点不太对劲,这段算法虽然高效,但是是否可以利用空间换时间,把这个数据存下来,再次提高效率呢?
最后,想到了最终版的高效方案。
+------------------------+------------------+------+-----+---------+-------+
| Null | Key | Default | Extra |
+------------------------+------------------+------+-----+---------+-------+
| int(10) unsigned | NO
| PRI | NULL
| check_in_date
| timestamp
| PRI | NULL
| check_in_time
| timestamp
| consecutive_check_days | int(10) unsigned | NO
+------------------------+------------------+------+-----+---------+-------+
假设数据库里有如下数据:
+---------+---------------+---------------------+-----------------------+
| user_id | check_in_date | check_in_time
| consecutive_check_days|
+---------+---------------+---------------------+-----------------------+
16:00:01 | 1
17:00:02 | 2
+---------+---------------+---------------------+-----------------------+
假设现在是日,我需要查询今天是否可签到,和之前的连续签到天数。
查询语句是:
SELECT * FROM check_in WHERE user_id = 1 AND check_in_date &= '' ORDER BY check_in_date DESC LIMIT 1;
如果一条数据都没,那么返回今天可签到,之前连续签到天数0
如果返回数据
check_in_time
是“今天”,并且
check_in_date
已经把之前提到的两个 UTC 日期坑位占满,那么今天就不可以签到了,但是之前的连续签到天数就是
相反,如果数据表示可以签到,那么这里就可以签到,签到逻辑和上面略有不同。
首先是多了
consecutive_check_days
,此时只要写入
即可。然后是根据查询到的数据,可以判断出 UTC 日期前一个坑位是否已经被占用,如果已经被占用,那么可以直接写入后一个坑位。
查询逻辑其实就是刚才插入逻辑的一部分。利用索引高效查询,而且只要一条数据,就可以知道所有信息,非常高效!
至此,一个简洁、高效、合理、无冲突的系统完成了。正是老司机的各种“怕”,造就了更安全的行车过程。
本作品由Dozer 创作,采用
进行许可。
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致求问老司机_抗压吧_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0可签7级以上的吧50个
本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:1,588,871贴子:
求问老司机
这是谁,番号
宇宙术不需要带二王吧
感觉想干的好多啊。 我...
正版,没开挂。玩的是单...
我玩舰队R才两天,今天...
京东抗压陪你赶超时尚,正品保证,京东抗压总有一款适合你!焕新包 尽在京东正品商城!
图---来自《萝莉经》萝莉萝莉有多好,身娇腰柔易推倒。肤嫩大眼个子小,淡妆素服一样好。樱桃小口吃得少,有糖有饼就不吵。乖巧服从性格好,纯洁心思容易了。主动讨喜真灵巧,无毛乾净气味好。可爱幼齿推到饱,后背骑乘难逃跑。                
你再问我?
贴吧热议榜
使用签名档&&
保存至快速回贴求问老司机
飞精灵3时应该注意些什么,怎么保养_大疆吧_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
求问老司机
飞精灵3时应该注意些什么,怎么保养收藏
大疆的售后修不起,平时应该注意些什么来提高飞机的使用寿命
不要作死逞强飞,偶尔适当装B千万别在人口密集的地方飞,安全放第一,规范操作。
各位老司机门多传授传授经验,感激不尽
护浆必装,远离人群,不飞太高,远离强干扰源
大疆精灵3a包装盒~有的有中文~大疆精灵~的字样~有的包装盒上为什么没有?
不要装逼就行
限高20米飞
注意不要炸鸡,炸鸡了注意不要砸到人或者是车
登录百度帐号推荐应用
为兴趣而生,贴吧更懂你。或The page is temporarily unavailable
nginx error!
The page you are looking for is temporarily unavailable.
Please try again later.
Website Administrator
Something has triggered an error on your
This is the default error page for
nginx that is distributed with
It is located
/usr/share/nginx/html/50x.html
You should customize this error page for your own
site or edit the error_page directive in
the nginx configuration file
/etc/nginx/nginx.conf.}

我要回帖

更多关于 滴滴司机签到二维码 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信