给自己复盘用的tjxt笔记day12第一部分

news/2024/9/19 0:58:12/ 标签: 笔记, java, spring cloud, spring, 开发语言

优惠券使用

优惠券规则定义

对优惠券的下列需求:

  • 判断一个优惠券是否可用,也就是检查订单金额是否达到优惠券使用门槛

  • 按照优惠规则计算优惠金额,能够计算才能比较并找出最优方案

  • 生成优惠券规则描述,目的是在页面直观的展示各种方案,供用户选择

因此,任何一张优惠券都应该具备上述3个功能,这样就能满足后续对优惠券的计算需求了。

我们抽象一个接口来标示优惠券规则

java">package com.tianji.promotion.strategy.discount;import com.tianji.promotion.domain.po.Coupon;/*** <p>优惠券折扣功能接口</p>*/
public interface Discount {/*** 判断当前价格是否满足优惠券使用限制* @param totalAmount 订单总价* @param coupon 优惠券信息* @return 是否可以使用优惠券*/boolean canUse(int totalAmount, Coupon coupon);/*** 计算折扣金额* @param totalAmount 总金额* @param coupon 优惠券信息* @return 折扣金额*/int calculateDiscount(int totalAmount, Coupon coupon);/*** 根据优惠券规则返回规则描述信息* @return 规则描述信息*/String getRule(Coupon coupon);
}

规则根据优惠类型(discountType)来看就分为4种,不同优惠仅仅是其它3个字段值不同而已。

所以优惠券的规则定义四种不同实现类即可,将来我们可以根据优惠类型不同选择具体的实现类来完成功能。像这种定义使用场景可以利用策略模式来定义规则。

  • DiscountStrategy:折扣策略的工厂,可以根据DiscountType枚举来获取某个折扣策略对象

java">public class DiscountStrategy {private final static EnumMap<DiscountType, Discount> strategies;static {strategies = new EnumMap<>(DiscountType.class);strategies.put(DiscountType.NO_THRESHOLD, new NoThresholdDiscount());strategies.put(DiscountType.PER_PRICE_DISCOUNT, new PerPriceDiscount());strategies.put(DiscountType.RATE_DISCOUNT, new RateDiscount());strategies.put(DiscountType.PRICE_DISCOUNT, new PriceDiscount());}public static Discount getDiscount(DiscountType type) {return strategies.get(type);}
}

优惠券智能推荐

思路分析

查询用户券

java">// 1.查询我的所有可用优惠券List<Coupon> coupons = userCouponMapper.queryMyCoupons(UserContext.getUser());if (CollUtils.isEmpty(coupons)) {return CollUtils.emptyList();}

查询的结果必须包含Coupon中的折扣相关信息,因此这条语句是coupon表和user_coupon表的联合查询,必须手写SQL语句。 

java">public interface UserCouponMapper extends BaseMapper<UserCoupon> {List<Coupon> queryMyCoupons(@Param("userId") Long userId);
}
java"><?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tianji.promotion.mapper.UserCouponMapper"><select id="queryMyCoupons" resultType="com.tianji.promotion.domain.po.Coupon">SELECT c.id, c.discount_type, c.`specific`, c.discount_value, c.threshold_amount,c.max_discount_amount, uc.id AS createrFROM user_coupon ucINNER JOIN coupon c ON uc.coupon_id = c.idWHERE uc.user_id = #{userId} AND uc.status = 1</select>
</mapper>

初步筛选

在初筛时,是基于所有课程计算总价,判断优惠券是否可用,这显然是不合适的

java">   // 2.初筛// 2.1.计算订单总价int totalAmount = orderCourses.stream().mapToInt(OrderCourseDTO::getPrice).sum();// 2.2.筛选可用券List<Coupon> availableCoupons = coupons.stream().filter(c -> DiscountStrategy.getDiscount(c.getDiscountType()).canUse(totalAmount, c)).collect(Collectors.toList());if (CollUtils.isEmpty(availableCoupons)) {return CollUtils.emptyList();}

细筛

细筛步骤有两步:

  • 首先要基于优惠券的限定范围对课程筛选,找出可用课程。如果没有可用课程,则优惠券不可用。

  • 然后对可用课程计算总价,判断是否达到优惠门槛,没有达到门槛则优惠券不可用

可以发现,细筛需要查询每一张优惠券的限定范围,找出可用课程。这就需要查询coupon_scope表,还是比较麻烦的。而且,后期计算优惠明细的时候我们还需要知道每张优惠券的可用课程,因此在细筛完成后,建议把每个优惠券及对应的可用课程缓存到一个Map中,形成映射关系,避免后期重复查找。

