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

Java 篇-项目实战-天机学堂(从0到1)-day12

java 篇: 1.基础地基 2.设计原理 3.项目实战

这章功力有点不足了,先放放,改日再会会。


定义优惠券规则业务流程分析及实现优惠规则:

策略模式,导包

定义接口:

因为是给其他服务用的,那得放在 api 模块下

和下面 FallbackConfig 当中对应

优惠券方案推荐 ComplateFuture 并发运算:

添加线程池

筛选最优解:

核心代码:

package com.tianji.promotion.service.impl; import com.tianji.api.dto.promotion.CouponDiscountDTO; import com.tianji.api.dto.promotion.OrderCouponDTO; import com.tianji.api.dto.promotion.OrderCourseDTO; import com.tianji.common.utils.CollUtils; import com.tianji.common.utils.UserContext; import com.tianji.promotion.domain.po.Coupon; import com.tianji.promotion.domain.po.CouponScope; import com.tianji.promotion.enums.UserCouponStatus; import com.tianji.promotion.mapper.UserCouponMapper; import com.tianji.promotion.service.ICouponScopeService; import com.tianji.promotion.service.IDiscountService; import com.tianji.promotion.strategy.discount.Discount; import com.tianji.promotion.strategy.discount.DiscountStrategy; import com.tianji.promotion.utils.PermuteUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @Slf4j @Service @RequiredArgsConstructor public class DiscountServiceImpl implements IDiscountService { private final UserCouponMapper userCouponMapper; private final ICouponScopeService scopeService; private final Executor discountSolutionExecutor; @Override public List<CouponDiscountDTO> findDiscountSolution(List<OrderCourseDTO> orderCourses) { // 1.查询我的所有可用优惠券 List<Coupon> coupons = userCouponMapper.queryMyCoupons(UserContext._getUser_()); if (CollUtils._isEmpty_(coupons)) { return CollUtils._emptyList_(); } // 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_(); } // 3.排列组合出所有方案 // 3.1.细筛(找出每一个优惠券的可用的课程,判断课程总价是否达到优惠券的使用需求) Map<Coupon, List<OrderCourseDTO>> availableCouponMap = findAvailableCoupon(availableCoupons, orderCourses); if (CollUtils._isEmpty_(availableCouponMap)) { return CollUtils._emptyList_(); } // 3.2.排列组合 availableCoupons = new ArrayList<>(availableCouponMap.keySet()); List<List<Coupon>> solutions = PermuteUtil._permute_(availableCoupons); // 3.3.添加单券的方案 for (Coupon c : availableCoupons) { solutions.add(List._of_(c)); } // 4.计算方案的优惠明细 List<CouponDiscountDTO> list = Collections._synchronizedList_(new ArrayList<>(solutions.size())); // 4.1.定义闭锁 CountDownLatch latch = new CountDownLatch(solutions.size()); for (List<Coupon> solution : solutions) { // 4.2.异步计算 CompletableFuture ._supplyAsync_( () -> calculateSolutionDiscount(availableCouponMap, orderCourses, solution), discountSolutionExecutor ).thenAccept(dto -> { // 4.3.提交任务结果 list.add(dto); latch.countDown(); }); } // 4.4.等待运算结束 try { latch.await(1, TimeUnit._SECONDS_); } catch (InterruptedException e) { _log_.error("优惠方案计算被中断,{}", e.getMessage()); } // 5.筛选最优解 return findBestSolution(list); } @Override public CouponDiscountDTO queryDiscountDetailByOrder(OrderCouponDTO orderCouponDTO) { // 1.查询用户优惠券 List<Long> userCouponIds = orderCouponDTO.getUserCouponIds(); List<Coupon> coupons = userCouponMapper.queryCouponByUserCouponIds(userCouponIds, UserCouponStatus._UNUSED_); if (CollUtils._isEmpty_(coupons)) { return null; } // 2.查询优惠券对应课程 Map<Coupon, List<OrderCourseDTO>> availableCouponMap = findAvailableCoupon(coupons, orderCouponDTO.getCourseList()); if (CollUtils._isEmpty_(availableCouponMap)) { return null; } // 3.查询优惠券规则 return calculateSolutionDiscount(availableCouponMap, orderCouponDTO.getCourseList(), coupons); } private List<CouponDiscountDTO> findBestSolution(List<CouponDiscountDTO> list) { // 1.准备Map记录最优解 Map<String, CouponDiscountDTO> moreDiscountMap = new HashMap<>(); Map<Integer, CouponDiscountDTO> lessCouponMap = new HashMap<>(); // 2.遍历,筛选最优解 for (CouponDiscountDTO solution : list) { // 2.1.计算当前方案的id组合 String ids = solution.getIds().stream() .sorted(Long::_compare_).map(String::_valueOf_).collect(Collectors._joining_(",")); // 2.2.比较用券相同时,优惠金额是否最大 CouponDiscountDTO best = moreDiscountMap.get(ids); if (best != null && best.getDiscountAmount() >= solution.getDiscountAmount()) { // 当前方案优惠金额少,跳过 continue; } // 2.3.比较金额相同时,用券数量是否最少 best = lessCouponMap.get(solution.getDiscountAmount()); int size = solution.getIds().size(); if (size > 1 && best != null && best.getIds().size() <= size) { // 当前方案用券更多,放弃 continue; } // 2.4.更新最优解 moreDiscountMap.put(ids, solution); lessCouponMap.put(solution.getDiscountAmount(), solution); } // 3.求交集 Collection<CouponDiscountDTO> bestSolutions = CollUtils ._intersection_(moreDiscountMap.values(), lessCouponMap.values()); // 4.排序,按优惠金额降序 return bestSolutions.stream() .sorted(Comparator._comparingInt_(CouponDiscountDTO::getDiscountAmount).reversed()) .collect(Collectors._toList_()); } private CouponDiscountDTO calculateSolutionDiscount( Map<Coupon, List<OrderCourseDTO>> couponMap, List<OrderCourseDTO> courses, List<Coupon> solution) { // 1.初始化DTO CouponDiscountDTO dto = new CouponDiscountDTO(); // 2.初始化折扣明细的映射 Map<Long, Integer> detailMap = courses.stream().collect(Collectors._toMap_(OrderCourseDTO::getId, oc -> 0)); dto.setDiscountDetail(detailMap); // 3.计算折扣 for (Coupon coupon : solution) { // 3.1.获取优惠券限定范围对应的课程 List<OrderCourseDTO> availableCourses = couponMap.get(coupon); // 3.2.计算课程总价(课程原价 - 折扣明细) int totalAmount = availableCourses.stream() .mapToInt(oc -> oc.getPrice() - detailMap.get(oc.getId())).sum(); // 3.3.判断是否可用 Discount discount = DiscountStrategy._getDiscount_(coupon.getDiscountType()); if (!discount.canUse(totalAmount, coupon)) { // 券不可用,跳过 continue; } // 3.4.计算优惠金额 int discountAmount = discount.calculateDiscount(totalAmount, coupon); // 3.5.计算优惠明细 calculateDiscountDetails(detailMap, availableCourses, totalAmount, discountAmount); // 3.6.更新DTO数据 dto.getIds().add(coupon.getCreater()); dto.getRules().add(discount.getRule(coupon)); dto.setDiscountAmount(discountAmount + dto.getDiscountAmount()); } return dto; } private void calculateDiscountDetails(Map<Long, Integer> detailMap, List<OrderCourseDTO> courses, int totalAmount, int discountAmount) { int times = 0; int remainDiscount = discountAmount; for (OrderCourseDTO course : courses) { // 更新课程已计算数量 times++; int discount = 0; // 判断是否是最后一个课程 if (times == courses.size()) { // 是最后一个课程,总折扣金额 - 之前所有商品的折扣金额之和 discount = remainDiscount; } else { // 计算折扣明细(课程价格在总价中占的比例,乘以总的折扣) discount = discountAmount * course.getPrice() / totalAmount; remainDiscount -= discount; } // 更新折扣明细 detailMap.put(course.getId(), discount + detailMap.get(course.getId())); } } 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; } }

至此,天机学堂篇完结,能看到这里的你,已经超厉害了!!!


如果对你有帮助的话,请点赞,关注,收藏。热爱可抵一切!👍 ❤️ 🔥

http://www.jsqmd.com/news/761797/

相关文章:

  • SBOM自动化工具minefield:像扫雷一样排查软件供应链安全漏洞
  • 【OC】多界面传值总结
  • 别让高功率激光烧坏你的镜头!一文搞懂LIDT(激光损伤阈值)怎么选
  • 如何一劳永逸解决Windows和Office激活问题?KMS智能激活终极指南
  • 开源鼠标增强工具MousePal:手势识别与按键重映射实战指南
  • 2026年甘肃亮化工程权威厂家TOP5:兰州路灯工程/兰州道路照明工程/兰州发光字/兰州商业街区泛光照明/兰州太阳能路灯/选择指南 - 优质品牌商家
  • 为什么你的PHP订单系统总在凌晨三点告警?资深架构师亲授6步根因定位法
  • MAXQ JTAG板固件更新全流程与常见问题解析
  • AI 系统上线后模型列表空白的稳定性治理:从缓存失效到分层兜底的工程实践
  • 拆解Simulink导弹模型:自动驾驶仪设计、导引头建模与Stateflow制导逻辑详解
  • 智能体编排框架agents-flex:构建复杂AI系统的柔性骨架
  • TPFanCtrl2终极指南:如何彻底掌控ThinkPad风扇,打造静音高效的散热系统
  • 嵌入式实时调度算法与分区技术解析
  • R 4.5量化回测黄金标准白皮书(2024 Q2更新):涵盖IS/OS划分规范、滚动窗口长度最优解(基于信息熵最小化)、及监管沙盒验证模板
  • 别再截图了!用Mathpix API+Python脚本,5分钟批量识别100份数学试卷公式
  • 3步解锁你的Switch:TegraRcmGUI完整免费教程
  • Yume1.5:基于文本控制的3D世界生成技术解析
  • Scikit-LLM:将大语言模型无缝集成到Scikit-learn工作流
  • 高中数学教资面试教案设计:用这个万能模板套用《函数单调性》等高频课题
  • IT资产管理系统是什么?其主要的数字化特征与智能监控功能有哪些?
  • Neovim集成MCP协议:构建AI智能体开发工作流
  • 基于Python的微信公众号监控工具:原理、部署与反爬策略实践
  • 基于Next.js与OpenAI API构建开源ChatGPT Web界面全解析
  • 长期使用中我们对Taotoken平台API稳定性的实际感受
  • Ubuntu全线宕机超24小时:亲伊朗组织DDoS与CVE-2026-31431 PoC公开的致命交汇
  • 从芯片手册到AutoSar代码:手把手教你为STM32配置片内/片外看门狗(含WdgIf抽象层详解)
  • 基于大语言模型的自然语言转SQL工具:从原理到企业级实践
  • QrScan:构建高效离线二维码批量识别系统的技术实践
  • 别再乱调路由器了!手把手教你根据家里设备选对WiFi频段(2.4G/5G/6G)和信道
  • Video-CoE框架:基于事件链建模的视频预测技术