使用Redis实现反向海淘购物车合并与过期清理

简介: 本文面向开发者,介绍如何用Redis实现反向海淘购物车:按店铺分组存储(Hash结构)、支持商品合并与最低起批量校验、7天自动过期+到期前1天提醒,并已落地于Taocarts代购系统。

视角:技术开发者

一、购物车的特殊需求

反向海淘业务中,购物车不同于普通电商。用户可能同时从淘宝、1688多个店铺选商品,而且很多商品需要满足最低起批量。另外,用户可能几天后才提交代购集运,购物车数据需要持久化但不能无限增长。

我们在Taocarts系统中使用Redis来存储购物车数据,设计了按商家分组的购物车结构,并支持自动过期清理。分享实现代码。

二、数据结构设计

// 购物车商品项
@Data
public class CartItem {
   
    private Long itemId;          // 商品ID
    private String title;
    private String skuProperties; // 所选规格JSON
    private Integer quantity;
    private BigDecimal price;
    private Long shopId;          // 店铺ID
    private Integer minOrderQty;  // 最低起批量
}

// 购物车整体结构:Hash存储,key=cart:userId,field=shopId,value=该店铺下的商品列表JSON

三、核心操作

@Service
public class CartService {
   

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    private static final String CART_KEY_PREFIX = "cart:";

    // 添加商品到购物车
    public void addItem(Long userId, CartItem item) {
   
        String key = CART_KEY_PREFIX + userId;
        String shopKey = String.valueOf(item.getShopId());
        // 获取该店铺现有商品列表
        List<CartItem> shopItems = getShopItems(userId, item.getShopId());
        Optional<CartItem> existing = shopItems.stream()
            .filter(i -> i.getItemId().equals(item.getItemId()) 
                && i.getSkuProperties().equals(item.getSkuProperties()))
            .findFirst();
        if (existing.isPresent()) {
   
            existing.get().setQuantity(existing.get().getQuantity() + item.getQuantity());
        } else {
   
            shopItems.add(item);
        }
        // 序列化存储
        String json = JSON.toJSONString(shopItems);
        redisTemplate.opsForHash().put(key, shopKey, json);
        // 设置整个购物车的过期时间为7天
        redisTemplate.expire(key, Duration.ofDays(7));
    }

    // 移除商品
    public void removeItem(Long userId, Long shopId, Long itemId, String sku) {
   
        String key = CART_KEY_PREFIX + userId;
        List<CartItem> shopItems = getShopItems(userId, shopId);
        shopItems.removeIf(i -> i.getItemId().equals(itemId) && i.getSkuProperties().equals(sku));
        if (shopItems.isEmpty()) {
   
            redisTemplate.opsForHash().delete(key, String.valueOf(shopId));
        } else {
   
            redisTemplate.opsForHash().put(key, String.valueOf(shopId), JSON.toJSONString(shopItems));
        }
    }

    // 获取购物车所有商品(按店铺分组)
    public Map<Long, List<CartItem>> getCart(Long userId) {
   
        String key = CART_KEY_PREFIX + userId;
        Map<Object, Object> entries = redisTemplate.opsForHash().entries(key);
        Map<Long, List<CartItem>> result = new HashMap<>();
        for (Map.Entry<Object, Object> entry : entries.entrySet()) {
   
            Long shopId = Long.valueOf(entry.getKey().toString());
            List<CartItem> items = JSON.parseArray(entry.getValue().toString(), CartItem.class);
            result.put(shopId, items);
        }
        return result;
    }

    private List<CartItem> getShopItems(Long userId, Long shopId) {
   
        String key = CART_KEY_PREFIX + userId;
        String json = (String) redisTemplate.opsForHash().get(key, String.valueOf(shopId));
        if (json == null) {
   
            return new ArrayList<>();
        }
        return JSON.parseArray(json, CartItem.class);
    }
}

四、最低起批量校验

