商城业务:购物车
人生在世如身处荆棘之中,心不动,人不妄动,不动则不伤;如心动则人妄动,伤其身痛其骨,于是体会到世间诸般痛苦。
1、购物车需求
1)、需求描述:
- 用户可以在登录状态下将商品添加到购物车【用户购物车/在线购物车】
- 放入数据库
- mongodb
- 放入 redis(采用)
登录以后,会将临时购物车的数据全部合并过来,并清空临时购物车;
- 用户可以在未登录状态下将商品添加到购物车【游客购物车/离线购物车/临时购物车】
- 放入 localstorage(客户端存储,后台不存)
- cookie - WebSQL
- 放入 redis(采用)
浏览器即使关闭,下次进入,临时购物车数据都在
- 用户可以使用购物车一起结算下单
- 给购物车添加商品
- 用户可以查询自己的购物车
- 用户可以在购物车中修改购买商品的数量。
- 用户可以在购物车中删除商品。
- 选中不选中商品
- 在购物车中展示商品优惠信息
- 提示购物车商品价格变化
2)、数据结构

因此每一个购物项信息,都是一个对象,基本字段包括:

另外,购物车中不止一条数据,因此最终会是对象的数组。即:

Redis 有 5 种不同数据结构,这里选择哪一种比较合适呢?
- 首先不同用户应该有独立的购物车,因此购物车应该以用户的作为 key 来存储,Value 是 用户的所有购物车信息。这样看来基本的`k-v`结构就可以了。
- 但是,我们对购物车中的商品进行增、删、改操作,基本都需要根据商品 id 进行判断, 为了方便后期处理,我们的购物车也应该是`k-v`结构,key 是商品 id,value 才是这个商品的 购物车信息。
综上所述,我们的购物车结构是一个双层 Map:Ma<string,map<string,string>>
- 第一层 Map,Key 是用户 id
- 第二层 Map,Key 是购物车中商品 id,值是购物项数据
3)、流程
参照京东

