拉黑后端实现

此处输入图片的描述

[TOC]

社区妹子强烈需要拉黑功能,结果刚上线,妹子就被拉黑了…这是个悲伤的故事…

初衷

对于种种原因不想再理会的人,可以删除私信时选择拉黑。拉黑后,拉黑者取消关注对方,删除私信,不在接收对方私信。被拉黑者收到系统消息提示被拉黑,如果是恶意、不良信息则超过一定次数被禁号,但是保留被拉黑之前所有状态,包括关注、私信,但是再次发送私信时提示私信被对方拒绝接收,再次关注提示对方拒绝被你加关注。唯一解除拉黑的手段,拉黑方再次关注对方则自动取消拉黑。

之所以保留状态,是为了尽量减少用户被拉黑的感知,但是又会收到系统提醒,相互矛盾。

策略

  • 删除私信时勾选拉黑:把对方加入自己blacklist列表中,统计数据,取消关注,删除私信会话。
  • 进入主页:返回拉黑数据,点击关注时则根据是否被拉黑进行提示。(更优方式:点击关注时后台判断是否被拉黑,拉黑则拒绝该请求,否则正常关注)
  • 被拉黑者发送私信:进入私信页面返回拉黑数据,已被拉黑则发送消息时直接提示私信被对方拒绝接收,如果中途被拉黑后台会主动判断blacklist而提示私信被对方拒绝接收。(更优方式:开始不传递拉黑数据,发送私信时后台判断提示)

上面也提到了更优办法,其实可以减少查询次数,但是最终没用,还是考虑到体验的问题,blacklist是使用kv存储的,后台查询比客户端请求快,kv费用消耗低。所以每次查询数据附带后传到前端,绝大多数情况用户点击就有提示,而不存在网络延时。

Coding Time

  • 删除私信时勾选拉黑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# kvdb设置统计user被拉黑数量
def set_blacklist_count(user):
blackcount_key = "blackcount_%s" % user.user_id
blackcount = kv.get_value(blackcount_key)
if blackcount is None:
kv.set_value(blackcount_key, 1)
else:
kv.set_value(blackcount_key, blackcount+1)
# 拉黑次数超过一定次数则发送邮件提醒
if blackcount >= 5:
print 'blacklist-email'
# 创建一个假用户
null_user = User()
null_user.nickname = user.nickname
null_user.email = user.user_id
null_user.password = blackcount
content = u"您在社区的账户信息如下:"
to_email = ['xxx@xxx.com']
subject = u"xxx"
html = render_template("login_info_email.html", user=null_user, content=content)
sc_mail(to_email, subject, html)
return 'sucesss'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 添加user到to_user的黑名单list
def add_to_blacklist(user, to_user):
# 禁止拉黑系统账号
if user.id == 1:
return 'failed'
key = 'blacklist_%s' % to_user.user_id
blacklist = kv.get_list(key)
# 名单为空
if blacklist is None:
blacklist = []
blacklist.append(user.user_id)
kv.set_list(key, blacklist)
# 追加list
else:
# 是否已经被拉黑
if str(user.user_id) in blacklist:
return 0
else:
blacklist.append(user.user_id)
kv.set_list(key, blacklist)
return 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 消息发送
@app.route('/message/<int:user_id>', methods=['GET', 'POST'])
@login_required
def message(user_id):
...
# 删除私信与拉黑处理
if request.form.get('delete'):
# 无私信对话直接返回
if session is None:
return 'error'
# 选定拉黑名单
if int(request.form.get('blacklist')) == 1:
# 添加到自己黑名单中
ret = add_to_blacklist(user, g.user)
# 取消关注
unfollow(user_id)
# 发送系统私信
sys_user = User.query.get(100)
content = u"系统消息:你已经被拉黑!恶意使用且被拉黑超过限定次数的账号将被禁用。"
sys_msg(sys_user, user, content)
content = u"系统消息:你已拉黑%s!恶意使用且被拉黑超过限定次数的账号将被禁用。" % user.nickname
sys_msg(sys_user, g.user, content)
# 拉黑数据统计
if ret:
set_blacklist_count(user)
# 删除与其私信
if g.user.id == session.from_id:
session.from_delete_time = datetime.now()
if g.user.id == session.to_id:
session.to_delete_time = datetime.now()
db.session.add(session)
db.session.commit()
return 'success'
...
# 进入私信判断是否在对方黑名单中
blacklist_key = 'blacklist_%s' % user_id
blacklist = kv.get_list(blacklist_key)
inBlacklist = 1 if (blacklist is not None and str(g.user.user_id) in blacklist) else 0
# 是否是系统账号
isSystem = 1 if user.id == 100 else 0
return render_template("msg_content.html", title=u'私信', messages=messages, user=user, inBlacklist=inBlacklist, isSystem=isSystem)
  • 被拉黑者发送私信
