当前位置: 首页 > news >正文

SpringBoot 模板模式实现优惠券逻辑

一、计算逻辑的类结构图

 在这张图里,顶层接口 RuleTemplate 定义了 calculate 方法,抽象模板类 AbstractRuleTemplate 将通用的模板计算逻辑在 calculate 方法中实现,同时它还定义了一个抽象方法 calculateNewPrice 作为子类的扩展点。各个具体的优惠计算类通过继承 AbstractRuleTemplate,并实现 calculateNewPrice 来编写自己的优惠计算方式。

二、代码实现

1、RuleTemplate.java

public interface RuleTemplate {// 计算优惠券ShoppingCart calculate(ShoppingCart settlement);
}

2、AbstractRuleTemplate.java

public ShoppingCart calculate(ShoppingCart order) {// 获取订单总价Long orderTotalAmount = getTotalPrice(order.getProducts());// 获取以shopId为维度的总价统计Map<Long, Long> sumAmount = getTotalPriceGroupByShop(order.getProducts());CouponTemplateInfo template = order.getCouponInfos().get(0).getTemplate();// 最低消费限制Long threshold = template.getRule().getDiscount().getThreshold();// 优惠金额或者打折比例Long quota = template.getRule().getDiscount().getQuota();// 如果优惠券未指定shopId,则shopTotalAmount=orderTotalAmount// 如果指定了shopId,则shopTotalAmount=对应门店下商品总价Long shopId = template.getShopId();Long shopTotalAmount = (shopId == null) ? orderTotalAmount : sumAmount.get(shopId);// 如果不符合优惠券使用标准, 则直接按原价走,不使用优惠券if (shopTotalAmount == null || shopTotalAmount < threshold) {log.debug("Totals of amount not meet");order.setCost(orderTotalAmount);order.setCouponInfos(Collections.emptyList());return order;}// 子类中实现calculateNewPrice计算新的价格Long newCost = calculateNewPrice(orderTotalAmount, shopTotalAmount, quota);if (newCost < minCost()) {newCost = minCost();}order.setCost(newCost);log.debug("original price={}, new price={}", orderTotalAmount, newCost);return order;
}

3、子类

MoneyOffTemplate.java

@Slf4j
@Component
public class MoneyOffTemplate extends AbstractRuleTemplate implements RuleTemplate {@Overrideprotected Long calculateNewPrice(Long totalAmount, Long shopAmount, Long quota) {// benefitAmount是扣减的价格// 如果当前门店的商品总价<quota,那么最多只能扣减shopAmount的钱数Long benefitAmount = shopAmount < quota ? shopAmount : quota;return totalAmount - benefitAmount;}    
}

4、工厂类

CouponTemplateFactory.java

@Component
@Slf4j
public class CouponTemplateFactory {@Autowiredprivate MoneyOffTemplate moneyOffTemplate;@Autowiredprivate DiscountTemplate discountTemplate;@Autowiredprivate RandomReductionTemplate randomReductionTemplate;@Autowiredprivate LonelyNightTemplate lonelyNightTemplate;@Autowiredprivate DummyTemplate dummyTemplate;@Autowiredprivate AntiPauTemplate antiPauTemplate;public RuleTemplate getTemplate(ShoppingCart order) {// 不使用优惠券if (CollectionUtils.isEmpty(order.getCouponInfos())) {// dummy模板直接返回原价,不进行优惠计算return dummyTemplate;}// 获取优惠券的类别// 目前每个订单只支持单张优惠券CouponTemplateInfo template = order.getCouponInfos().get(0).getTemplate();CouponType category = CouponType.convert(template.getType());switch (category) {// 订单满减券case MONEY_OFF:return moneyOffTemplate;// 随机立减券case RANDOM_DISCOUNT:return randomReductionTemplate;// 午夜下单优惠翻倍case LONELY_NIGHT_MONEY_OFF:return lonelyNightTemplate;// 打折券case DISCOUNT:return discountTemplate;case ANTI_PUA:return antiPauTemplate;// 未知类型的券模板default:return dummyTemplate;}}}

5、使用

CouponCalculationServiceImpl.java

