Files
zyt/server/tests/RevisitCohortCalculatorTest.php
T
2026-10-17 16:08:24 +08:00

151 lines
9.3 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);
/** php server/tests/RevisitCohortCalculatorTest.php:日期队列 / 继承重算,不连接数据库。 */
require dirname(__DIR__) . '/vendor/autoload.php';
use app\adminapi\logic\stats\RevisitCohortCalculator as C;
use app\adminapi\logic\stats\RevisitRateCalculator as Legacy;
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++;
}
$t = static fn (string $date): int => (int) strtotime($date);
$logId = 0;
$orderId = 0;
$log = static function (int $did, int $from, int $to, string $date, int $inherit = 0) use (&$logId, $t): array {
return ['id' => ++$logId, 'diagnosis_id' => $did, 'from_assistant_id' => $from,
'to_assistant_id' => $to, 'create_time' => $t($date), 'is_inherit' => $inherit, 'operator_admin_id' => 7];
};
$order = static function (int $did, string $date) use (&$orderId, $t): array {
return ['id' => ++$orderId, 'diagnosis_id' => $did, 'create_time' => $t($date),
'order_no' => 'TEST-' . $orderId, 'amount' => 100, 'creator_id' => 1];
};
$sort = static function (array $rows): array {
usort($rows, static fn (array $a, array $b): int =>
[$a['diagnosis_id'], $a['create_time'], $a['id']] <=> [$b['diagnosis_id'], $b['create_time'], $b['id']]);
return $rows;
};
$start = $t('2026-09-01 00:00:00');
$end = $t('2026-10-30 23:59:59');
$now = $t('2026-12-01 12:00:00');
$run = static fn (array $logs, array $orders, string $type, array $offsets = [], array $reject = [], array $rollback = []): array =>
C::compute($sort($logs), $sort($orders), $offsets, $reject, $rollback, $start, $end, $now, $type);
$pack = static fn (array $calc, array $aids): array => C::metricPack($calc, $aids, [2, 3, 4]);
// 新指派筛选的是指派日期:周期前指派的患者不混入分子,周期后的复诊累计至查询当天。
$logs = [
$log(10, 0, 1, '2026-09-01 00:00:00'),
$log(11, 0, 1, '2026-08-31 23:59:59'),
$log(12, 0, 1, '2026-10-30 23:59:59'),
$log(13, 0, 1, '2026-10-31 00:00:00'),
];
$orders = [
$order(10, '2026-09-02'), $order(10, '2026-11-15'), $order(10, '2026-12-01 12:00:00'), $order(10, '2026-12-02'),
$order(11, '2026-09-03'), $order(11, '2026-10-01'),
$order(12, '2026-11-01'), $order(12, '2026-11-20'),
$order(13, '2026-11-01'), $order(13, '2026-11-20'),
];
$calc = $run($logs, $orders, 'new');
$p = $pack($calc, [1]);
expect($p['assigned_count'] === 2 && $p['inherit_count'] === 0, '范围两端均包含,前后一天均排除');
expect($p['visit2_count'] === 2 && $p['visit2_rate'] === 100.0, '11 月复诊计入9~10月指派队列');
expect($p['visit3_count'] === 1 && $p['visit4_count'] === 0, '查询同刻订单包含,未来订单不计入');
expect($p['visit_all_count'] === 3 && $p['visit_all_rate'] === 150.0, '合计是人均复诊单次,可以超过100%');
expect(count(C::scopedOrders($calc, [1], 2)[2]) === $p['visit2_count'], '二诊明细数量等于二诊分子');
expect($pack($calc, [99])['visit2_rate'] === null, '无患者分母为0,率为null');
// 继承前有三笔订单和偏移:继承后首单仍算二诊,原医助只领取转出前订单。
$logs = [$log(20, 0, 1, '2026-09-01'), $log(20, 1, 2, '2026-10-10 10:00:00', 1)];
$orders = [$order(20, '2026-09-02'), $order(20, '2026-09-03'), $order(20, '2026-09-04'),
$order(20, '2026-10-10 10:00:00'), $order(20, '2026-11-20')];
$new = $run($logs, $orders, 'new', [20 => 5]);
$inherited = $run($logs, $orders, 'inherit', [20 => 5]);
expect(array_keys($new['pairsByAssistant']) === [1] && array_keys($inherited['pairsByAssistant']) === [2], '新指派、继承独立入组');
expect(!isset($new['ordersByAssistant'][2]), '继承订单不得混入新指派分子');
$p = $pack($inherited, [2]);
expect($p['assigned_count'] === 0 && $p['inherit_count'] === 1 && $p['visit2_count'] === 1, '继承首单计二诊');
expect($p['visit3_count'] === 1 && $p['visit2_den'] === 1 && $p['visit3_den'] === 1, '继承次单计三诊,各列分母只含继承患者');
$first = C::scopedOrders($inherited, [2], 2)[2][20];
expect($first['create_time'] === $t('2026-10-10 10:00:00') && $first['global_visit_slot'] === 9, '同秒归新医助且不受全局诊次/偏移影响');
expect($first['cohort_assign_time'] === $t('2026-10-10 10:00:00'), '订单明细带实际承接时间');
expect($pack($new, [1])['visit_all_count'] === 3 && !isset($new['ordersByAssistant'][1][9]), '新指派保留全局诊次偏移,只计转出前3单');
// 转派、释放、再次继承、同一医助既新指派又继承:不混算,继承每段重新起算。
$logs = [
$log(30, 0, 1, '2026-09-01'),
$log(30, 1, 2, '2026-09-10', 1),
$log(30, 2, 2, '2026-09-11', 1), // 自指派忽略,不重置
$log(30, 2, 0, '2026-09-15'),
$log(30, 0, 2, '2026-09-20', 1),
$log(30, 2, 1, '2026-10-01', 1),
];
$orders = [$order(30, '2026-09-02'), $order(30, '2026-09-10'), $order(30, '2026-09-12'),
$order(30, '2026-09-16'), $order(30, '2026-09-21'), $order(30, '2026-09-22'),
$order(30, '2026-10-02')];
$calc = $run($logs, $orders, 'inherit');
expect($calc['pairsByAssistant'][2][30]['assign_count'] === 2, '多次真实继承计次数,自指派不计');
expect($calc['pairsByAssistant'][1][30]['assign_count'] === 1, '同一医助既新指派又继承,继承独立统计');
$p2 = $pack($calc, [2]);
expect($p2['inherit_count'] === 1 && $p2['visit2_count'] === 1 && $p2['visit3_count'] === 1, '重复继承患者分母、同一诊次分子均去重');
expect(C::scopedOrders($calc, [2], 3)[3][30]['create_time'] === $t('2026-09-12'), '自指派不把后续订单重置到二诊');
$total = $pack($calc, [1, 2]);
expect($total['inherit_count'] === 1 && $total['visit2_count'] === 1 && $total['visit2_rate'] === 100.0, '部门/合计按患者去重,不能把多医助同患者二诊重复相加');
expect($pack($calc, [1])['visit3_count'] === 0, '按医助筛选时不能混入另一医助三诊');
expect($total['visit_all_count'] === array_sum(array_map('count', C::scopedOrders($calc, [1, 2]))), '合计订单明细与去重分子逐项对账');
// 周期后重新继承的订单不应继续计入旧队列;释放也会终止归属。
$logs = [$log(40, 0, 1, '2026-09-01', 1), $log(40, 1, 0, '2026-10-01'),
$log(40, 0, 1, '2026-11-01', 1)];
$calc = $run($logs, [$order(40, '2026-09-02'), $order(40, '2026-10-02'), $order(40, '2026-11-02')], 'inherit');
expect($pack($calc, [1])['visit_all_count'] === 1, '释放后及区间外再次承接的订单不归旧队列');
// 写入侧从待分配重新指派给原医助时会回填 from,不能误判为自指派。
foreach (['new' => 0, 'inherit' => 1] as $type => $isInherit) {
$logs = [$log(41, 0, 1, '2026-08-01'), $log(41, 1, 0, '2026-08-10'),
$log(41, 1, 1, '2026-09-01', $isInherit)];
$calc = $run($logs, [$order(41, '2026-08-02'), $order(41, '2026-09-02')], $type);
expect($pack($calc, [1])['assigned_total_count'] === 1 && $pack($calc, [1])['visit2_count'] === 1,
'释放后同医助重新承接正常入组并统计:' . $type);
}
// 回退 is_inherit=0,通过回退键识别继承;拒收/退款剔除同时作用于分子、分母。
$rollbackTime = $t('2026-09-15');
$logs = [$log(50, 0, 1, '2026-09-01'), $log(50, 1, 2, '2026-09-15')];
$orders = [$order(50, '2026-09-02'), $order(50, '2026-09-16')];
$keys = [Legacy::rollbackKey(50, $rollbackTime, 7) => true];
expect(!isset($run($logs, $orders, 'new', [], [], $keys)['pairsByAssistant'][2]), '历史回退不能计入新指派');
expect($pack($run($logs, $orders, 'inherit', [], [], $keys), [2])['visit2_count'] === 1, '历史回退首单按继承二诊');
$rejected = $run($logs, $orders, 'inherit', [], [50 => true], $keys);
expect($rejected['pairsByAssistant'] === [] && $rejected['ordersByAssistant'] === [], '拒收/退款诊单完全剔除,无孤立分子');
// 同秒多次转派按日志 id 升序,订单最终归最新承接事件;两笔同秒有效订单按 id 编号。
$logs = [$log(60, 0, 1, '2026-09-01'), $log(60, 1, 2, '2026-09-10', 1), $log(60, 2, 3, '2026-09-10', 1)];
$calc = $run($logs, [$order(60, '2026-09-10'), $order(60, '2026-09-10')], 'inherit');
expect($pack($calc, [2])['visit_all_count'] === 0 && $pack($calc, [3])['visit_all_count'] === 2, '同秒转派和订单按稳定顺序计算');
// 日期解析支持跨月与未来结束日,不把月末或非法日期自动纠正。
[$s, $e, $sTs, $eTs] = C::dateRange(['start_date' => '2026-09-01', 'end_date' => '2026-10-30'], $t('2026-10-16'));
expect($s === '2026-09-01' && $e === '2026-10-30' && $sTs === $start && $eTs === $end, '跨月范围保留未来结束日');
expect(array_slice(C::dateRange([], $t('2026-10-16')), 0, 2) === ['2026-10-01', '2026-10-16'], '默认当月首日至当天');
expect(array_slice(C::dateRange(['month' => '2024-02'], $now), 0, 2) === ['2024-02-01', '2024-02-29'], '兼容旧 month 参数和闰月');
foreach ([['2026-02-30', '2026-03-01'], ['2026-09-10', '2026-09-01'], ['', '2026-10-30'], ['2026-9-01', '2026-10-30']] as [$s, $e]) {
$threw = false;
try {
C::dateRange(['start_date' => $s, 'end_date' => $e], $now);
} catch (InvalidArgumentException) {
$threw = true;
}
expect($threw, '拒绝非法/缺失/倒置日期:' . $s . ' - ' . $e);
}
echo "RevisitCohortCalculatorTest OK ({$checks} checks)\n";