一、前言
在《redis构建web应用(一)》中,我们实现了三个基本功能:
(1) 记录登录用户的token和用户名
(2) 记录每个用户最近浏览的五个商品
(3) 当登录网站的用户量超服务器限制时,使用淘汰最近最久未使用算法删除一部分用户所对应的信息
通过上篇blog的练习,读者是否对redis的数据类型应用有了更加强的印象了呢,在本文中,我们将继续完善web应用的一些基础功能。如果对Java语言不熟悉,也可以自行选择任意一种能够连接redis的语言进行完成,如果对其他语言也不熟悉,那还是跟着我一起用Java吧,文章结尾有学习讨论群和Java系列文章。
二、本文主要功能
本文的主要功能是:
(1)实现用户的购物车功能
(2)实现推荐用户购买功能
三、相关功能实现
3.1 实现用户的购物车功能
如果是真实的web项目前后端联动开发,那可能更方便理解,因为图形化界面更加符合人类对事物的认知,但是因为我们现在的学习是专注后端,在本文的例子中不掺入前端,所以实现购物车的逻辑得先捋清楚。
每位用户的购物车采用散列(HASH)的数据结构进行存储
使用过有淘宝的小朋友肯定知道,添加购物车商品的数量是使用图像界面的,从购物车删除商品也是使用图形界面的,因此小伙伴们没有看见过数量为负数的商品被添加到购物车。而在本文所构建的web应用中,如果用户订购的某件商品,并输入数量,则添加进购物车,如果此前购物车就有这件商品,将两次的订购数量相加,如果数量大于零,则覆盖此前已有的订购数量,如果数量小于等于零,则程序会在购物车散列中自动移除该商品。以下是程序的逻辑图。
具体实现代码如下:
/**
* 添加购物车
* 入参:Jedis连接,商品名,添加购物车数量
*/
public static void addShoppingCart(Jedis conn, User user, String goodsName, int count) {
/**
* 先判断购物车中是否有这个商品*/
int a = 0;//默认购物车中没有,则数量为0
if (conn.hget("cart:" + user.getToken(), goodsName) != null) {
a = Integer.valueOf(conn.hget("cart:" + user.getToken(), goodsName));
}
System.out.println("a = " + a);
/**
* 购物车中已有这个商品
* 再将两者数量相加,最终订购数量小于0,则移除*/
if ((count + a) <= 0) {
conn.hdel("cart:" + user.getToken(), goodsName);
System.out.println("购物车中商品数量为负,已被自动删除");
} else {
conn.hset("cart:" + user.getToken(), goodsName, String.valueOf(count + a));
System.out.println("添加购物车成功," + goodsName + "在购物车中的数量是" + conn.hget("cart:" + user.getToken(), goodsName));
}
}
运行程序测试一下:
也许你会觉得,好像上面的代码块没写这些中文输出的语句,为什么我运行程序会出现这些东西,实际上,这些非主要功能的代码是在main()函数里面给用户以提示的,代码的全貌可以去文末链接下载源码,仔细查看。如果所有的代码都贴到博客里面来,那用处也不大的,编程最重要的还是思维逻辑。
那么,我们来看一下我们的redis里面是否已经存储了我们的商品。
先查看kelvin的token
根据token再来查看购物车
符合我们的预期逻辑。
3.2 实现商品推荐
商品推荐可以分为个性化商品推荐和大众化商品推荐,其中个性化实现起来较为复杂,涉及一些复杂的算法,在这里就不做展示,读者可自行研究实现。那么接下来我们的主要的关注地方就在于大众推荐。
大众推荐比较类似于给商品进行积分排序,被浏览被购买的时间越接近分数就越高,浏览次数和购买次数越多,分数就越高。
因为要排序,所以我们需要一个ZSET来存储商品名和商品排序分
对于排序分值的算法,我们规定是,
a、越新排名越高,因此分值里面要加入时间戳,这里的时间戳应指商家上架的时间戳而不是用户自己购买的时间戳,那么在本次应用中,我们默认所有商品上架的时间戳都一样,所以就不加入了,但是实际项目中是不一样的哦。
b、浏览量越高排名越高,因此分值要加入浏览量X10(浏览分值常数)
c、购买数量越多排名越高,因此分值要加入购买数量X50(购买分值常数)
(因为我们的应用没有加入支付模块,所以我们视加入购物车为购买)
还有比较重要的一点,因为我们要求的是分数越高排名越靠前,但是ZSET中排序是按照分值从小到大排序的,也就是说我们想要排在前面的商品被ZSET排在了后面,故而工程师们想了一个有趣的办法,就是使用负数将所有的分值都置为负数,那么分数绝对值越高的商品就可以排的越靠前了。
那么我们就可以回到购物车的代码块中,在添加购物车的语句后面加上一个为商品加分的语句了。
conn.zincrby("goods:",-count*50,goodsName);//添加购物车加分
再回到浏览商品的地方添加浏览商品的分值
conn.zincrby("goods:",-10,goodsName);//浏览一次加10分
对代码稍作整理(对照文末源码查看)之后运行程序
细心的读者可以计算一下各个商品的分值,反正我是不想算的,毕竟我对我的程序充满信心。
好了各位,redis构建web应用到此就告一段落了,后续会继续深入实战演练的。
总结
一个完整的web应用肯定不止这一点点功能,但是在一个web中的相关功能的实现方式就是如上两篇文章所写的这样。如果大家想完善这个web应用,那么请继续认真学习吧,并且还需要较好的前端能力。处于redis的入门级别,我们做到这里已经非常不错了。
本文源码下载地址:
http://47.99.164.123:8088/root/redisLearn.git
对Java系列知识感兴趣的朋友可以加入QQ群
慧梦软件开发技术联盟:952317701
更多系列文章在java高级程序开发微信公众号