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

news2024/9/29 13:24:26

优惠券使用

优惠券规则定义

对优惠券的下列需求:

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

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

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

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

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

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枚举来获取某个折扣策略对象

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);
    }
}

优惠券智能推荐

思路分析

查询用户券

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

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

public interface UserCouponMapper extends BaseMapper<UserCoupon> {

    List<Coupon> queryMyCoupons(@Param("userId") Long userId);
}
<?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 creater
        FROM user_coupon uc
            INNER JOIN coupon c ON uc.coupon_id = c.id
        WHERE uc.user_id = #{userId} AND uc.status = 1
    </select>
</mapper>

初步筛选

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

   // 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中,形成映射关系,避免后期重复查找。

// 3.排列组合出所有方案
        // 3.1.细筛(找出每一个优惠券的可用的课程,判断课程总价是否达到优惠券的使用需求)
        Map<Coupon, List<OrderCourseDTO>> availableCouponMap = findAvailableCoupon(availableCoupons, orderCourses);
        if (CollUtils.isEmpty(availableCouponMap)) {
            return CollUtils.emptyList();
        }
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.获取范围对应的分类id
            Set<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个不重复数字的全排列组合可以使用回溯算法

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

   // 3.2.排列组合
        availableCoupons = new Ar

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2088025.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringWeb 重定向

现在前端后分离&#xff1a;如何确认是跳转到前端页面还是后端的方法呢&#xff1f;RedirectView&#xff1a;重定向如何区分重定向的是前端页面还是后端的一个controller呢 先看下&#xff1a;SpringBoot系列教程web篇之重定向-阿里云开发者社区 ## 根据浏览器中返回的状态码…

vue3中,vue-echarts基本使用(关系图、知识图谱、柱状图、饼图、折线图)

vue3 安装vue-echarts npm i -S vue-echarts echarts//cnpm 安装 cnpm i -S vue-echarts echartsvue2 注意:Vue 2 下使用 vue-echarts,必须还要安装 @vue/composition-api : npm i -D @vue/composition-api //cnpm 安装 cnpm i -D @vue/composition-api main.js中全局注册…

【支付】PayPal支付通道注册(中国大陆、香港)

PayPal支付通道分个人版和企业版&#xff0c;在注册和功能以及收费上都有所区别&#xff0c;如果在测试阶段个人版也有sandbox账户&#xff08;包括Client ID和Secret&#xff09;&#xff0c;如果要切换到生产环境&#xff0c;会让你升级为企业版&#xff0c;只有企业版才可以…

华清远见元宇宙实验中心,开启嵌入式、物联网与人工智能教学新篇章

2024年8月23日&#xff0c;在北京举行的“匠心服务智启新程”2025新品发布会上&#xff0c;华清远见教育科技集团向行业展示了其最新的科技成果。其中最引人注目的焦点之一&#xff0c;莫过于元宇宙实验中心的发布。 正值华清远见教育科技集团20周年的里程碑时刻&#xff0c;这…

【零知识证明】构建第一个zk

1 必要步骤 视频学习&#xff1a;5. Circcom 中的基本算术电路_哔哩哔哩_bilibili 文字学习&#xff1a;https://hackmd.io/YlNLZS2ESI21OSqdTW_mPw/S1jqN-h80/edit 第五课&#xff0c;circom实践&#xff0c;需要安装 1 vscode 2 rust&#xff1a;Windows安装Rust环境&…

深入了解 ThinkSNS:开源社交网络系统的强大力量

深入了解 ThinkSNS&#xff1a;开源社交网络系统的强大力量 一、概述 在如今的互联网时代&#xff0c;社交网络已成为人们生活中不可或缺的一部分。无论是企业还是个人开发者&#xff0c;都可能需要构建一个功能强大的社交平台。而在众多的解决方案中&#xff0c;ThinkSNS 脱颖…

MySQL数据库用户管理和授权

一、数据库用户管理 1. 创建用户 要创建新用户&#xff0c;可以使用 CREATE USER 语句。语法如下&#xff1a; CREATE USER 用户名来源地址 [IDENTIFIED BY [PASSWORD] 密码];用户名&#xff1a;指定要创建的用户名。来源地址&#xff1a;指定用户可以从哪些主机登录&#x…

【科研】如何查询导师的国家自然科学基金

国家自然科学基金查询工具 下拉则可以看到国自然基金 点进去可以看到相关的成果

【鸿蒙开发从0到1 day02】

css初体验 一.css引入方式二. 标签选择器三.类选择器四. id选择器五.通配符选择器六.画盒子七.文字控制属性八.水平对齐方式-图片九.文本修饰线十.文字颜色取值十一.调试工具十二.复合选择器十三.伪类选择器十四.css的特性十五.背景图十六.背景图位置十七.背景图的缩放十八.标签…