用户在结算时,需要校验每个店铺的商品数量是否达到最低起批量。

public void validateMinOrderQty(Long userId) {
   
    Map<Long, List<CartItem>> cart = getCart(userId);
    for (Map.Entry<Long, List<CartItem>> entry : cart.entrySet()) {
   
        for (CartItem item : entry.getValue()) {
   
            if (item.getQuantity() < item.getMinOrderQty()) {
   
                throw new BusinessException(String.format("商品【%s】最少需要购买%d件", item.getTitle(), item.getMinOrderQty()));
            }
        }
    }
}

五、过期清理与主动通知

购物车7天自动过期。但用户可能忘记,我们在过期前1天发送提醒。

@Component
public class CartExpirationListener implements KeyExpirationEventListener {
   
    @Override
    public void onKeyExpired(String key) {
   
        if (key.startsWith("cart:")) {
   
            Long userId = Long.valueOf(key.substring(5));
            // 发送站内信提醒
            notificationService.sendCartExpireWarning(userId);
        }
    }
}

注意:Redis的key过期事件默认不开启,需要在redis.conf中配置notify-keyspace-events Ex

六、与Taocarts系统的集成

上述购物车逻辑已在Taocarts系统中实现。Taocarts系统作为代购源码,购物车模块还支持“代购集运”的批量选择、运费预估等功能。做反向海淘独立站可以直接使用。

