积分商城:积分有过期情况下动态计算剩余积分

一个大致思路:拉出将所有积分变动数据 进行计算

<?php
namespace AppService;

use AppModelsTbaIntegralLog;
use AppModelsUser;

/**
 * 计算剩余积分:
 *
 * 1.将有 有效期的积分 按顺序排列
 * 2.将 扣积分的数据 按照时间顺序排列
 * 3.遍历扣积分的数据,依次从有效期顺序的积分中扣除
 * 4.不够扣的到下一个有效期积分中扣,扣没了就直接累计负数
 */
class CalculateBalance
{
    /**
     * $income_score = [
     *   '2022-01-31' => 150,
     *   '2022-02-26' => 150,
     *   '2022-03-31' => 150,
     * ];
     * $used_score = [
     *   '2021-10-01' => 100,
     *   '2021-10-03' => 150,
     * ];
     */
    // 收入的积分
    public $income_score;
    // 消耗的积分
    public $used_score;
    // 字段用于income_score不够扣了,将不够扣的金额累计到borrow中
    public $borrow = 0;
    // 初始化积分数据 和消费数据
    public function __construct(User $user)
    {
        $income_score = [];
        $used_score = [];
        TbaIntegralLog::select(["user_id", "expire_at", "amount"])
            ->where("user_id", $user->id)
            ->whereIn("type", [TbaIntegralLog::TYPE_HISTORY_IN, TbaIntegralLog::TYPE_MONTHLY_IN, TbaIntegralLog::TYPE_FORMAL_AWARD])
            ->orderBy("expire_at", "asc")
            ->get()
            ->each(function ($item) use (&$income_score) {
                if (empty($income_score[$item->expire_at])) {
                    $income_score[$item->expire_at] = 0;
                }
                $income_score[$item->expire_at] = bcadd($income_score[$item->expire_at], $item->amount, 2);
            });
        TbaIntegralLog::select(["user_id", "amount", "at"])
            ->where("user_id", $user->id)
            ->whereIn("type", [TbaIntegralLog::TYPE_EXPEND_OUT])
            ->orderBy("at", "asc")
            ->get()
            ->each(function ($item) use (&$used_score) {
                if (empty($used_score[$item->at])) {
                    $used_score[$item->at] = 0;
                }
                $used_score[$item->at] = bcsub($used_score[$item->at], $item->amount, 2);
            });
        $this->income_score = $income_score;
        $this->used_score = $used_score;
    }
    /**
     * 输入消费日期和消费积分
     * 按积分过期顺序实时抵消(消费时还未过期的)收入积分
     *
     * @return void
     */
    protected function hedgeFund($date, $score)
    {
        $income_arr = &$this->income_score;
        $current = $score;
        foreach ($income_arr as $expire_at => $income) {
            if ($income > 0 && $current > 0 && $expire_at > $date) {
                if ($income >= $current) {
                    $income_arr[$expire_at] = bcsub($income, $current, 2);
                    $current = '0.00';
                } else {
                    $income_arr[$expire_at] = '0.00';
                    $current = bcsub($current, $income, 2);
                }
            }
            if ($current === '0.00') {
                break;
            }
        }
        // 积分不够的时候就要借了
        if ($current > 0) {
            $this->borrow = bcadd($this->borrow, $current, 2);
        }
    }
    public function run()
    {
        foreach ($this->used_score as $used_at => $used) {
            $this->hedgeFund($used_at, $used);
        }
        // 获取最终积分余额
        if ($this->borrow > 0) {
            return bcsub(0, $this->borrow, 2);
        }
        $now = date("Y-m-d");
        $sum = 0;
        foreach ($this->income_score as $expire_at => $score) {
            if ($now < $expire_at) {
                $sum = bcadd($sum, $score, 2);
            }
        }
        return $sum;
    }
}
原文地址:https://www.cnblogs.com/zjhblogs/p/15492373.html