砍价业务总结

前言

 产品经理: 有个新需求,要做一个类似拼多多砍价的小程序我: 排几天工期啊?产品经理:一个星期天够了吧?我:(jack这么高傲的技术愤青) NoNoNo,3天就行了具体业务背景:第三方某银行为了推广自己的商城APP拉新用户,计划推出一个砍价小程序。APP老用户可以发起不同优惠券面值砍价任务拉新用户进行助力,新用户助力后可以获取APP固定面值优惠券一张老用户砍价成功后可以获取一张相应任务的面值优惠券。活动期间一个老用户只能发起一个砍价任务业务需要支持任务数量限制、奖品库存限制、用户领取限制、任务有效期、优惠券领取失败可以再次发起领取 ..........

需求分析

砍价金额算法

既然是一个砍价业务,必然涉及到一个场景:这个用户应该砍多少钱呢?由于砍价的总金额是确定的,所以每个用户砍多少钱可以以剩余总金额为区 间使用随机数来确定。那这个剩余总金额如何获得呢?难不成每个砍价请求寄来都要查表把砍过的金额重新计算一遍计算得出剩余金额?这种方式肯定是不行的:首先是性能较差,其次最后一个用户的砍价金额等于剩余金额,如果查库计算剩余金额容易出现并发问题。所以这种方案肯定是行不通的。
jack陷入深思,喝了桌子上昨天加班买的可乐冷静片刻便开始在网上查找拼多多砍价架构的设计,翻了半天也没发现有人讲明白的。Jack开始慌张,合上电脑冲了杯咖啡,准备挑灯夜战。
砍价金额逻辑无外乎需要解决并发、性能问题。仔细分析后设计出了新的逻辑:当用户发起砍价任务时,请求是可以确定砍价总金额以及完成砍价任务的总人数(笔者项目的砍价金额对应的砍价人数是支持配置的),所以当用户发起砍价任务时根据砍价总金额和砍价总人数直接生成好每个人的随机金额放入Redis中,相当于一个萝卜一个坑。当用户来砍价时直接从Redis中的队列中取随机金额即可。有效解决了并发、性能问题(对于笔者目前的项目)。
想法确定后开始实现砍价算法。感觉和拼多多的砍价比还差点意思。jack依稀记得拼多多发起砍价任务的人砍的金额都特别多,基本都是第一刀砍总金额的90%左右。“嗯,这个必须得有,好让用户有欲望去拉好友去砍价”。10分钟后jack手撸了第一版随机砍价金额算法,然后开始测试。测试很多测试用例后发现:第一个人的砍价比率太高的话,剩下金额不够分给剩下的用户,幸亏jack心细测试的比较全面,避免了生产故障。经过仔细分析撸出了最终的砍价算法:


import cn.common.exceptions.BusinessException;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;/*** <p>*     随机生成砍价金额工具类. 算法逻辑如下:*     1. 第一个人砍价支持砍价比率,其他人则根据配置的最小砍价金额与剩余总金额进行随机数生成*     (每次生成一个随机金额后,首要重新计算剩余金额,公式为 : 剩余金额 = 剩余金额 - (剩余人数 * 每人最小砍价金额),保证预留每个人最少的砍价金额).*     2.生成完所有随机数后进行总和校验*     3.除第一个人金额外,将其他随机金额随机打乱。目的是: 随着剩余金额越来越小,随机数区间也越来越小。这就导致靠后的随机金额比较均匀不够随机,所以将顺序打乱*     注意 : 当第一个人的砍价金额比率太大时,其他人的随机区间会很小,所以会导致其他人的随机金额趋于平缓,后期会再出一版算法优化*               该问题。目前该算法以经过百万人次使用。          * </p>** @author chengxiaonan (jackcheng1117@163.com)*/
@Slf4j
public class RandomBargainAmountUtil {/*** 单位 :分*/private static final int CENT_MONEY_UNIT = 100;/**** @param money 砍价任务总金额* @param count 砍价任务总人数* @param firstPersonCutRateMin 第一个砍价占总金额最小比率* @param firstPersonCutRateMax 第一个砍价占总金额最大比率* @param minCutPrice 除第一人外,其他人最小砍价金额 (单位:分)* @return*/public static List<String> random(final int money, final int count, final double firstPersonCutRateMin,final double firstPersonCutRateMax, final int minCutPrice) {log.info("开始生成随机砍价金额 money:{};count:{};firstPersonCutRateMin:{};firstPersonCutRateMax:{};minCutPrice:{}",money, count, firstPersonCutRateMin, firstPersonCutRateMax, minCutPrice);if (money <= 0 || minCutPrice <= 0 || count <= 0) {log.error("随机金额算法参数校验失败: money:{};minCutPrice:{};count:{}", money, minCutPrice, count);throw new BusinessException("", "随机金额算法参数校验失败");}//保存随机金额List<String> list = new ArrayList<>();//总金额,单位 : 元int totalMoney = money;//将单位: 元  转为 : 分int totalMoneyWithFen = money * CENT_MONEY_UNIT;try {//计算第一个人的最小、最大金额的区间int minPrice = (int) (totalMoneyWithFen * firstPersonCutRateMin);int maxPrice = (int) (totalMoneyWithFen * firstPersonCutRateMax);//校验随机数量if ((count - 1) > ((totalMoneyWithFen - maxPrice) / minCutPrice)) {log.error("随机金额算法总金额不足以生成指定随机金额数量 money:{};firstPersonCutRateMin:{};" +"firstPersonCutRateMax:{};minCutPrice:{};count:{}", money, firstPersonCutRateMin, firstPersonCutRateMax, minCutPrice, count);throw new BusinessException("", "随机金额算法总金额不足以生成指定随机金额数量");}//随机生成第一个人的随机金额int remainMoney = randomPrice(totalMoneyWithFen, minPrice, maxPrice, list);//生成其他人随机金额execute(list, remainMoney, count, minCutPrice);//校验 : 计算生成的总金额是否等于砍价总金额if (list.size() != count) {log.error("随机生成砍价金额数量与设置不符 list:{};count:{}", list, count);throw new BusinessException("", "随机生成砍价金额数量与设置不符");}BigDecimal sum = BigDecimal.ZERO;for (String item : list) {sum = sum.add(new BigDecimal(item));}if (sum.compareTo(new BigDecimal(String.valueOf(totalMoney))) != 0) {log.error("随机生成砍价金额与总价不符; list:{};money:{};count:{};minCutPrice:{}", list, money, count, minCutPrice);throw new BusinessException("", "随机生成砍价金额与总价不符");}//除第一个的随机金额外,将其他人的随机打乱顺序String firstPrice = list.remove(0);Collections.shuffle(list);list.add(0, firstPrice);return list;} catch (Exception e) {log.error("随机砍价金额生成异常 msg:{}", e.getMessage(), e);return null;}}/*** 随机生成砍价金额* @param list 用于保存生成的砍价金额* @param money 砍价总金额 (单位 : 分)* @param count 随机金额数量* @param minCutPrice 最小砍价金额*/private static void execute(List<String> list, int money, int count, int minCutPrice) {try {while (list.size() < count) {if (money <= 0) {break;}//生成随机金额money = apply(list, money, count, minCutPrice);if (list.size() == count) {break;}}} catch (Exception e) {log.error("生成随机砍价金额[RandomBargainAmountUtil#execute]异常 list:{};money:{};count:{};minCutPrice:{}",list, money, count, minCutPrice, e);return;}}private static int apply(List<String> list, int money, int count, int minCutPrice) {//最后一个人补齐砍价总金额if (list.size() == count - 1) {list.add(String.valueOf((double) money / CENT_MONEY_UNIT));return 0;}//剩余金额 = 剩余金额 - (剩余人数 * 每人最小砍价金额),保证预留每个人最少的砍价金额int remainNum = count - list.size();int minRemainPrice = minCutPrice * remainNum;//校验剩余金额是否满足剩余人数可以获得最小砍价金额if (money < minRemainPrice) {log.error("生成随机砍价金额[RandomBargainAmountUtil#execute]异常:剩余金额不满足剩余人数最小金额 " +"list:{};money:{};count:{};minCutPrice:{}", list, money, count, minCutPrice);throw new BusinessException("", "生成随机砍价金额[RandomBargainAmountUtil#execute]异常:剩余金额不满足剩余人数最小金额");}//校验剩余金额是否等于最小砍价金额,如果是则直接取最小砍价金额if (money == minRemainPrice) {money -= minCutPrice;list.add(String.valueOf((double) minCutPrice / CENT_MONEY_UNIT));}//校验剩余金额是否大于最小砍价金额,如果是则随机金额if (money > minRemainPrice) {int maxPrice = money - (minCutPrice * remainNum);money = randomPrice(money, minCutPrice, maxPrice, list);}return money;}//生成随机数private static int randomPrice(int money, int minPrice, int maxPrice, List<String> list) {int price = ThreadLocalRandom.current().nextInt(minPrice, maxPrice);money -= price;list.add(String.valueOf((double) price / CENT_MONEY_UNIT));return money;}}

目前该算法已经过百万人次使用,正确性是没有问题的。但是该算法有一个缺陷:由于支持配置第一人砍价比率,所以当第一个人比率较大时,其他人的砍价金额随机区间会越来越小。极端情况下会出现后面的人的砍价金额区域平缓都是最小金额并且都是一样的。后期有机会会优化一下该算法:使随机金额具备随机曲线

活动流程设计

设计好砍价算法之后下面就要开始设计活动流程了,通过和第三方的讨论得知他们的用户量很大,而且通过之前为第三方做过的活动的统计数据来看,活动的并发量还是蛮高的。再说了,即使用户量不大,不是还有那么多羊毛党来薅羊毛吗?这可让jack犯了愁,出去抽了支烟,打开画图软件就是一顿猛操作:

算法和设计搞好之后接下来jack就该加班撸代码了。最终效果如下:

砍价效果图

类拼多多砍价业务总结相关推荐