user-key 是随机生成的 id,不管有没有登录都会有这个 cookie 信息。
两个功能:新增商品到购物车、查询购物车。
新增商品:判断是否登录
- 是:则添加商品到后台 Redis 中,把 user 的唯一标识符作为 key。
- 否:则添加商品到后台 redis 中,使用随机生成的 user-key 作为 key。
查询购物车列表:判断是否登录
- 否:直接根据 user-key 查询 redis 中数据并展示
- 是:已登录,则需要先根据 user-key 查询 redis 是否有数据。
- 有:需要提交到后台添加到 redis,合并数据,而后查询。
- 否:直接去后台查询 redis,而后返回。
2、临时购物车
/*** 获取到我们要操作的购物车* @return*/private BoundHashOperations<String, Object, Object> getCartOps() {UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();//1.String cartKey = "";if (userInfoTo.getUserId() != null) {cartKey = CART_PREFIX + userInfoTo.getUserId();} else {cartKey = CART_PREFIX + userInfoTo.getUserKey();}BoundHashOperations<String, Object, Object> operations = redisTemplate.boundHashOps(cartKey);return operations;}
3、登录购物车
@Overridepublic CartItem addToCart(Long skuId, Integer num) {BoundHashOperations<String, Object, Object> cartOps = getCartOps();String res = (String) cartOps.get(skuId.toString());if(StringUtils.isEmpty(res)){CartItem cartItem = new CartItem();// 1.运程查询当前要添加的商品信息CompletableFuture<Void> getSkuInfoTask = CompletableFuture.runAsync(() -> {//2.商品添加到购物车(新商品)R skuInfo = productFeignService.getSkuInfo(skuId);SkuInfoVo data = skuInfo.getData("skuInfo", new TypeReference<SkuInfoVo>() {});cartItem.setCheck(true);cartItem.setCount(1);cartItem.setImage(data.getSkuDefaultImg());cartItem.setTitle(data.getSkuTitle());cartItem.setPrice(data.getPrice());cartItem.setSkuId(data.getSkuId());},executor);CompletableFuture<Void> getSkuSaleAttr = CompletableFuture.runAsync(() -> {//运程查询sku的组合信息List<String> values = productFeignService.getSkuSaleAttrValues(skuId);cartItem.setSkuAttr(values);}, executor);CompletableFuture<Void> allOf = CompletableFuture.allOf(getSkuInfoTask, getSkuSaleAttr);try {allOf.get();} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}String s = JSON.toJSONString(cartItem);cartOps.put(skuId.toString(),s);return cartItem;}else{//购物车有这个商品CartItem cartItem = JSON.parseObject(res, CartItem.class);cartItem.setCount(cartItem.getCount()+num);cartOps.put(skuId.toString(),JSON.toJSONString(cartItem));return cartItem;}}@Overridepublic CartItem getCartItem(Long skuId) {BoundHashOperations<String, Object, Object> cartOps = getCartOps();String str = (String) cartOps.get(skuId.toString());CartItem cartItem = JSON.parseObject(str, CartItem.class);return cartItem;}
@Overridepublic Cart getCart() {UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();Cart cart = new Cart();if(userInfoTo.getUserId()!=null){//登录String cartKey = CART_PREFIX+userInfoTo.getUserId();//如果临时购物车的数据还没有合并【合并购物车】String tempCartKey= CART_PREFIX + userInfoTo.getUserKey();List<CartItem> tempCartItems = getCartItems(tempCartKey);if(tempCartItems!=null){//合并for (CartItem item : tempCartItems) {addToCart(item.getSkuId(),item.getCount());}}//3.获取登录后的数据List<CartItem> cartItems = getCartItems(cartKey);cart.setItems(cartItems);//清除临时购物车数据clearCart(tempCartKey);}else {//没登陆String cartKey = CART_PREFIX+userInfoTo.getUserKey();//获取临时购物车的所有购物项List<CartItem> cartItems = getCartItems(cartKey);cart.setItems(cartItems);}return cart;}
@Overridepublic void checkItem(Long skuId, Integer check) {BoundHashOperations<String, Object, Object> cartOps = getCartOps();CartItem cartItem = getCartItem(skuId);cartItem.setCheck(check==1?true:false);String s = JSON.toJSONString(cartItem);cartOps.put(skuId.toString(),s);}@Overridepublic void changeItemCount(Long skuId, Integer num) {BoundHashOperations<String, Object, Object> cartOps = getCartOps();CartItem cartItem = getCartItem(skuId);cartItem.setCount(num);String s = JSON.toJSONString(cartItem);cartOps.put(skuId.toString(),s);}@Overridepublic void deleteItem(Long skuId) {BoundHashOperations<String, Object, Object> cartOps = getCartOps();cartOps.delete(skuId.toString());}
人生在世如身处荆棘之中,心不动,人不妄动,不动则不伤;如心动则人妄动,伤其身痛其骨,于是体会到世间诸般痛苦。
相关文章:
商城业务:购物车
人生在世如身处荆棘之中,心不动,人不妄动,不动则不伤;如心动则人妄动,伤其身痛其骨,于是体会到世间诸般痛苦。 1、购物车需求 1)、需求描述: - 用户可以在登录状态下将商品添加到购…...
计算机网络学习笔记(一)
网络是由若干接点和连接这些结点的链路组成。 多个网络通过路由器互联起来构成覆盖范围更大的互联网。 普通用户通过ISP接入因特网。 基于ISP的三层结构因特网 相隔较远的两台主机间通信可能需要经过多个ISP。 有电路交换,报文交换,分组交换三种交换方…...
【单目标优化算法】烟花优化算法(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
微服务项目【秒杀商品展示及商品秒杀】
登录方式调整 第1步:从zmall-common的pom.xml中移除spring-session-data-redis依赖 注意: 1)本次不采用spring-session方式,改用redis直接存储用户登录信息,主要是为了方便之后的jmeter压测; 2)…...
DIDL3_模型选择、复杂度、过欠拟合的相关概念
模型选择、复杂度、过欠拟合的概念模型选择训练误差和泛化误差验证数据集和测试数据集K-则交叉验证(没有足够多数据时使用)过拟合和欠拟合模型容量模型容量的影响估计模型容量控制模型容量数据复杂度处理过拟合的方法(1)ÿ…...
Android 9.0 去除锁屏界面及SystemUI无sim卡拨打紧急电话控件显示功能实现
1.1概述 在9.0的系统rom定制化开发中,关于SystemUI的定制化功能也是比较多的,在SystemUI的锁屏页面和状态栏提示无sim卡拨打紧急电话控件显示等相关提示 的功能中,在有些systemui的定制中是不需要这些功能的,所以需要从systemui中去掉这些功能提示的,这就需要从systemui中…...
AntDB-M设计之内存结构
亚信科技专注通信行业多年,AntDB数据库从诞生开始,就面对通信级的大数据量应用场景挑战,在性能、稳定性、规模化等方面获得了超过10年的通信核心业务系统验证,性能峰值达到每秒百万的通信核心交易量。AntDB-M(AntDB内存…...
互联网舆情监测公司监测哪些内容,TOOM北京舆情监测公司
互联网舆情监测公司是一种提供舆情监测、分析和管理服务的公司,其业务主要涉及互联网舆情监测、数据分析、报告撰写、危机处理等方面。这些公司通过使用各种技术和工具,帮助客户监测他们在互联网上的声誉和品牌形象,并提供相应的建议和解决方…...
一篇文章带你熟练使用Ansible中的playbook
目录 一、Playbook的功能 二、YAML 1、简介 2、特点 3、语法简介 4、YAML 列表 5、YAML的字典 三、playbook执行命令 四、 Playbook的核心组件 五、vim 设定技巧 练习 一、Playbook的功能 playbook 是由一个或多个play组成的列表 Playboot 文件使用YAML来写的 二、…...
HashedWheelTimer
序言这种算法是一种轮询算法的优化升级,能够以只有一个Timer的情况下处理大量的定时任务.Begin结合HashedWheelTimer的思想根据自然时间1分钟为例,来做大批量的定时任务触发首先定一个长度为60的数组,数组中存放的是Set集合,集合里面是任务详情.当有定时任务刚来的时候判断是否…...
OPenCV库移植到ARM开发板子上面配置过程
步骤一 1,环境准备去下载opencv官方的源码。 我这里用的是opencv-4.5.5版本的 2,还需要交叉编译工具一般,你交叉编译的工具板子厂家会提供工具,最好还是用板子厂家提供的交叉编译工具,因为我之前编译试过其他的交叉…...
Jenkins实现CI/CD
Jenkins是一个开源的持续集成和持续交付(CI/CD)解决方案,它可以自动执行构建、测试和部署等任务,从而简化了开发工作流程。本文将详细介绍如何使用Jenkins实现CI/CD。 首先,您需要安装Jenkins并启动它。您可以通过以下…...
如何给img标签里的请求添加自定义header
是这样的需求,有一个web页面,里面图片的上传和预览来自于一个独立的文件服务器,对http的请求需要进行访问权限的设置,就是在请求的header里加一个Authorization的字段。上传好说我用的Axios直接添加一个header就行了,但…...
Linux系统基本概念操作,用户和文件权限管理
常用快捷键和通配符常用快捷键按键作用Ctrld键盘输入结束或退出终端Ctrls暂停当前程序,暂停后按下任意键恢复运行Ctrlz将当前程序放到后台运行,恢复到前台为命令fgCtrla将光标移至输入行头,相当于Home键Ctrle将光标移至输入行末,相…...
数据库中的单表查询和多表查询
一、单表查询素材: 表名:worker-- 表中字段均为中文,比如 部门号 工资 职工号 参加工作 等 CREATE TABLE worker (部门号 int(11) NOT NULL,职工号 int(11) NOT NULL,工作时间 date NOT NULL,工资 float(8,2) NOT NULL,政治面貌 varchar(10) …...
全网详解MyBatis-Plus LambdaQueryWrapper的使用说明以及LambdaQueryWrapper和QueryWapper的区别
文章目录1. 文章引言2. 代码演示3. 分析LambdaQueryWrapper3.1 引入LambdaQueryWrapper的原因3.2 LambdaQueryWrapper和QueryWapper的区别4. 重要总结1. 文章引言 今天在公司写代码时,发现同事使用LambdaQueryWrapper来查询数据,而我一直习惯使用QueryW…...
暴力破解(new)
数据来源 本文仅用于信息安全的学习,请遵守相关法律法规,严禁用于非法途径。若观众因此作出任何危害网络安全的行为,后果自负,与本人无关。 01 暴力破解介绍及应用场景 》暴力破解介绍 》暴力破解字典 GitHub - k8gege/Passwor…...
Android12之apex调试
1.问题在调试libtinyalsa.so中添加log后,但是发现push so后,却没有log打印,why?2.分析以下为libtinyalsa.so的位置/system/lib64/libtinyalsa.so /system/lib/libtinyalsa.so /apex/com.android.vndk.v31/lib64/libtinyalsa.so /a…...
Python - 数字(Number)数据类型常用操作
目录数字运算类型转换数学函数数学库math、cmathmath 模块常量math 模块方法随机函数库 randomrandom 模块方法保留小数到指定位数三角函数数字运算 :用于给变量赋值type(x):查看数据所属类型isinstance(x, A_tuple):判断数据是否为预期类型…...
QT(51)-动态链接库-windows
1.qt- 调用win32 DLL 2.qt- 调用MFC DLL 0概述: 01.扩展DLL: 必须有一个DllMain()函数,且调用AfxInitExtensionModule()函数。 CRuntimeClass类-初始化函数CDynLinkLibrary。02.windows定位DLL文件: 1)…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...
大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...
【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...
