因开发需要加入粉丝关注模块.
在好友关注关系中,主要有以上三种状态,即:
我的粉丝(fans)
我的关注(follow)
互粉(mutual)
设计思路如下:
总体思路我们采用redis里面的zset完成整个功能, 原因是zset有排序(我们要按照关注时间的倒序排列), 去重(我们不能多次关注同一用户)功能. 一个用户我们存贮两个集合, 一个是保存用户关注的人 另一个是保存关注用户的人.
具体命令参考:http://doc.redisfans.com/sorted_set/index.html
用到的命令是:
1、 zadd 添加成员:命令格式: zadd key score member [score …]
2、zrem 移除某个成员:命令格式: zrem key member [member ...]
3、 zcard 统计集合内的成员数:命令格式: zcard key
4、 zrange 查询集合内的成员:命令格式: ZRANGE key start stop [WITHSCORES]
描述:返回指定区间的成员。其中成员位置按 score 值递增(从小到大)来排序。 WITHSCORES选项是用来让成员和它的score值一并返回.
5、 zrevrange跟zrange作用相反
6、zrank获取成员的排名:命令格式: zrank key member
描述:返回有序集key中成员member的排名。成员按 score 值递增(从小到大)顺序排列。排名以0开始,也就是说score 值最小的为0。返回值:返回成员排名,member不存在返回nil.
7、 zinterstore 取两个集合的交集:命令格式:ZINTERSTORE destination numkeys key key ...] [AGGREGATE SUM|MIN|MAX]
描述:计算给定的一个或多个有序集的交集。其中给定 key 的数量必须以 numkeys 参数指定,并将该交集(结果集)储存到 destination 。默认情况下,结果集中某个成员的 score 值是所有给定集下该成员 score 值之 和 。
返回值:保存到 destination 的结果集成员数。
Redis实现思路
1. 添加关注
添加关注分为两步:
将对方Bid添加到自己的关注A列表中;
Redis::ZADD("A:follow", time(), B)
将自己的Aid添加到对方B的粉丝列表中:
Redis::ZADD("B:fans", time(), A)
2. 取消关注
取消关注同样分为两步:
将对方Bid从自己A的关注A列表中移除;
Redis::ZREM("A:follow", B)
将自己的Aid从对方的粉丝B列表中移除:
Redis::ZREM("B:fans", A)
3. 关注列表
Redis::ZRANGE("A:follow", 0 , -1)
4. 粉丝列表
Redis::ZRANGE("A:fans", 0 , -1)
5. 人物关系
5.1、 单向关注
A单向关注B,要同时满足两个条件:1、A的关注列表中有B(或B的粉丝列表中有A);2、A的粉丝列表中没有B(或B的关注列表中没有A)。
Redis::ZSCORE("A:fans", B) #未返回分数
Redis::ZSCORE("A:follow", B) #返回分数
5.2、 是否互粉
A和B是否互粉,要同时满足两个条件:1、A的关注列表中有B(或B的粉丝列表中有A);2、A的粉丝列表中有B(或B的关注列表中有A)。同时成立才为互粉。
Redis::ZSCORE("A:fans", B) #返回分数
Redis::ZSCORE("A:follow", B) #返回分数
6. 数量相关
6.1、 关注数
Redis::ZCARD("A:follow"); #返回数量
6.2、 粉丝数
Redis::ZCARD("A:fans"); #返回数量
7. 排序取出所有的人
7.1、根据关注的时间倒叙取出用户的id
Redis::ZREVRANGE("A:fans", 0, -1, TRUE); #倒序取值
7.2、根据关注时间顺序取出用户的id
Redis::ZRANGE("A:fans", 0, -1, TRUE); #顺序取值
8.获取互粉列表Mutuals
Redis::SInter("A:follow","B:follow");
或
Redis::SInterStore("A_B:follow" , "A:follow","B:follow"); //选存储
Redis::ZRANGE("A_B:follow", 0 , -1)//再获取
具体功能实现 代码分为8个部分
public class RedisFollowHelper
{
//(FOLLOW_USERS)关注表
private static string FOLLOW_USERS = "gz:{0}:follow";
//(FANS_USERS)粉丝表
private static string FANS_USERS = "gz:{0}:fans";
private static string INFO_USERS = "uinfo:{0}";
private static int INFO_USERS_TIME = 86400;
/// <summary>
/// 0.判断是否已经关注了
/// </summary>
/// <param name="cid"></param>
/// <param name="followId"></param>
/// <returns></returns>
public static bool isFollowed(long cid, long followId)
{
string followUsersKey = string.Format(FOLLOW_USERS, cid);
object value = RedisHelper.ZScore(followUsersKey, followId);
return value != null;
}
followId去关注别人,判断是否已关注
//public static bool isFollowedByFollowId(long followId, User user)
//{
// string fanUsersKey = string.Format(FANS_USERS, followId);
// Object value = RedisHelper.HGet(fanUsersKey, user.getId().ToString());
// return value != null;
//}
/// <summary>
/// 1. 添加关注
/// </summary>
/// <param name="cid"></param>
/// <param name="followId"></param>
public static void follow(long cid, long followId)
{
long dt = CFunc.GetTimeStamp(DateTime.Now);
//1、将对方id添加到自己的关注列表中;
string followUsersKey = string.Format(FOLLOW_USERS, cid);
RedisHelper.ZAdd(followUsersKey, (dt, followId));
//2、将自己的id添加到对方的粉丝列表中:
string fanUsersKey = string.Format(FANS_USERS, followId);
RedisHelper.ZAdd(fanUsersKey, (dt, cid));
}
/// <summary>
/// 2. 取消关注
/// </summary>
/// <param name="cid"></param>
/// <param name="followId"></param>
public static void unFollow(long cid, long followId)
{
//1、将对方id从自己的关注列表中移除;
string followUsersKey = string.Format(FOLLOW_USERS, cid);
RedisHelper.ZRem(followUsersKey, followId);
//将自己的id从对方的粉丝列表中移除:
string fanUsersKey = string.Format(FANS_USERS, followId);
RedisHelper.ZRem(fanUsersKey, cid);
}
/// <summary>
/// 3. 关注列表
/// </summary>
/// <param name="userId">查看者的用户id</param>
/// <param name="start"></param>
/// <param name="stop"></param>
/// <returns></returns>
public static string[] Follows(long userId, long start = 0, long stop = -1)
{
string followsUsersKey = string.Format(FOLLOW_USERS, userId);
string[] objectMap = RedisHelper.ZRange(followsUsersKey, start, stop);
return objectMap;
}
/// <summary>
/// 4. 粉丝列表
/// </summary>
/// <param name="followId"></param>
/// <param name="start"></param>
/// <param name="stop"></param>
/// <returns></returns>
public static string[] Fans(long userId, long start = 0, long stop = -1)
{
string fansUsersKey = string.Format(FANS_USERS, userId);
string[] objectMap = RedisHelper.ZRange(fansUsersKey, start, stop);
return objectMap;
}
/// <summary>
/// 5. 人物关系
/// 1.cidA单向关注cidB
/// </summary>
/// <param name="cidA"></param>
/// <param name="cidB"></param>
public static bool LinkAtoB(long cidA, long cidB)
{
string fanKey = string.Format(FANS_USERS, cidA);
string followKey = string.Format(FOLLOW_USERS, cidA);
//A单向关注B,要同时满足两个条件:1、A的关注列表中有B(或B的粉丝列表中有A);2、A的粉丝列表中没有B(或B的关注列表中没有A)。
if (RedisHelper.ZScore(followKey, cidB) > 0 && RedisHelper.ZScore(fanKey, cidB) == null)
{
return true;
}
return false;
}
/// <summary>
/// 2.是否互粉
/// </summary>
/// <param name="cidA"></param>
/// <param name="cidB"></param>
/// <returns></returns>
public static bool LinkAandB(long cidA, long cidB)
{
string fanKey = string.Format(FANS_USERS, cidA);
string followKey = string.Format(FOLLOW_USERS, cidA);
//A和B是否互粉,要同时满足两个条件:1、A的关注列表中有B(或B的粉丝列表中有A);2、A的粉丝列表中有B(或B的关注列表中有A)。同时成立才为互粉。
if (RedisHelper.ZScore(followKey, cidB) > 0 && RedisHelper.ZScore(fanKey, cidB) > 0)
{
return true;
}
return false;
}
/// <summary>
/// 6.1、 我的关注数
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
public static long FollowsCount(long userId)
{
string followsCountKey = string.Format(FOLLOW_USERS, userId);
long iCount = RedisHelper.ZCard(followsCountKey);
return iCount;
}
/// <summary>
/// 6.2、 我的粉丝数
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
public static long FansCount(long userId)
{
string fansCountKey = string.Format(FANS_USERS, userId);
long iCount = RedisHelper.ZCard(fansCountKey);
return iCount;
}
/// <summary>
/// 7.1根据粉丝添加的时间倒叙取出用户的id
/// </summary>
/// <param name="cid"></param>
/// <param name="start"></param>
/// <param name="stop"></param>
/// <returns></returns>
public static string[] FansByTimeDesc(long cid, long start = 0, long stop = -1)
{
string fansCountKey = string.Format(FANS_USERS, cid);
string[] objectMap = RedisHelper.ZRevRange(fansCountKey, start, stop);
return objectMap;
}
/// <summary>
/// 8.获取共同关注列表
/// </summary>
/// <param name="cidA"></param>
/// <param name="cidB"></param>
/// <param name="start"></param>
/// <param name="stop"></param>
/// <returns></returns>
public static string[] Mutuals(long cidA, long cidB, long start = 0, long stop = -1)
{
string followsUsersA = string.Format(FOLLOW_USERS, cidA);
string followsUsersB = string.Format(FOLLOW_USERS, cidB);
string[] objectMap = RedisHelper.SInter(followsUsersA, followsUsersB);
//也可以使用RedisHelper.SInterStore,存储起来再查询
if (stop > start)
{
long iCount = objectMap.Length;
if (start <= iCount)
{
if (stop > iCount)
{
stop = iCount;
}
int j = 0;
string[] newMap = new string[] { };
for (long i = start; i < stop; i++)
{
newMap[j] = objectMap[i];
j++;
}
return newMap;
}
}
return objectMap;
}
/// <summary>
/// 7.2根据关注的时间倒叙取出用户的id
/// </summary>
/// <param name="cid"></param>
/// <param name="start"></param>
/// <param name="stop"></param>
/// <returns></returns>
public static string[] FollowByTimeAsc(long cid, long start = 0, long stop = -1)
{
string fansCountKey = string.Format(FOLLOW_USERS, cid);
string[] objectMap = RedisHelper.ZRevRange(fansCountKey, start, stop);
return objectMap;
}
public static JObject getUserInfo(long cid, bool Refresh = false)
{
string UsersKey = string.Format(INFO_USERS, cid);
string userstr = "";
if (Refresh == false)
{//是否强制刷新数据
userstr = RedisHelper.Get(UsersKey);
}
JObject jo = new JObject();
if (string.IsNullOrEmpty(userstr))
{
//查询数据库数据
DataSet ds = ;
if (ds.Tables[0].Rows.Count > 0)
{
jo.Add("cid", cid);
jo.Add("nickname", CFunc.SafeToStr(ds.Tables[0].Rows[0]["nickname"]));
jo.Add("headpic", 1);// CFunc.ObjToInt32(ds.Tables[0].Rows[0]["regtime"]);
jo.Add("xkpic", 1);// CFunc.ObjToInt32(ds.Tables[0].Rows[0]["logindate"]);
RedisHelper.Set(UsersKey, jo, INFO_USERS_TIME);
}
ds.Clear();
}
else
{
jo = (JObject)JsonConvert.DeserializeObject(userstr);
RedisHelper.Expire(UsersKey, INFO_USERS_TIME);
}
return jo;
}
internal static bool infoChanged(long cid)
{
string UsersKey = string.Format(INFO_USERS, cid);
if (RedisHelper.Exists(UsersKey))
{
RedisHelper.Del(UsersKey);
return true;
}
return false;
}
}
|