商城业务:购物车
人生在世如身处荆棘之中,心不动,人不妄动,不动则不伤;如心动则人妄动,伤其身痛其骨,于是体会到世间诸般痛苦。
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)…...
如何将微信聊天记录永久保存?WeChatMsg完整免费指南帮你实现数据自主权
如何将微信聊天记录永久保存?WeChatMsg完整免费指南帮你实现数据自主权 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_T…...
Unity半透明Shader:从阴影投射到接收的完整实战解析
1. 半透明Shader的两种实现方式 在Unity中实现半透明效果主要有两种技术路线:透明度测试(Alpha Test)和透明度混合(Alpha Blending)。这两种方式看似都能实现透明效果,但底层原理和适用场景却大相径庭。 …...
2026届学术党必备的五大AI辅助论文网站推荐榜单
Ai论文网站排名(开题报告、文献综述、降aigc率、降重综合对比) TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 智能写作辅助工具DeepSeek,于论文写作整个流程里能起到多维度的作用,…...
SK海力士:从行业寒冬到AI风口逆袭,多重风险下能否穿越下一轮行业变局?
SK海力士:从行业寒冬到AI风口的逆袭与隐忧,能否穿越下一轮行业变局?2025年至今,全球资本市场最魔幻的黑马行业,当属存储芯片。在其中,全球第二大存储巨头SK海力士,是最大受益者之一。2025年&…...
API中转站统一管理工具:基于Electron的自动化运维实践
1. 项目概述:一个桌面端API中转站管理工具如果你正在使用或管理多个AI模型的API中转服务,比如OpenAI、Claude、Anthropic、Gemini等,那么你大概率会遇到一个非常头疼的问题:管理混乱。不同的中转站有不同的后台地址、不同的账号密…...
强力揭秘:Windows热键冲突诊断神器,一键揪出“热键小偷“
强力揭秘:Windows热键冲突诊断神器,一键揪出"热键小偷" 【免费下载链接】hotkey-detective A small program for investigating stolen key combinations under Windows 7 and later. 项目地址: https://gitcode.com/gh_mirrors/ho/hotkey-d…...
独立开发者如何利用Taotoken管理多个个人项目的AI调用成本
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 独立开发者如何利用Taotoken管理多个个人项目的AI调用成本 对于独立开发者而言,同时推进多个小项目是常态。每个项目都…...
构建企业内部知识库问答机器人时的API聚合与降本思考
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 构建企业内部知识库问答机器人时的API聚合与降本思考 1. 场景与挑战 许多企业希望利用大语言模型构建一个能够理解并回答内部文档…...
Neo4j数据迁移实战:从旧graph.db到新库,用CSV批量导入重构知识图谱
Neo4j数据迁移实战:从旧graph.db到新库的CSV重构指南 当你面对一个积累了多年数据的Neo4j数据库时,直接操作graph.db文件就像在走钢丝——一个失误就可能导致数据灾难。本文将带你用CSV这座"桥梁",安全地将数据从旧库迁移到新环境。…...
STM32F103 Flash读写避坑大全:从解锁失败到数据丢失,我踩过的坑你别再踩
STM32F103 Flash读写避坑大全:从解锁失败到数据丢失,我踩过的坑你别再踩 第一次在STM32F103上操作内部Flash时,我以为按照手册步骤就能轻松完成。直到调试灯疯狂闪烁、数据神秘消失、芯片莫名锁死,才意识到这片存储区域远没有想象…...
