You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel 10中基于用户偏好计算下一个可通知时间的问题排查

问题分析与修正方案

你的代码核心错误在于计算下一个可通知日期时,直接将用户定义的周几数值作为天数添加,而非计算当前日期到目标周几的实际天数差;同时依赖$notifiableDays数组的排序顺序,导致数组乱序时结果错误。

具体问题点

  1. 天数计算逻辑错误:例如当前是周一(用户定义的1),目标周二(用户定义的2),原代码用addDays(2)会得到周三,正确应该是addDays(1)。
  2. 依赖数组排序:如果$notifiableDays不是按升序排列,会取到第一个大于当前周几的数值,而非最近的那个。
  3. 跨周计算错误:当所有可通知日期都在当前日期之前时,直接取数组第一个元素添加天数,而非计算下一周的最近日期。

修正后的完整代码

/**
 * Get the next notifiable datetime based on configured setup
 */
public function getNextNotifiableDatetimeAttribute(): string
{
    $now = Carbon::now('UTC');
    $notifiableDays = $this->notify_on_days;

    // if on holiday, return next date as on_holiday_until
    if ($this->on_holiday_until && $this->on_holiday_until > $now) {
        return Carbon::parse($this->on_holiday_until)->toDateTimeString();
    }

    // 转换Carbon的周几格式为用户定义的格式(1=周一,7=周日)
    $currentUserDay = $now->dayOfWeek === 0 ? 7 : $now->dayOfWeek;
    // 如果当天可通知且在时段内,直接返回当前时间
    if (in_array($currentUserDay, $notifiableDays) && $this->isWithinTimeSlot($now)) {
        return $now->toDateTimeString();
    }

    // 计算到下一个可通知日期的最小天数差
    $minDaysToAdd = 7; // 初始化为一周的最大天数
    foreach ($notifiableDays as $day) {
        if ($day === $currentUserDay) {
            // 当天可通知但已过时段,取下周同一天
            $daysToAdd = 7;
        } elseif ($day > $currentUserDay) {
            // 本周内的后续日期,计算天数差
            $daysToAdd = $day - $currentUserDay;
        } else {
            // 跨周的日期,计算到下一周该日期的天数差
            $daysToAdd = $day + 7 - $currentUserDay;
        }

        // 保留最小的天数差
        if ($daysToAdd < $minDaysToAdd) {
            $minDaysToAdd = $daysToAdd;
        }
    }

    // 计算下一个可通知日期
    $nextNotifyDatetime = Carbon::createMidnightDate(
        $now->year,
        $now->month,
        $now->day
    )->addDays($minDaysToAdd);

    if (!$this->isWithinTimeSlot($nextNotifyDatetime)) {
        $nextNotifyDatetime = $this->setToNextAvailableTime($nextNotifyDatetime);
    }

    return $nextNotifyDatetime->toDateTimeString();
}

/**
 * Check if the given datetime is within the user's time slot.
 *
 * @param  Carbon\Carbon  $datetime
 * @return bool
 */
private function isWithinTimeSlot(Carbon $datetime): bool
{
    // if the user has time-sensitive notifications enabled then we should
    // disregard the time slot.
    if ($this->prefers_time_sensitive_notifications) {
        return true;
    }

    switch ($this->notify_time_slot) {
        case 'anytime':
            // anytime will notify regardless of current time
            return true;
        case 'morning':
            return $datetime->hour >= 6 && $datetime->hour < 12;
        case 'afternoon':
            return $datetime->hour >= 12 && $datetime->hour < 18;
        case 'evening':
            return $datetime->hour >= 18 && $datetime->hour < 23;
        case 'daytime':
            return $datetime->hour >= 9 && $datetime->hour < 18;
        default:
            // Handle additional time slots as needed.
            return false;
    }
}

/**
 * Set the given datetime to the next available time slot.
 *
 * @param  Carbon\Carbon  $datetime
 * @return Carbon\Carbon
 */
private function setToNextAvailableTime(Carbon $datetime): Carbon
{
    switch ($this->notify_time_slot) {
        case 'morning':
            return $datetime->setHour(6)->setMinute(0)->setSecond(0);
        case 'afternoon':
            return $datetime->setHour(12)->setMinute(0)->setSecond(0);
        case 'evening':
            return $datetime->setHour(18)->setMinute(0)->setSecond(0);
        case 'daytime':
            return $datetime->addDay()->setHour(9)->setMinute(0)->setSecond(0);
        default:
            // Handle additional time slots as needed.
            return $datetime;
    }
}

修正逻辑说明

  1. 统一周几格式:将Carbon的dayOfWeek(0=周日,1=周一)转换为用户定义的格式(1=周一,7=周日),避免格式不匹配导致的判断错误。
  2. 计算最小天数差:遍历所有可通知日期,分别计算本周内、跨周的天数差,保留最小的那个,确保找到最近的可通知日期。
  3. 脱离数组排序依赖:无论$notifiableDays数组顺序如何,都能正确找到最近的可通知日期。

内容的提问来源于stack exchange,提问作者Ryan H

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 04:45:03