一个大致思路:拉出将所有积分变动数据 进行计算
<?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;
}
}