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

135 lines
8.8 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);
/**
* 复诊到期企微推送命令(RevisitDueNotify)纯函数单测,不连库、不读 config、不调企微。
* php server/tests/RevisitDueNotifyTest.php
* 覆盖:文案拼装(daily 汇总排序/截断/过号标签、self、supervisor)、每日上限与日切判断(selfSentToday / selfQuotaLeft)、
* 主管抄送去重判断(shouldCcSupervisor 阈值 + wecom_supervisor_at 日切、normalizeSupervisorIds 去重/剔除本人)、
* 工作时段判断、日切区间。
*/
require dirname(__DIR__) . '/vendor/autoload.php';
use app\command\RevisitDueNotify as N;
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);
// ---------- 日切判断 ----------
expect(N::isSameDay($ts('2026-10-14 09:00:00'), $ts('2026-10-14 23:59:59')), '同日:同一自然日');
expect(!N::isSameDay($ts('2026-10-13 23:59:59'), $ts('2026-10-14 00:00:00')), '同日:跨午夜不同日');
expect(!N::isSameDay(0, $ts('2026-10-14 09:00:00')), '同日:0 视为从未发生');
[$s, $e] = N::dayRange('2026-10-14');
expect(date('Y-m-d H:i:s', $s) === '2026-10-14 00:00:00' && date('Y-m-d H:i:s', $e) === '2026-10-14 23:59:59', 'dayRange 当日起止');
[$s2] = N::dayRange('bad');
expect(date('Y-m-d', $s2) === date('Y-m-d'), 'dayRange 非法日期回落今天');
// ---------- 每日上限 ----------
$now = $ts('2026-10-14 10:30:00');
$row = ['wecom_self_at' => 0, 'wecom_self_count_day' => 0];
expect(N::selfSentToday($row, $now) === 0 && N::selfQuotaLeft($row, $now, 2) === 2, '从未推送:剩余 = cap');
$row = ['wecom_self_at' => $ts('2026-10-14 09:10:00'), 'wecom_self_count_day' => 1];
expect(N::selfSentToday($row, $now) === 1 && N::selfQuotaLeft($row, $now, 2) === 1, '今日已推 1 次:剩余 1');
$row = ['wecom_self_at' => $ts('2026-10-14 09:10:00'), 'wecom_self_count_day' => 2];
expect(N::selfQuotaLeft($row, $now, 2) === 0, '今日已推 2 次:达上限');
$row = ['wecom_self_at' => $ts('2026-10-13 19:50:00'), 'wecom_self_count_day' => 2];
expect(N::selfSentToday($row, $now) === 0 && N::selfQuotaLeft($row, $now, 2) === 2, '昨日推满:日切重置为 cap');
$row = ['wecom_self_at' => $ts('2026-10-14 09:10:00'), 'wecom_self_count_day' => 5];
expect(N::selfQuotaLeft($row, $now, 2) === 0, '计数超过 cap 不为负');
expect(N::selfQuotaLeft(['wecom_self_at' => 0, 'wecom_self_count_day' => 0], $now, 0) === 0, 'cap=0 不推');
// ---------- 主管抄送判断 ----------
$row = ['snooze_count' => 5, 'wecom_supervisor_at' => 0];
expect(N::shouldCcSupervisor($row, $now, 5), '首次达到 5 次且未抄送 → 抄送');
$row = ['snooze_count' => 4, 'wecom_supervisor_at' => 0];
expect(!N::shouldCcSupervisor($row, $now, 5), '4 次不抄送');
$row = ['snooze_count' => 7, 'wecom_supervisor_at' => $ts('2026-10-14 09:00:00')];
expect(!N::shouldCcSupervisor($row, $now, 5), '今日已抄送 → 不重复');
$row = ['snooze_count' => 7, 'wecom_supervisor_at' => $ts('2026-10-13 09:00:00')];
expect(N::shouldCcSupervisor($row, $now, 5), '昨日抄送过 → 今日再抄送一次');
$row = ['snooze_count' => 3, 'wecom_supervisor_at' => 0];
expect(N::shouldCcSupervisor($row, $now, 3), '阈值可配置(3)');
expect(!N::shouldCcSupervisor(['snooze_count' => 0, 'wecom_supervisor_at' => 0], $now, 0), '阈值 0 按 1 处理,0 次不抄送');
expect(N::normalizeSupervisorIds([3, '3', 0, -1, 'x', 5, 7], 5) === [3, 7], '主管名单:去重、剔除非正整数、剔除医助本人');
expect(N::normalizeSupervisorIds('3, 7,,7 9', 0) === [3, 7, 9], '主管名单:逗号/空格字符串');
expect(N::normalizeSupervisorIds(null) === [] && N::normalizeSupervisorIds([]) === [] && N::normalizeSupervisorIds([5], 5) === [], '主管名单:空 / 仅本人 → 空');
// ---------- 超期标签 ----------
expect(N::overdueLabel(['state' => C::STATE_OVERDUE, 'overdue_days' => 3]) === '超期3天', '标签:超期 3 天');
expect(N::overdueLabel(['state' => C::STATE_OVERDUE, 'overdue_days' => 0]) === '今日应挂号', '标签:今日应挂号');
expect(N::overdueLabel(['state' => C::STATE_MISSED, 'overdue_days' => null]) === '过号', '标签:过号无到期日');
expect(N::overdueLabel(['state' => C::STATE_MISSED, 'overdue_days' => 2]) === '过号·超期2天', '标签:过号且超期');
expect(N::overdueLabel(['state' => C::STATE_OVERDUE, 'overdue_days' => null]) === '到期', '标签:缺天数兜底');
// ---------- daily 文案 ----------
$items = [
['patient_name' => '李四', 'state' => C::STATE_OVERDUE, 'overdue_days' => 1],
['patient_name' => '张三', 'state' => C::STATE_OVERDUE, 'overdue_days' => 3],
['patient_name' => '王五', 'state' => C::STATE_MISSED, 'overdue_days' => null],
];
$text = N::buildDailyText($items, 5);
expect(str_starts_with($text, '【复诊到期提醒】您有 3 位患者到期未挂号,最久超期 3 天:'), 'daily:头部含人数与最久超期');
expect(str_contains($text, '张三(超期3天)、李四(超期1天)、王五(过号)'), 'daily:按超期天数降序,过号排末');
expect(!str_contains($text, '等 3 人') && str_ends_with($text, N::SIGNATURE), 'daily:未超出列数不加「等 N 人」,带落款');
$many = [];
for ($i = 1; $i <= 7; $i++) {
$many[] = ['patient_name' => '患者' . $i, 'state' => C::STATE_OVERDUE, 'overdue_days' => $i];
}
$text = N::buildDailyText($many, 5);
expect(str_contains($text, '您有 7 位患者') && str_contains($text, '最久超期 7 天'), 'daily:7 人统计');
expect(str_contains($text, '患者7(超期7天)、患者6(超期6天)、患者5(超期5天)、患者4(超期4天)、患者3(超期3天)…等 7 人'), 'daily:最多列 5 人并加「等 7 人」');
expect(!str_contains($text, '患者2('), 'daily:第 6 人起不列出');
$text = N::buildDailyText([['patient_name' => '', 'state' => C::STATE_OVERDUE, 'overdue_days' => 0]], 5);
expect(str_contains($text, '您有 1 位患者到期未挂号:患者(今日应挂号)') && !str_contains($text, '最久超期'), 'daily:空姓名兜底「患者」,无超期不写「最久超期」');
expect(N::buildDailyText([], 5) === '', 'daily:空列表返回空串');
expect(str_contains(N::buildDailyText($many, 0), '…等 7 人'), 'daily:listMax<=0 按 1 处理');
// ---------- self 文案 ----------
$row = ['patient_name' => '张三', 'current_slot' => 2, 'due_date' => '2026-10-10', 'snooze_count' => 5, 'assistant_id' => 7];
$st = ['state' => C::STATE_OVERDUE, 'overdue_days' => 4];
$text = N::buildSelfText($row, $st);
expect($text === '【复诊到期提醒】患者 张三(第2诊后,预计复诊 10-10)超期4天未挂号,您已延迟 5 次,请尽快联系患者挂号。' . N::SIGNATURE, 'self 文案');
$text = N::buildSelfText(['patient_name' => '', 'current_slot' => 0, 'due_date' => null, 'snooze_count' => 6], ['state' => C::STATE_MISSED, 'overdue_days' => null]);
expect($text === '【复诊到期提醒】患者 患者(待复诊)过号未挂号,您已延迟 6 次,请尽快联系患者挂号。' . N::SIGNATURE, 'self 文案:缺姓名/诊次/到期日');
// ---------- supervisor 文案 ----------
$row = ['patient_name' => '张三', 'current_slot' => 2, 'due_date' => '2026-10-10', 'snooze_count' => 5, 'contact_count' => 1, 'assistant_id' => 7];
$text = N::buildSupervisorText('李医助', $row, $st);
expect($text === '【复诊到期升级】医助 李医助 的患者 张三 超期4天未挂号(第2诊后,预计复诊 10-10),已延迟 5 次、已联系 1 次,请关注。' . N::SIGNATURE, 'supervisor 文案:含医助、患者、超期天数、延迟/联系次数');
$text = N::buildSupervisorText('', ['patient_name' => '张三', 'current_slot' => 1, 'due_date' => '2026-10-10', 'snooze_count' => 5, 'contact_count' => 0, 'assistant_id' => 7], $st);
expect(str_contains($text, '医助 #7 的患者') && !str_contains($text, '已联系'), 'supervisor 文案:医助缺名用 #id,无联系次数不写');
// ---------- 工作时段 ----------
$wh = ['09:00', '20:00'];
expect(N::withinWorkHours($ts('2026-10-14 09:00:00'), $wh), '工作时段:09:00 含起点');
expect(N::withinWorkHours($ts('2026-10-14 19:59:59'), $wh), '工作时段:19:59 在内');
expect(!N::withinWorkHours($ts('2026-10-14 20:00:00'), $wh), '工作时段:20:00 不含终点');
expect(!N::withinWorkHours($ts('2026-10-14 08:59:59'), $wh), '工作时段:08:59 在外');
expect(N::withinWorkHours($ts('2026-10-14 03:00:00'), ['00:00', '24:00']), '工作时段:全天配置');
expect(!N::withinWorkHours($ts('2026-10-14 21:00:00'), 'bad'), '工作时段:非法配置回落 09:00~20:00');
echo "RevisitDueNotifyTest OK ({$checks} checks)\n";