1
2
3
4
5
6
7
8
9
10
...
# 提交数据
if request.form.get('message'):
# 判断是否在blacklist中
key = 'blacklist_%s' % user_id
blacklist = kv.get_list(key)
print blacklist
if blacklist is not None and str(g.user.user_id) in blacklist:
return 'blacklist'
...
  • 访问主页和取消拉黑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 在对方blacklist中则拒绝关注请求
blacklist_key = 'blacklist_%s' % user_id
blacklist = kv.get_list(blacklist_key)
if str(g.user.user_id) in blacklist:
# flash(u'对方拒绝被你加好友!')
return redirect(url_for('profile', user_id=user_id, list=1))
user = User.query.filter_by(user_id=user_id).first()
if user:
if g.user.is_following(user):
return redirect(url_for('profile', user_id=user_id))
else:
# 用户是否在blacklist中
blacklist_key = 'blacklist_%s' % g.user.user_id
blacklist = kv.get_list(blacklist_key)
# 取消拉黑
if blacklist is not None and str(user_id) in blacklist:
blacklist.remove(str(user_id))
kv.set_list(blacklist_key, blacklist)
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# -*- coding: utf-8 -*-
__author__ = 'SkyWay'
import sae
import json
import sae.kvdb
class KvdbStorage():
# 初始化kvdb
def __init__(self):
self.kv = sae.kvdb.KVClient()
# 获取value
def get_value(self, key):
return self.kv.get(key)
# 获取dict_value
def get(self, key):
string_value = self.kv.get(key)
if string_value is None:
return None
return decode_dict(string_value)
# 设置value
def set_value(self, key, value):
self.kv.set(key, value)
# 设置dict_value
def set(self, key, dict_value):
string_value = encode_dict(dict_value)
self.kv.set(key, string_value)
# 设置tuple_list
def set_list(self, key, list_value):
string_value = encode_list(list_value)
self.kv.set(key, string_value)
# 获取list
def get_list(self, key):
string_value = self.kv.get(key)
if string_value is None:
return None
return decode_list(string_value)
# 批量获取key
def getkeys_by_prefix(self, prefix):
return list(self.kv.getkeys_by_prefix(prefix, limit=100, marker=None))
# 删除key
def delete(self, key):
self.kv.delete(key)
# 编码字典
def encode_dict(my_dict):
return "\x1e".join("%s\x1f%s" % x for x in my_dict.iteritems())
# 解码字典
def decode_dict(my_string):
return dict(x.split("\x1f") for x in my_string.split("\x1e"))
# 编码list
def encode_list(my_list):
return "\x1e".join(str(x) for x in my_list)
# 解码list
def decode_list(my_string):
return list(my_string.split("\x1e"))

至于编码、解码为什么用\x1e\x1f和为什么最终连接成字符串,可参考SAE在kvdb保存数据的格式比较:json、pickle、string

Shutdown

策略其实已经差不多,当然,其实还应该不允许拉黑者给被拉黑者发送私信,但是好像也没什么必要,再说吧;还有拉黑超过次数发送邮件提醒管理员,其实不太科学,拉黑并不代表有不良信息或操作,所以要二次审核。之所以没有做举报,一个是减少对用户的迷惑性,并且经常通过系统账号和用户发消息,用户也可以通过私信来进行举报。

实现方式可能还有改进的地方,以后慢慢改进,比如加入私信的等级,用户对系统账号的反馈级别较高会邮件提醒,在个人主页增加举报项等等。

Night!