资讯专栏INFORMATION COLUMN

PHP实现微信红包拆分算法

Drummor / 1105人阅读

算法为自创,非微信官方算法(仅供参考);如测出有问题,请及时反馈。感谢

原文:http://flc.ren/2018/04/701.html

CHANGELOG

2018-04-07

修复最后一个红包输出未保留2位数

修复领取的红包金额低于最小红包限制

handle();
 *      print_r($res);
 *
 * @author Flc <2018-04-06 20:09:53>
 * @see http://flc.ren | http://flc.io | https://github.com/flc1125
 */
class Coupon
{
    /**
     * 红包金额
     *
     * @var float
     */
    protected $amount;

    /**
     * 红包个数
     *
     * @var int
     */
    protected $num;

    /**
     * 领取的红包最小金额
     *
     * @var float
     */
    protected $coupon_min;

    /**
     * 红包分配结果
     *
     * @var array
     */
    protected $items = [];

    /**
     * 初始化
     *
     * @param float $amount     红包金额(单位:元)最多保留2位小数
     * @param int   $num        红包个数
     * @param float $coupon_min 每个至少领取的红包金额
     */
    public function __construct($amount, $num = 1, $coupon_min = 0.01)
    {
        $this->amount = $amount;
        $this->num = $num;
        $this->coupon_min = $coupon_min;
    }

    /**
     * 处理返回
     *
     * @return array
     */
    public function handle()
    {
        // A. 验证
        if ($this->amount < $validAmount = $this->coupon_min * $this->num) {
            throw new Exception("红包总金额必须≥".$validAmount."元");
        }

        // B. 分配红包
        $this->apportion();

        return [
            "items" => $this->items,
        ];
    }

    /**
     * 分配红包
     */
    protected function apportion()
    {
        $num = $this->num;  // 剩余可分配的红包个数
        $amount = $this->amount;  //剩余可领取的红包金额

        while ($num >= 1) {
            // 剩余一个的时候,直接取剩余红包
            if ($num == 1) {
                $coupon_amount = $this->decimal_number($amount);
            } else {
                $avg_amount = $this->decimal_number($amount / $num);  // 剩余的红包的平均金额

                $coupon_amount = $this->decimal_number(
                    $this->calcCouponAmount($avg_amount, $amount, $num)
                );
            }

            $this->items[] = $coupon_amount; // 追加分配

            $amount -= $coupon_amount;
            --$num;
        }

        shuffle($this->items);  //随机打乱
    }

    /**
     * 计算分配的红包金额
     *
     * @param float $avg_amount 每次计算的平均金额
     * @param float $amount     剩余可领取金额
     * @param int   $num        剩余可领取的红包个数
     *
     * @return float
     */
    protected function calcCouponAmount($avg_amount, $amount, $num)
    {
        // 如果平均金额小于等于最低金额,则直接返回最低金额
        if ($avg_amount <= $this->coupon_min) {
            return $this->coupon_min;
        }

        // 浮动计算
        $coupon_amount = $this->decimal_number($avg_amount * (1 + $this->apportionRandRatio()));

        // 如果低于最低金额或超过可领取的最大金额,则重新获取
        if ($coupon_amount < $this->coupon_min
            || $coupon_amount > $this->calcCouponAmountMax($amount, $num)
        ) {
            return $this->calcCouponAmount($avg_amount, $amount, $num);
        }

        return $coupon_amount;
    }

    /**
     * 计算分配的红包金额-可领取的最大金额
     *
     * @param float $amount
     * @param int   $num
     */
    protected function calcCouponAmountMax($amount, $num)
    {
        return $this->coupon_min + $amount - $num * $this->coupon_min;
    }

    /**
     * 红包金额浮动比例
     */
    protected function apportionRandRatio()
    {
        // 60%机率获取剩余平均值的大幅度红包(可能正数、可能负数)
        if (rand(1, 100) <= 60) {
            return rand(-70, 70) / 100; // 上下幅度70%
        }

        return rand(-30, 30) / 100; // 其他情况,上下浮动30%;
    }

    /**
     * 格式化金额,保留2位
     *
     * @param float $amount
     *
     * @return float
     */
    protected function decimal_number($amount)
    {
        return sprintf("%01.2f", round($amount, 2));
    }
}

// 例子
$coupon = new Coupon(200, 5, 30);
$res = $coupon->handle();
print_r($res);

运行结果:http://www.dooccn.com/php7/#i...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/28544.html

相关文章

  • 自如2018新年活动系统 — 抢红包

    摘要:活动规模既然公司对自如客这么阔,那对我们员工也得够意思,所以年底我们共准备了个活动。拆分后,红包占有只需操作,响应性能已不是问题。 首发于 樊浩柏科学院 2017 年是自如快速增长的一年,自如客突破 100 万,管理资产达到 50 万间,在年底成功获得了 40 亿 A 轮融资,而这些都要感谢广大的自如客,公司为了回馈自如客,在六周年活动时就发放了 6000 万租住基金,当然年底散币活...

    fanux 评论0 收藏0
  • 自如2018新年活动系统 — 抢红包

    摘要:活动规模既然公司对自如客这么阔,那对我们员工也得够意思,所以年底我们共准备了个活动。拆分后,红包占有只需操作,响应性能已不是问题。 首发于 樊浩柏科学院 2017 年是自如快速增长的一年,自如客突破 100 万,管理资产达到 50 万间,在年底成功获得了 40 亿 A 轮融资,而这些都要感谢广大的自如客,公司为了回馈自如客,在六周年活动时就发放了 6000 万租住基金,当然年底散币活...

    learning 评论0 收藏0
  • 如何做一个小程序口令红包功能

    摘要:语音识别该功能的应用场景是用户设置了一个中文的口令红包,接收到该红包的用户需要用语音说出该口令,完全匹配的话则获取该红包的某个比例金额。表结构如下如此,便完成了语音识别功能。 在做小程序后端支持的过程中遇到不少有意思的功能,有些比较考你的思维散发及解决问题的实际能力,这里摘录一下记录下来,是为抛砖引玉、如能帮到别人,自然是最好不过了。 先放几张设计图看下大概功能: showImg(ht...

    zhiwei 评论0 收藏0
  • 如何做一个小程序口令红包功能

    摘要:语音识别该功能的应用场景是用户设置了一个中文的口令红包,接收到该红包的用户需要用语音说出该口令,完全匹配的话则获取该红包的某个比例金额。表结构如下如此,便完成了语音识别功能。 在做小程序后端支持的过程中遇到不少有意思的功能,有些比较考你的思维散发及解决问题的实际能力,这里摘录一下记录下来,是为抛砖引玉、如能帮到别人,自然是最好不过了。 先放几张设计图看下大概功能: showImg(ht...

    RaoMeng 评论0 收藏0
  • 浅析微信支付:商户平台开通现金红包、指定用户发放、红包记录查询

    摘要:本文是浅析微信支付系列文章的第十三篇,主要讲解在如何开通商户平台的红包功能和为用户发放红包,以及查询发送红包记录。操作路径登录微信支付商户平台产品中心现金红包开通。 本文是【浅析微信支付】系列文章的第十三篇,主要讲解在如何开通商户平台的红包功能和为用户发放红包,以及查询发送红包记录。 浅析微信支付系列已经更新十三篇了哟~,没有看过的朋友们可以看一下哦。 浅析微信支付:(余额提现)企业...

    fizz 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<