目录
相关文章
|
1月前
|
存储 安全 网络安全
什么时候该升级,什么时候再等等
升级总在“怕漏修复”和“怕踩坑”间纠结?本文提供实用决策框架:5项升级前必查项、3类该升/可缓场景、3步可靠回滚预案,助你实现可控、可逆的平滑升级。
|
1月前
|
缓存 Java API
反向海淘运费计算引擎:支持多渠道、体积重、补差逻辑的实现
面向技术开发者:Taocarts反向海淘运费引擎,支持多渠道(EMS/DHL/云途等)、体积重动态计算(可配系数)、首续重计费、自动补差与多渠道比价。含模板化配置、Caffeine缓存及完整Java实现,开箱即用,助力跨境代购独立站高效落地。(239字)
126 0
|
1月前
|
开发框架 测试技术 定位技术
Codex 实践系列 Vol.02:让 Codex 读懂开源项目 Typer
这次用 Codex 读 Typer,最重要的一点是:面对一个新项目,第一步先别急着让它写代码。比较稳妥的做法,是先让 Codex 读目录、找入口、解释核心文件,再沿着一个具体功能追下去,最后通过测试理解项目如何验证行为。
264 3
Codex 实践系列 Vol.02:让 Codex 读懂开源项目 Typer
|
NoSQL Java 关系型数据库
【AgentScope Java新手村系列】(5)记忆与会话管理
记忆与会话管理 — AgentState 管理上下文窗口,AgentStateStore 持久化,RuntimeContext.sessionId 隔离多用户会话。
398 0
|
1月前
|
存储 人工智能 算法
Claude Code自我进化系统解析:AI编程助手持久化记忆与行为学习实现方案
在日常使用Claude Code开展编程工作时,多数用户都会遇到一个普遍痛点:每开启一次全新会话,AI都会清空此前的对话内容、项目认知与个人编码习惯。此前沟通的项目架构、反复确认的代码规范、调试总结的经验教训都需要重新讲解,不仅耗费大量时间,还会降低整体开发效率。针对这一问题,业内技术团队基于Claude Code原生能力,搭建了一套完整的持久化记忆与自我进化系统,让这款AI编程助手能够跨会话留存信息、自主学习用户行为规律,逐步适配个人与团队的开发模式。本文将完整拆解这套系统的整体架构、核心模块、技术实现、运行流程以及落地效果,同时讲解设计思路与优化细节,为AI编程工具的深度定制提供参考。
264 4
|
1月前
|
缓存 人工智能 自然语言处理
阿里云百炼通义千问Qwen3.6-Flash完整实操指南:轻量化旗舰功能特性、落地优势与分层优惠订阅方案详解
当前AI应用落地场景分化愈发明显,除复杂智能体、百万字长文档、全栈大型工程开发等高门槛业务外,大量企业存在高频轻量问答、实时客服对话、短文本批量生成、简单数据提取、前端实时交互等标准化轻量化需求。这类场景单日调用频次可达数万乃至数十万次,对接口响应延迟、单轮调用成本、并发承载能力有极高要求,若选用高规格旗舰模型会造成算力预算严重浪费,而普通基础轻量化模型又存在逻辑推理弱、工具调用不稳定、短文本输出质量差等短板。
567 4
|
1月前
|
弹性计算 负载均衡 安全
阿里云负载均衡(SLB)从入门到精通:完整配置流程与最佳实践
本文详细讲解阿里云负载均衡(SLB)的完整配置流程,涵盖实例创建、服务器组配置、监听设置、健康检查、安全策略及优化实践,同时包含常见问题解答,帮助用户快速掌握SLB部署与运维核心技能。
|
1月前
|
人工智能 缓存 监控
阿里云百炼Token Plan全维度详解:核心功能、团队使用优势与AI生产力模型订阅实操指南
随着AI智能体、长文档解析、全栈代码开发、多模态图文分析等业务在企业内部常态化落地,绝大多数团队在大模型调用过程中暴露出一系列成本与管理痛点:按量付费模式账单波动剧烈,业务高峰期调用量激增导致月度预算严重超支;多员工共用模型资源时无法实现额度隔离,单人超额消耗会挤占整个团队算力;不同型号大模型单价差异大,切换模型后计费规则不统一,财务核算流程繁琐;算力高峰时段按量调用容易出现排队延迟、接口限流,影响业务系统稳定运行;团队缺乏统一的用量监控、权限分级、预算预警能力,AI资源使用处于无管控状态。
290 1
|
1月前
|
人工智能 缓存 运维
阿里云百炼通义千问Qwen3.7-Plus完整指南:全维度功能特性、落地优势与优惠订阅方案实操手册
AI应用规模化落地进程中,绝大多数企业与开发者面临性能与成本难以平衡的核心难题:轻量化模型推理、图文解析、长文档处理能力不足,无法支撑中等复杂度智能体任务;旗舰级模型长期高频调用成本偏高,中小团队难以持续投入算力预算。依托自研通义千问技术体系打造的Qwen3.7-Plus,是阿里云百炼平台推出的中端全能型多模态大模型,精准填补轻量化模型与旗舰模型之间的市场空白,在保留百万级上下文、原生图文多模态、全链路工具调用、通用代码生成全套核心能力的基础上,大幅下调调用单价,适配个人开发者、小微创业团队、中小企业全层级使用需求。
764 1
|
1月前
|
缓存 人工智能 运维
阿里云百炼Qwen3.7-Max全解:旗舰模型核心能力、技术优势与优惠订阅方案实操指南
AI智能体技术进入规模化落地阶段后,市场对大模型的长文本承载、多步骤自主推理、工具链式调用、全栈代码开发能力提出前所未有的高标准。传统轻量化对话模型仅能满足基础问答,无法支撑企业级长周期自动化任务、复杂软件工程、海量文档深度分析等高价值场景。阿里云依托自研通义千问技术体系,在百炼大模型服务平台正式推出Qwen3.7-Max旗舰大模型,作为当前千问3.7系列综合性能天花板,全面对标国际头部闭源旗舰模型,专为智能体全链路工作流深度优化,兼顾推理精度、并发稳定性、多模态理解与成本可控性,同时配套分层订阅优惠计划,覆盖个人开发者、小微团队、中大型集团企业全维度使用需求。本文将完整拆解Qwen3.7-M
477 1