java">// 3.排列组合出所有方案// 3.1.细筛(找出每一个优惠券的可用的课程,判断课程总价是否达到优惠券的使用需求)Map<Coupon, List<OrderCourseDTO>> availableCouponMap = findAvailableCoupon(availableCoupons, orderCourses);if (CollUtils.isEmpty(availableCouponMap)) {return CollUtils.emptyList();}
java">private final ICouponScopeService scopeService;private Map<Coupon, List<OrderCourseDTO>> findAvailableCoupon(List<Coupon> coupons, List<OrderCourseDTO> courses) {Map<Coupon, List<OrderCourseDTO>> map = new HashMap<>(coupons.size());for (Coupon coupon : coupons) {// 1.找出优惠券的可用的课程List<OrderCourseDTO> availableCourses = courses;if (coupon.getSpecific()) {// 1.1.限定了范围,查询券的可用范围List<CouponScope> scopes = scopeService.lambdaQuery().eq(CouponScope::getCouponId, coupon.getId()).list();// 1.2.获取范围对应的分类idSet<Long> scopeIds = scopes.stream().map(CouponScope::getBizId).collect(Collectors.toSet());// 1.3.筛选课程availableCourses = courses.stream().filter(c -> scopeIds.contains(c.getCateId())).collect(Collectors.toList());}if (CollUtils.isEmpty(availableCourses)) {// 没有任何可用课程,抛弃continue;}// 2.计算课程总价int totalAmount = availableCourses.stream().mapToInt(OrderCourseDTO::getPrice).sum();// 3.判断是否可用Discount discount = DiscountStrategy.getDiscount(coupon.getDiscountType());if (discount.canUse(totalAmount, coupon)) {map.put(coupon, availableCourses);}}return map;
}

优惠方案全排列组合

我们要找出优惠金额最高的优惠券组合,就必须先找出所有的排列组合,然后分别计算出优惠金额,然后对比并找出最优解。

这里我们采用的思路是这样的:

  • 优惠券放在一个List集合中,他们的角标就是0~N的数字

  • 优惠券的全排列组合,就是找N个不重复数字的全排列组合

    • 例如2个数字:[0,1],排列就包含:[0,1]、[1,0]两种

  • 然后按照角标排列优惠券即可

找N个不重复数字的全排列组合可以使用回溯算法

需要注意的是,全排列中只包含券组合方案,但是页面渲染的时候需要展示单张券供用户选择。因此我们将单张券也作为组合添加进去。

java">   // 3.2.排列组合availableCoupons = new Ar

http://www.ppmy.cn/news/1519856.html

相关文章

CTFHub-SSRF过关攻略(持续更新中...)

第一题&#xff0c;内网访问 一&#xff0c;打开web/ssrf/内网访问 二&#xff0c;进入页面什么都没有查看一下上一步给的参数 三&#xff0c;输入http://127.0.0.1/flag.php回车显示flag 四&#xff0c;然后复制提交&#xff08;恭喜通关&#xff09; 第二题&#xff0c;伪协…

MYSQL:简述对B树和B+树的认识

MySQL的索引使用B树结构。 1、B树 在说B树之前&#xff0c;先说说B树&#xff0c;B树是一个多路平衡查找树&#xff0c;相较于普通的二叉树&#xff0c;不会发生极度不平衡的状况&#xff0c;同时也是多路的。 B树的特点是&#xff1a;他会将数据也保存在非叶子节点。而这个…

适用于 Visual Studio 的 C++ 万能头

您可以通过 star 我固定的 GitHub 存储库来支持我&#xff0c;谢谢&#xff01;以下是我的一些 GitHub 存储库&#xff0c;很有可能对您有用&#xff1a; Proxy Everything - Open Source (PE-OS) QR Generator - Open Source (QG-OS) 用于在 Visual Studio 上预编译的 C 包含…

AD7606芯片驱动-FPGA实现

简介 AD7606是一款16位ADC芯片,可实现8通道并行采集,每通道最大速度可达1M,可实现多种模式数据采集。 介绍 本次FPGA使用的是8通道串行采样模式,设计中所用到的AD7606引脚说明如下: 名称定义CONVST同步采集转换开始信号BUSYADC忙碌状态信号RD/SCLK采样/寄存器工作时钟CS片…

MAC +win10 笔记本, OBS 桌面音频不起作用 问题 总结

现象: MAC+WIN10笔记本,音频是好好的,可以听,但是OBS使用的时候,桌面音频条,保持静止,录制的视频,也没有系统声音。 问题排查 1、通常的OBS无法捕获音频解决方法 1)、 驱动问题,更新到最新驱动 2)、声音参数配置问题 3)、右侧小喇叭,音量合成器中, 4)、设…

小程序自定义组件配合插槽和组件传值

创建组件目录 首先,在项目的 components 目录下创建一个新的文件夹来存放你的组件 /components/└── my-component/├── my-component.wxml├── my-component.wxss├── my-component.js└── my-component.json自定义组件 1.my-component.wxml <view class=&…

线程间同步的方式有哪些?

Linux 系统提供了五种用于线程间同步的方式&#xff1a;互斥锁、读写锁、自旋锁、信号量、条件变量 互斥锁 主要用于保护共享数据&#xff0c;确保同一时间内只有一个线程访问数据。 互斥量本质上来说就是一把锁&#xff0c;在访问共享资源前对互斥量进行加锁&#xff0c;访…