iOS巨魔商店免越狱作弊解决方案

众所周知&#xff0c;在iOS独特的闭源生态下&#xff0c;官方唯一的应用下载渠道是App Store&#xff0c;应用下载会经过层层审核与测试来保障其安全性与稳定性&#xff0c;未经审核的应用将无法下载到手机。 这一举措限制了用户获取非官方或破解版应用的可能性&#xff0c;与…

Java:泛型

文章目录 1 基础概念1.1 泛型概念1.2 泛型好处 2 泛型2.1 泛型类2.1.1. 泛型在父子继承关系上的表现 2.2 泛型接口2.2.1 案例 2.3 泛型方法2.4 泛型的通配2.5 泛型的擦除 1 基础概念 学习目标&#xff1a; 理解泛型的概念及掌握泛型的好处 泛型类、泛型接口的定义 理解泛型在…

机器学习课程学习周报十

机器学习课程学习周报十 文章目录 机器学习课程学习周报十摘要Abstract一、机器学习部分1.1 生成对抗网络1.2 生成器与辨别器的训练过程1.3 信息论1.3.1 信息量1.3.2 熵1.3.3 交叉熵1.3.4 相对熵/KL散度1.3.5 交叉熵损失函数1.3.6 JS散度 1.4 GAN的理论介绍 总结 摘要 本周学习…

知识竞赛活动中的一些新颖特殊的赛制

以下知识竞赛海活动一些特殊新颖的竞赛规则&#xff0c;可以作为特殊情况下的参考。 &#xff08;一&#xff09;争分夺秒 答题选手&#xff1a;各队1号选手。1和2号队、3和4号队、5和6号队、7和8号队各为一组。 答题步骤&#xff1a;1号队和2号队1号选手同时离开座位&#x…

企业如何防止内部人员泄密?(5种方法详细说明)

企业内部信息泄密问题已经成为许多企业的严重威胁。随着数字化办公的普及&#xff0c;企业信息泄密的风险越来越高。内部人员泄密问题更是防不胜防&#xff0c;因此企业必须采取有效的措施来防止内部人员泄密。以下是五种可以帮助企业防止内部人员泄密的方法&#xff1a; 1. 使…

【412】【统计近似相等数对 I】

我的思路&#xff1a; 两层循环找数组两个数 然后进行1次过滤&#xff0c;如果数字相同直接下一组 不相同的话就要进行2次过滤 方便处理&#xff0c;转移到str格式 change函数用于比较两个输入的字符串是否相同 change中使用两层循环暴力调用两位数位进行交换比较&#xf…

SpringBoot 新手入门(实操)

Spring Boot 是一个开源框架&#xff0c;旨在简化基于 Spring 的 Java 应用程序的开发。它通过提供一系列默认配置和约定大于配置的理念&#xff0c;让开发者可以更快速地创建和部署应用。以下是一个 Spring Boot 新手入门的实操指南&#xff0c;帮助你从零开始创建一个简单的 …

Gitee上传项目(从0开始)

1.默认你Git已经下载好的情况下。 下载好的两种显示&#xff1a; 1.右击桌面显示这个 2.如果没有情况1出现&#xff0c;需要自己去创建快捷方式 2.去网站创建仓库 网站参考&#xff1a;yanyongzhitest/java_web - 码云 - 开源中国 (gitee.com) 新建仓库&#xff1a; 仓库名…

科研绘图系列:R语言基因PPI互作网络图(PPI network plot )

介绍 基因的PPT互作网络图。 加载R包 导入所需要的R包,在导入前需要用户自己安装。 library("tidyverse") library("magrittr") library("here") library("janitor") library("ggpubr") library("ComplexHeatmap&…

js函数方法apply,bind,call,手写new操作符

函数方法 函数方法可以用来改变函数的this指向&#xff0c;对于内置的标准函数来说&#xff0c;改变this就相当于改变了函数的作用目标&#xff1b;比如说&#xff0c;对于一个对象的方法toString()&#xff0c;可以将它的使用目标修改成指定的参数&#xff0c; 这里原本是对o…

大语言模型数据增强与模型蒸馏解决方案

背景 在人工智能和自然语言处理领域&#xff0c;大语言模型通过训练数百亿甚至上千亿参数&#xff0c;实现了出色的文本生成、翻译、总结等任务。然而&#xff0c;这些模型的训练和推理过程需要大量的计算资源&#xff0c;使得它们的实际开发应用成本非常高&#xff1b;其次&a…