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

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

优惠券使用

优惠券规则定义

对优惠券的下列需求:

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

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

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

因此,任何一张优惠券都应该具备上述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.kler.cn/news/283313.html

相关文章:

  • 【原型设计工具评测】Axure、Figma、Sketch三强争霸
  • 关于stm32的硬件CRC32与U盘分区中的CRC32计算方式不同的探索;stm32的硬件CRC32的使用细节;stm32的硬件CRC32的问题;
  • gin 通过 OpenTelemetry 实现链路追踪
  • 上新!Matlab实现基于QRGRU-Attention分位数回归门控循环单元注意力机制的时间序列区间预测模型
  • 数学基础 -- 线性代数之增广矩阵
  • Redis缓存穿透、缓存击穿与缓存雪崩的详细讲解和案例示范
  • 【WiFi协议的发展学习1】
  • SpringTask定时任务笔记
  • 仿BOSS招聘系统开发:构建高效、智能的在线招聘平台
  • Hadoop集群运维管理
  • OZON新品藏品,OZON收藏品推荐
  • LeetCode - 4 寻找两个正序数组的中位数
  • Pytorch 自动微分注意点讲解
  • 在 MySQL 中使用 `REPLACE` 函数
  • python实现蚁群算法
  • Google 插件推荐 50 个
  • 【数据库】两个集群数据实现同步方案
  • Python配置管理工具库之hydra使用详解
  • 机器学习—线性回归算法(Linear Regression)
  • 图结构与高级数据结构的学习笔记一
  • 语言的数据访问
  • 高性能4G灯杆网关,未来智慧城市的神经中枢
  • 【LeetCode面试150】——54螺旋矩阵
  • React Hooks 的高级用法
  • LuaJit分析(八)LuaJit预编译库函数加载过程
  • 【秋招笔试】8.21华为秋招-三语言题解
  • 算法训练营|图论第4天 110.字符串接龙 105.有向图的完全可达性 106.岛屿的周长
  • 网络原理 TCP与UDP协议
  • 本地构建spotbugs,替换gradle的默认仓库地址。
  • chapter08-面向对象编程——(Object类详解)——day09