  1. 拼多多“砍价免费拿”始终差“0.09%”遭起诉,官方回应:活动真实

    整理 | 王晓曼 出品 | 程序人生 (ID:coder _life) 相信很多人都收到过"麻烦帮我点下可以么"."戳一戳,你最好啦"这类拼多多链接,它们可能来 ...

  2. 拼多多分享好友砍价Java实现_拼多多砍价怎么分享到朋友圈 砍价发到微信朋友圈方法...

    拼多多是一款电商+社交的共享式购物平台,现在还推出了砍价的活动,只要邀请好友砍价,你就以最低的价格购买商品,还可以可能是免费拿到,那么今天小编就给大家讲讲如何将自己的砍价信息分享到微信朋友圈. 首先下 ...

  3. 拼多多砍价背后的逻辑

    之前有一篇pdd提现的文章,[闲谈] 砍价活动可以说是拼多多一手带起来的,通过砍价活动,拼多多通过低成本进行了大量的品牌曝光,并且也累积了大量用户. 拼多多用户发起最多的砍价活动是首页的"天 ...

  4. 拼助手/帮你拼/兔查查小程序如何查询拼多多砍价记录?

    有时候我们在拼多多帮助朋友砍价后,可能会想要是否帮助别人砍价成功.结下来我给大家分享查看别人拼多多砍价记录的方法. 一.如何查看别人的拼多多砍价记录? 1.进入查砍价小程序,点击链接前往查砍价小程序 ...

  5. 拼多多砍价用户福利贴:通过python模拟操作进行拼多多砍价

    下文提到的所有工具都可以在这里下载:拼多多砍价神器Python教程所需软件 大家可以点下小站,给个支持~谢谢啦~ /1 前语/ 嘿,各位小伙伴们晚上好呀,今日小编又给我们带来干货内容啦,今日带来的是, ...

  6. 拼多多批发业务能做成吗?

    拼多多又有新动作. 据媒体报道,拼多多近日上线了一项名为"多多批发"的新业务.同时,拼多多商家页面新增"供货管理"功能,同时新增了"批发供货" ...

  7. 四川铺管家:拼多多砍价需要怎么砍

    现在许多人都喜爱在网上进行购物,而拼多多上的消费者也是比较多,许多人对于拼多多印象便是产品比较廉价,还有一个便是砍价免费拿东西,下面我们说说拼多多砍价免费拿的规矩是什么? 活动规矩如下: 1.挑选心仪 ...

  8. 手机+html+砍价,拼多多砍价免费拿手机是真的吗?可以拿到吗?

    大家最开始被拼多多吸引到的就是可以砍价,大家通过砍价之后就能得到很多好的产品,比如有的哦鞥有在问就是拼多多上可以砍价到免费的手机吗?针对这个事情我们做一个充分的分析,看看背后是什么? 拼多多砍一刀的模 ...

  9. 拼多多砍价显示服务器吃撑了,拼多多砍价网页打不开是为什么

    大家好,我是时间财富网智能客服时间君,上述问题将由我为大家进行解答. 拼多砍价网页打不开有多种原因: 1.可能是同时砍价的人过多,导致服务器瘫痪: 2.可能是浏览器缓存过多,可以清除缓存再进入砍价页面 ...

最新文章

  1. HTML导航页面结构
  2. 计算机专业技能知识,2017年度计算机专业技能知识资料基础知识资料试题'及其答案...
  3. python3讨论交流地_讨论 - 廖雪峰的官方网站
  4. eclipse 快捷键及插件
  5. pytest+allure之测试报告本地运行
  6. 和bmc_热固性BMC的注塑成型介绍
  7. 9.23 最后45天
  8. matlab绘图举例,MATLAB绘图教程详解
  9. 将sql server 2000的备份文件导入到sql server 2012中
  10. QTreeView宽度
  11. 【PyTorch】CUDA error: device-side assert triggered
  12. 不行不行,不能再讨厌英语了,要爱上英语。。。I Love You。。。
  13. 请问php中 $_data是啥变量呢
  14. unity实战 手机屏幕适配
  15. Pinyin4j导读
  16. 搜图出处的软件_【识图】怎样查找图片的来源出处,又快又好
  17. 微分几何笔记(2):微分流形的例子
  18. 【Python入门基础】Web前端
  19. js存取数组和取数组的操作
  20. 用Python制作学生管理系统

热门文章

  1. 有哪些地图编辑平台?制作地图软件哪个最好?
  2. html css语义化
  3. 基于python的国内外研究现状怎么写_毕业论文指之国内外研究现状的写法与范文...
  4. Android开发基础规范(二)
  5. UNIX环境高级编程(1)——UNIX系统总览
  6. 什么是重放攻击与中间人攻击?
  7. Matlab常用数学函数和数学运算符
  8. 六款在线项目管理工具
  9. JavaScript系列文章:变量提升和函数提升
  10. HRNet语义分割训练及TensorRT部署