编程学习方法——感悟分享

编程的确是一条充满挑战的道路&#xff0c;每个开发者都可能在这条路上遇到挫折。面对Bug的迷宫和复杂算法&#xff0c;以下是一些我用来克服困难的策略&#xff0c;希望能为你的编程之路提供帮助。 1. 分解问题 复杂的算法和Bug往往显得难以捉摸。将问题分解成小块&#xff…

vue3手动写一个图片懒加载的插件

关于图片懒加载&#xff0c;网上自然是有许多形形色色的成品的&#xff0c;但是现在我们自己来实现一个图片懒加载的功能 实现原理&#xff1a; 主要利用了js提供的一个新方法&#xff0c;IntersectionObserver用于监听元素是否在页面可视区域&#xff0c; 用法&#xff1a; C…

Navicat连接Mongodb成功了,但是无法显示数据库怎么办?

不知道你是否遇到过&#xff1f;Navicat连接Mongodb成功了&#xff0c;但是无法显示数据库怎么办&#xff1f; 解决办法 这个问题比较坑&#xff0c;对于第一次接触的小伙伴&#xff0c;可能会一脸懵逼&#xff0c;原因就是在Navicat中默认会不显示隐藏的项目&#xff0c;如果不…

【项目日记】高并发内存池---实现线程缓存

比起那些用大嗓门企图压制世界的人&#xff0c; 让全世界都安静下来听你小声说话的人更可畏。 --- 韩寒 《告白与告别》--- 高并发内存池项目---实现线程缓存 1 框架设计2 自由链表类和哈希规则2.1 自由链表类2.2 映射规则 3 实现线程缓存3.1 申请内存3.2 释放内存 4 多线程…

局部整体(五)利用python绘制旭日图

局部整体&#xff08;五&#xff09;利用python绘制旭日图 旭日图&#xff08; Sunburst Charts&#xff09;简介 由于其形状像太阳光由内向外辐射出来&#xff0c;所以叫SunBurst(太阳爆发)&#xff0c;中文也叫日出图。是多个层级的环图/饼图的拓展&#xff0c;可以显示多个…

FastAPI 进阶:使用 Pydantic 验证器增强 Query 参数验证

在 FastAPI 中&#xff0c;为 Query 类参数添加更复杂的验证逻辑可以通过以下几种方法实现&#xff1a; 使用 Pydantic 验证器&#xff1a; Pydantic 允许你在模型中定义自定义验证器。这些验证器可以用于 Query 参数&#xff0c;以实现复杂的验证逻辑。 from fastapi import F…

设计模式--装饰器模式

装饰器模式 装饰器模式&#xff08;Decorator Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许我们向一个现有的对象添加新的功能&#xff0c;同时又不改变其结构。就增加功能来说&#xff0c;装饰器模式相比生成子类更为灵活。这种模式创建了一个包装对象&#xf…

51单片机-LED闪烁

时间&#xff1a;2024.8.28 作者&#xff1a;Whappy 目的&#xff1a;学习51单片机 代码&#xff1a; #include <REGX52.H> #include "intrins.h"void Delay500ms() //11.0592MHz {unsigned char i, j, k;_nop_();i 4;j 129;k 119;do{do{while (--k);} …

音视频解码 AVIO内存输入模式

原因 根据下文&#xff0c;我们已经学会了如何从本地文件读取数据&#xff0c;对音视频进行解码操作得到原始数据。 ffmpeg 音视频解码-CSDN博客 现在有一个需求&#xff0c;网络读取到的数据&#xff0c;也就是内存数据如何直接进行解码操作&#xff1f; 本文就是介绍解决…

特种设备作业气瓶作业试题附答案

1.液化石油气瓶检验完毕后&#xff0c;逐只进行抽真空其主要目的是()。 A、提高气体的纯度 B、防止形成爆鸣气体 C、验证检验质量 D、提高充装速度 答案:B 2.无“()”监督检验钢印标记的气瓶严禁充装。 A、SC B、CC C、TS D、SS 答案:C 3.特种气瓶是指()。 A、盛装液化石油气…

微积分复习笔记 Calculus Volume 1 - 1.3Trigonometric Functions

1.3 Trigonometric Functions - Calculus Volume 1 | OpenStax

H264码流结构讲解

所谓的码流结构就是指&#xff1a;视频经过编码之后所得到的数据是怎样排列的&#xff0c;换句话说&#xff0c;就是编码后的码流我们该如何将一帧一帧的数据分离开来&#xff0c;哪一块数据是一帧图像&#xff0c;哪一块是另外一帧图像&#xff0c;只要了解了这个&#xff0c;…

【原型模式】

原型模式 Prototype Pattern 属于创建型模式是指原型实例指定创建对象的种类&#xff0c;并且通过拷贝这些原型创建新的对象&#xff0c;调用者不需要知道任何创建细节&#xff0c;不调用构造函数关键点&#xff1a;不通过 new 关键字&#xff0c;而是通过方法去创建对象 原型模…