Files
zyt/server/tests/RevisitDueLogicTest.php
T
2026-10-15 11:03:54 +08:00

132 lines
8.7 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
/**
* 复诊到期弹窗逻辑(RevisitDueLogic)纯函数单测,不连库、不读 config。
* php server/tests/RevisitDueLogicTest.php
* 覆盖:延迟间隔阶梯(4h→2h→1h→30min、default 回落、非法配置)、工作时段解析、
* 钳制(早于开始 → 当日开始;晚于结束 → 次日开始;边界 20:00:00 / 20:00:01)、
* nextRemindAfterSnooze 组合、承诺日 09:00、承诺日范围、提醒文案、升级等级、弹窗口径。
*/
require dirname(__DIR__) . '/vendor/autoload.php';
use app\adminapi\logic\tcm\RevisitDueLogic as L;
use app\common\service\tcm\RevisitDueCalculator as C;
date_default_timezone_set('Asia/Shanghai');
$checks = 0;
function expect(bool $condition, string $message): void
{
global $checks;
if (!$condition) {
fwrite(STDERR, "FAIL: {$message}\n");
exit(1);
}
$checks++;
}
$ts = static fn (string $dt): int => (int) strtotime($dt);
$fmt = static fn (int $t): string => date('Y-m-d H:i:s', $t);
$cfg = [
'snooze_intervals' => [1 => 14400, 2 => 7200, 3 => 3600, 'default' => 1800],
'work_hours' => ['09:00', '20:00'],
'promise_max_days' => 14,
'contact_max_per_cycle' => 2,
];
// ---------- snoozeInterval ----------
expect(L::snoozeInterval(1, $cfg['snooze_intervals']) === 14400, '第 1 次延迟 4h');
expect(L::snoozeInterval(2, $cfg['snooze_intervals']) === 7200, '第 2 次延迟 2h');
expect(L::snoozeInterval(3, $cfg['snooze_intervals']) === 3600, '第 3 次延迟 1h');
expect(L::snoozeInterval(4, $cfg['snooze_intervals']) === 1800, '第 4 次起回落 default 30min');
expect(L::snoozeInterval(99, $cfg['snooze_intervals']) === 1800, '第 99 次仍 30min');
expect(L::snoozeInterval(1, ['1' => '7200', 'default' => '600']) === 7200, '字符串配置可被归一');
expect(L::snoozeInterval(5, [1 => 100]) === L::DEFAULT_SNOOZE_SECONDS, '缺 default 时回落常量 1800');
expect(L::snoozeInterval(1, null) === L::DEFAULT_SNOOZE_SECONDS, '非数组配置回落 1800');
expect(L::snoozeInterval(1, [1 => 0, 'default' => -5]) === L::DEFAULT_SNOOZE_SECONDS, '非正数回落 1800');
expect(L::snoozeInterval(0, $cfg['snooze_intervals']) === 1800, 'n=0 取 default');
// ---------- parseWorkHours ----------
expect(L::parseWorkHours(['09:00', '20:00']) === [540, 1200], '09:00~20:00 → 540~1200 分钟');
expect(L::parseWorkHours(['08:30', '18:00']) === [510, 1080], '08:30~18:00');
expect(L::parseWorkHours(['09:00', '24:00']) === [540, 1440], '结束允许 24:00');
expect(L::parseWorkHours(['20:00', '09:00']) === [540, 1200], '开始不早于结束 → 默认');
expect(L::parseWorkHours(['9', '20']) === [540, 1200], '非 HH:mm → 默认');
expect(L::parseWorkHours(null) === [540, 1200], 'null → 默认');
expect(L::parseWorkHours(['09:00']) === [540, 1200], '少于两项 → 默认');
// ---------- clampToWorkHours ----------
$wh = $cfg['work_hours'];
expect(L::clampToWorkHours($ts('2026-10-13 14:00:00'), $wh) === $ts('2026-10-13 14:00:00'), '工作时段内原样返回');
expect(L::clampToWorkHours($ts('2026-10-13 20:30:00'), $wh) === $ts('2026-10-14 09:00:00'), '20:30 → 次日 09:00');
expect(L::clampToWorkHours($ts('2026-10-13 20:00:00'), $wh) === $ts('2026-10-13 20:00:00'), '恰好 20:00:00 不顺延');
expect(L::clampToWorkHours($ts('2026-10-13 20:00:01'), $wh) === $ts('2026-10-14 09:00:00'), '20:00:01 顺延次日 09:00');
expect(L::clampToWorkHours($ts('2026-10-13 03:00:00'), $wh) === $ts('2026-10-13 09:00:00'), '凌晨 → 当日 09:00');
expect(L::clampToWorkHours($ts('2026-10-13 08:59:59'), $wh) === $ts('2026-10-13 09:00:00'), '08:59:59 → 09:00:00');
expect(L::clampToWorkHours($ts('2026-10-13 09:00:00'), $wh) === $ts('2026-10-13 09:00:00'), '恰好 09:00 原样');
expect(L::clampToWorkHours($ts('2026-10-31 23:30:00'), $wh) === $ts('2026-11-01 09:00:00'), '跨月顺延');
expect(L::clampToWorkHours($ts('2026-12-31 21:00:00'), $wh) === $ts('2027-01-01 09:00:00'), '跨年顺延');
expect(L::clampToWorkHours($ts('2026-10-13 18:30:00'), ['08:30', '18:00']) === $ts('2026-10-14 08:30:00'), '自定义时段 18:30 → 次日 08:30');
expect(L::clampToWorkHours($ts('2026-10-13 23:59:59'), ['09:00', '24:00']) === $ts('2026-10-13 23:59:59'), '24:00 结束时深夜不顺延');
// ---------- nextRemindAfterSnooze ----------
expect($fmt(L::nextRemindAfterSnooze($ts('2026-10-13 09:30:00'), 1, $cfg)) === '2026-10-13 13:30:00', '09:30 第 1 次 → 13:30');
expect($fmt(L::nextRemindAfterSnooze($ts('2026-10-13 17:30:00'), 1, $cfg)) === '2026-10-14 09:00:00', '17:30 + 4h 越过 20:00 → 次日 09:00');
expect($fmt(L::nextRemindAfterSnooze($ts('2026-10-13 18:30:00'), 3, $cfg)) === '2026-10-13 19:30:00', '18:30 第 3 次 +1h → 19:30');
expect($fmt(L::nextRemindAfterSnooze($ts('2026-10-13 19:50:00'), 4, $cfg)) === '2026-10-14 09:00:00', '19:50 第 4 次 +30min → 次日 09:00');
expect($fmt(L::nextRemindAfterSnooze($ts('2026-10-13 20:30:00'), 1, $cfg)) === '2026-10-14 09:00:00', '20:30 点延迟 → 次日 09:00(验收 §11-4-2)');
expect($fmt(L::nextRemindAfterSnooze($ts('2026-10-13 19:00:00'), 5, $cfg)) === '2026-10-13 19:30:00', '第 5 次 30min 仍在时段内');
expect(L::nextRemindAfterSnooze($ts('2026-10-13 10:00:00'), 1, []) === $ts('2026-10-13 10:30:00'), '空配置:30min + 默认时段');
expect(L::nextRemindAfterSnooze($ts('2026-10-13 10:00:00'), 1, $cfg) > $ts('2026-10-13 10:00:00'), '下次提醒恒晚于 now(条件 UPDATE 幂等前提)');
// ---------- promisedRemindAt ----------
expect($fmt(L::promisedRemindAt('2026-10-16', $wh)) === '2026-10-16 09:00:00', '承诺日 09:00');
expect($fmt(L::promisedRemindAt('2026-10-16', ['08:30', '18:00'])) === '2026-10-16 08:30:00', '承诺日取时段开始');
expect(L::promisedRemindAt('2026-13-40', $wh) === 0, '非法日期 → 0');
expect(L::promisedRemindAt('', $wh) === 0, '空 → 0');
expect($fmt(L::promisedRemindAt('2026-10-16 12:00:00', $wh)) === '2026-10-16 09:00:00', '带时间的串只取日期');
// ---------- isPromisedDateInRange ----------
expect(L::isPromisedDateInRange('2026-10-13', '2026-10-13', 14), '今天可选');
expect(L::isPromisedDateInRange('2026-10-27', '2026-10-13', 14), '今天 +14 可选');
expect(!L::isPromisedDateInRange('2026-10-28', '2026-10-13', 14), '今天 +15 不可选');
expect(!L::isPromisedDateInRange('2026-10-12', '2026-10-13', 14), '昨天不可选');
expect(!L::isPromisedDateInRange('bad', '2026-10-13', 14), '非法日期不可选');
expect(!L::isPromisedDateInRange('2026-02-30', '2026-02-13', 30), '不存在的日期不可选');
expect(L::isPromisedDateInRange('2026-10-13', '2026-10-13', 0), 'max=0 仅今天');
// ---------- remindTimeText ----------
$now = $ts('2026-10-13 10:00:00');
expect(L::remindTimeText($ts('2026-10-13 14:30:00'), $now) === '14:30', '当日 HH:mm');
expect(L::remindTimeText($ts('2026-10-14 09:00:00'), $now) === '明日 09:00', '次日前缀');
expect(L::remindTimeText($ts('2026-10-16 09:00:00'), $now) === '10-16 09:00', '更远 MM-DD HH:mm');
expect(L::remindTimeText(0, $now) === '', '0 → 空');
// ---------- escalationLevel(与 Calculator 一致) ----------
expect(C::escalationLevel(1) === 1 && C::escalationLevel(2) === 1, '1~2 次 → level 1');
expect(C::escalationLevel(3) === 2 && C::escalationLevel(4) === 2, '3~4 次 → level 2');
expect(C::escalationLevel(5) === 3, '第 5 次 → level 3 居中模态');
// ---------- toastEligible ----------
$row = static fn (array $o = []): array => array_replace([
'closed_state' => 0, 'appointment_state' => 0, 'due_date' => '2026-10-10', 'promised_date' => null,
], $o);
expect(L::toastEligible($row(), '2026-10-13'), '到期未挂号进弹窗');
expect(L::toastEligible($row(['due_date' => '2026-10-13']), '2026-10-13'), '今日到期进弹窗');
expect(!L::toastEligible($row(['due_date' => '2026-10-14']), '2026-10-13'), '未到期不进');
expect(L::toastEligible($row(['appointment_state' => 4, 'due_date' => '2026-10-20']), '2026-10-13'), '过号不受到期日限制');
expect(L::toastEligible($row(['appointment_state' => 4, 'due_date' => null]), '2026-10-13'), '过号且缺天数仍进');
expect(!L::toastEligible($row(['due_date' => null]), '2026-10-13'), '缺天数不进');
expect(!L::toastEligible($row(['promised_date' => '2026-10-13']), '2026-10-13'), '承诺日=今天不进');
expect(!L::toastEligible($row(['promised_date' => '2026-10-16']), '2026-10-13'), '承诺日未到不进');
expect(L::toastEligible($row(['promised_date' => '2026-10-12']), '2026-10-13'), '承诺日已过再弹');
expect(!L::toastEligible($row(['closed_state' => 4]), '2026-10-13'), '人工关闭不进');
expect(!L::toastEligible($row(['closed_state' => 2]), '2026-10-13'), '已挂号不进');
expect(L::toastEligible($row(['promised_date' => '0000-00-00']), '2026-10-13'), '0000-00-00 视为无承诺');
echo "RevisitDueLogicTest OK ({$checks} checks)\n";