    @Autowiredprivate CouponTemplateFactory couponProcessorFactory;// 优惠券结算// 这里通过Factory类决定使用哪个底层Rule,底层规则对上层透明@Overridepublic ShoppingCart calculateOrderPrice(@RequestBody ShoppingCart cart) {log.info("calculate order price: {}", JSON.toJSONString(cart));RuleTemplate ruleTemplate = couponProcessorFactory.getTemplate(cart);return ruleTemplate.calculate(cart);}

相关文章:

SpringBoot 模板模式实现优惠券逻辑

一、计算逻辑的类结构图 在这张图里&#xff0c;顶层接口 RuleTemplate 定义了 calculate 方法&#xff0c;抽象模板类 AbstractRuleTemplate 将通用的模板计算逻辑在 calculate 方法中实现&#xff0c;同时它还定义了一个抽象方法 calculateNewPrice 作为子类的扩展点。各个具…...

并查集 rank 的优化(Java 实例代码)

目录 并查集 rank 的优化 Java 实例代码 UnionFind3.java 文件代码&#xff1a; 并查集 rank 的优化 上一小节介绍了并查集基于 size 的优化&#xff0c;但是某些场景下&#xff0c;也会存在某些问题&#xff0c;如下图所示&#xff0c;操作 union(4,2)。 根据上一小节&…...

TDA4超级玩家浮出水面,行泊一体功能、成本刷到极致

2023年以来&#xff0c;智能驾驶市场进入L2普及、高阶ADAS功能&#xff08;NOA&#xff09;大规模量产的新周期&#xff0c;降本增效&#xff0c;打造极致性价比、提升用户体验等&#xff0c;成为了竞争的焦点。 其中&#xff0c;替换更具性价比的硬件平台、传感器复用、系统优…...

3分钟了解Android中稳定性测试

一、什么是Monkey Monkey在英文里的含义是猴子&#xff0c;在测试行业的学名叫“猴子测试”&#xff0c;指的是没有测试经验的人甚至是根本不懂计算机的人&#xff08;就像一只猴子&#xff09;&#xff0c;不需要知道程序的任何用户交互方面的知识&#xff0c;给他一个程序&a…...

LVS-DR+keepalived实现高可用负载群集

VRRP 通信原理&#xff1a; VRRP就是虚拟路由冗余协议&#xff0c;它的出现就是为了解决静态路由的单点故障。 VRRP是通过一种竞选的一种协议机制&#xff0c;来将路由交给某台VRRP路由。 VRRP用IP多播的方式&#xff08;多播地址224.0.0.18&#xff09;来实现高可用的通信&…...

阿里云国际版注册教程

什么是阿里云国际版&#xff1f; 阿里云国际版是阿里云专为海外客户供给的服务器及核算资源&#xff0c;涵盖了云主机、弹性裸金属服务器、容器服务、数据库及安全和监控等一系列云核算解决方案。 与其他云核算服务供给商不同&#xff0c;阿里云国际版在安全性、稳定性、性能方…...

基于百度文心大模型创作的实践与谈论

文心概念 百度文心大模型源于产业、服务于产业&#xff0c;是产业级知识增强大模型。百度通过大模型与国产深度学习框架融合发展&#xff0c;打造了自主创新的AI底座&#xff0c;大幅降低了AI开发和应用的门槛&#xff0c;满足真实场景中的应用需求&#xff0c;真正发挥大模型…...

Java基础知识题(五)

系列文章目录 Java基础知识题(一) Java基础知识题(二) Java基础知识题(三) Java基础知识题(四) Java基础知识题(五) 文章目录 系列文章目录 前言 一 Java的数据连接——JDBC 1. 简述什么是JDBC&#xff1f;重点 2. JDBC PreparedStatement比Statement有什么优势&…...

攻防世界-fileinclude

原题 解题思路 题目已经告诉了&#xff0c;flag在flag.php中&#xff0c;先查看网页源代码&#xff08;快捷键CTRLU&#xff09;。 通过抓包修改&#xff0c;可以把lan变量赋值flag。在cookie处修改。新打开的网页没有cookie&#xff0c;直接添加“Cookie: languagephp://filte…...

流媒体服务器SRS的搭建及QT下RTMP推流客户端的编写

一、前言 目前市面上有很多开源的流媒体服务器解决方案&#xff0c;常见的有SRS、EasyDarwin、ZLMediaKit和Monibuca。这几种的对比如下&#xff1a; &#xff08;本图来源&#xff1a;https://www.ngui.cc/zz/1781086.html?actiononClick&#xff09; 二、SRS的介绍 SRS&am…...

Effective C++条款11——在operator=中处理“自我赋值”(构造/析构/赋值运算)

“自我赋值”发生在对象被赋值给自己时: class Widget {}; Widget w; // ... w w; // 赋值给自己 这看起来有点愚蠢&#xff0c;但它合法&#xff0c;所以不要认定客户绝不会那么做。此外赋值动作并不总是那么可被一眼辨识出来&#xff0c;例如: a[i] a[j]; …...

可视化绘图技巧100篇基础篇(八)-气泡图(一)

目录 前言 适用场景 图例 绘图工具及代码实现 EXCEL 1、单轴气泡图...

Elasticsearch查询之Disjunction Max Query

前言 Disjunction Max Query 又称最佳 best_fields 匹配策略&#xff0c;用来优化当查询关键词出现在多个字段中&#xff0c;以单个字段的最大评分作为文档的最终评分&#xff0c;从而使得匹配结果更加合理 写入数据 如下的两条例子数据&#xff1a; docId: 1 title: java …...

Lock wait timeout exceeded; try restarting transaction的错误

文章目录 一、异常发现二、异常定位1、锁表语句确认2、实际场景排查三、解决思路1、本次解决方式2、其他场景解决思路扩展1、【治标方法】innodb_lock_wait_timeout 锁定等待时间改大2、【治标方法】事务信息查询3、【治标方法】如果杀掉线程依然不能解决,可以查找执行线程耗时…...

ShardingSphere01-docker环境安装

使用docker安装数据库是一个非常好的选择&#xff0c;后续的读写分离、数据分片等功能的数据库都是由docker创建。 一、安装准备 1、前提条件 Docker可以运行在Windows、Mac、CentOS、Ubuntu等操作系统上 Docker支持以下的CentOS版本&#xff1a; CentOS 7 (64-bit)CentOS …...

Java代码审计13之URLDNS链

文章目录 1、简介urldns链2、hashmap与url类的分析2.1、Hashmap类readObject方法的跟进2.2、URL类hashcode方法的跟进2.3、InetAddress类的getByName方法 3、整个链路的分析3.1、整理上述的思路3.2、一些疑问的测试3.3、hashmap的put方法分析3.4、反射3.5、整个代码 4、补充说明…...

区间预测 | MATLAB实现QRBiGRU双向门控循环单元分位数回归时间序列区间预测

区间预测 | MATLAB实现QRBiGRU双向门控循环单元分位数回归时间序列区间预测 目录 区间预测 | MATLAB实现QRBiGRU双向门控循环单元分位数回归时间序列区间预测效果一览基本介绍模型描述程序设计参考资料 效果一览 基本介绍 MATLAB实现QRBiGRU双向门控循环单元分位数回归时间序列…...

Python面向对象植物大战僵尸

先来一波效果图 来看看如何设计游戏架构 import sysimport pygameclass BaseSprite(pygame.sprite.Sprite):def __init__(self, name):super().__init__()self.image pygame.image.load(name)self.rect self.image.get_rect()class AnimateSprite(BaseSprite):def __init__(…...

大屏模板,增加自适应(包含websocket)

1、简单的Node服务端 const WebSocket require(ws);// 创建 WebSocket 服务器 const wss new WebSocket.Server({ port: 8888 });const getHeader (protocol) > {const protocolArr protocol.split(,)const headers {};for (let i 0; i < protocolArr.length; i …...

电商系统架构设计系列(九):如何规划和设计分库分表?

上篇文章中&#xff0c;我给你留了一个思考题&#xff1a;分库分表该如何设计&#xff1f; 今天这篇文章&#xff0c;我们来聊一下如何规划和设计分库分表&#xff0c;以及要考虑哪些问题。 引言 当要解决海量数据的问题&#xff0c;就必须要用到分布式的存储集群了&#xff…...

Python|GIF 解析与构建(5):手搓截屏和帧率控制

目录 Python&#xff5c;GIF 解析与构建&#xff08;5&#xff09;&#xff1a;手搓截屏和帧率控制 一、引言 二、技术实现&#xff1a;手搓截屏模块 2.1 核心原理 2.2 代码解析&#xff1a;ScreenshotData类 2.2.1 截图函数&#xff1a;capture_screen 三、技术实现&…...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

RocketMQ延迟消息机制

两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数&#xff0c;对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后&#xf…...

前端倒计时误差!

提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...

Docker 运行 Kafka 带 SASL 认证教程

Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明&#xff1a;server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

1688商品列表API与其他数据源的对接思路

将1688商品列表API与其他数据源对接时&#xff0c;需结合业务场景设计数据流转链路&#xff0c;重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点&#xff1a; 一、核心对接场景与目标 商品数据同步 场景&#xff1a;将1688商品信息…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#xff0c;但特别为多语…...

Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?

在大数据处理领域&#xff0c;Hive 作为 Hadoop 生态中重要的数据仓库工具&#xff0c;其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式&#xff0c;很多开发者常常陷入选择困境。本文将从底…...

在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)

考察一般的三次多项式&#xff0c;以r为参数&#xff1a; p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]&#xff1b; 此多项式的根为&#xff1a; 尽管看起来这个多项式是特殊的&#xff0c;其实一般的三次多项式都是可以通过线性变换化为这个形式…...