This commit is contained in:
Your Name
2026-05-26 11:23:43 +08:00
parent 9af5c5be63
commit 5b233fb0ab
2 changed files with 186 additions and 34 deletions
@@ -1437,6 +1437,16 @@ class YejiStatsLogic
* @param int[] $deptIdsForAdmin
* @param array<int, true> $tableRowSet
* @param array<int, array<string, mixed>> $deptById
*
* 多链路 admin 的归属选择:按**原始 admin_dept 链路的深度**择优(更深 = 更具体的岗位归属),
* 而非按解析出的表格行深度。否则同层中心兄弟节点在父视图(如 [郑州一中心, 郑州二中心])会
* id 大小决胜,而子视图(钻取到某中心后行集变为其子组)则只剩唯一候选——同一条挂号在两个
* 视图被路由到不同行,导致 admin「已完成挂号」父行 < 子行合计。
*
* 向下兜底(resolveFirstTableRowUnderDept)按「链路深度 最浅表格行深度 - 1」收窄:
* 仅当链路本身就是用户筛选根(与表格行同层或刚刚高一层)时才允许向下归到首个子表格行;
* 否则像「只挂在甄养堂(root) / 一中心」等过宽链路,会在不同视图被任意攀附到第一个子节点,
* 同一挂号父视图归到第一个中心、子视图归到该中心第一个子组 父子合计错位。
*/
private static function mapAdminDeptLinksToTableRow(array $deptIdsForAdmin, array $tableRowDeptIds, array $tableRowSet, array $deptById): int
{
@@ -1449,7 +1459,7 @@ class YejiStatsLogic
}
$hit = self::resolveDeptToNearestTableRowUpward($d, $tableRowSet, $deptById);
if ($hit > 0) {
$depth = self::deptDepthFromRoot($hit, $deptById);
$depth = self::deptDepthFromRoot($d, $deptById);
if ($depth > $bestDepth || ($depth === $bestDepth && ($best === 0 || $hit < $best))) {
$best = $hit;
$bestDepth = $depth;
@@ -1459,11 +1469,22 @@ class YejiStatsLogic
if ($best > 0) {
return $best;
}
$minTableRowDepth = PHP_INT_MAX;
foreach ($tableRowDeptIds as $tid) {
$td = self::deptDepthFromRoot((int) $tid, $deptById);
if ($td > 0 && $td < $minTableRowDepth) {
$minTableRowDepth = $td;
}
}
$linkDepthFloor = $minTableRowDepth === PHP_INT_MAX ? PHP_INT_MAX : ($minTableRowDepth - 1);
foreach ($deptIdsForAdmin as $d) {
$d = (int) $d;
if ($d <= 0) {
continue;
}
if (self::deptDepthFromRoot($d, $deptById) < $linkDepthFloor) {
continue;
}
$hit = self::resolveFirstTableRowUnderDept($d, $tableRowDeptIds, $deptById);
if ($hit > 0) {
return $hit;
@@ -1618,10 +1639,31 @@ class YejiStatsLogic
$aids[] = $d;
}
}
$aids = array_values(array_unique($aids));
$adminToTable = ($tableRowDeptIds !== [] && $deptById !== [])
? self::batchMapPerformanceAdminsToTableDept($aids, $tableRowDeptIds, $deptById)
: [];
/**
* 视图无关的「该 admin 是否有 admin_dept 归属」。
* 用途:仅当 admin 完全无部门归属时才允许由"医助"回退到"接诊医生"
* 否则父视图能解析到 admin 的中心行 计入 assistant;子视图(钻取另一中心)
* adminToTable / adminToPrimary 双双 0 时会回退到 doctor,造成同一挂号在父视图归 assistant、
* 在子视图归 doctor,父行 < 子行合计。
*
* @var array<int, true> $adminHasAnyDept
*/
$adminHasAnyDept = [];
if ($aids !== []) {
$linkedAids = Db::name('admin_dept')->whereIn('admin_id', $aids)->column('admin_id');
foreach ($linkedAids as $lid) {
$lid = (int) $lid;
if ($lid > 0) {
$adminHasAnyDept[$lid] = true;
}
}
}
$byDept = [];
foreach ($rows as $r) {
$effId = (int) ($r['eff_a'] ?? 0);
@@ -1657,6 +1699,14 @@ class YejiStatsLogic
$primary = $p;
break;
}
/**
* 视图无关地有部门归属,仅是当前视图行集 / primaryDeptIds 不含
* 视为该 admin 的归属(视图外不可见),不再回退到下一候选(医生),
* 以避免父子视图归属分裂。narrowTotalsToTable 视图下 unmapped 会被自动隐藏。
*/
if (isset($adminHasAnyDept[$aid])) {
break;
}
}
if ($primary > 0) {
@@ -4058,7 +4108,14 @@ class YejiStatsLogic
if ($candidates === []) {
return 0;
}
/** @var int[] $matching */
/**
* 每个命中的表格行追加 ['rid'=>表格行 id, 'link_depth'=>原始 admin_dept 链路深度]
* 用链路深度(而非表格行深度)择优,保证父视图(同层中心兄弟节点行集)与子视图(钻取单中心后行集为其子组)
* 对同一 admin 的归属选择一致——否则同 admin 在父行被路由到「错」的中心,在子行被路由到正确中心子组,
* 表现为父行计数 < 子行合计。
*
* @var array<int, array{rid: int, link_depth: int}> $matching
*/
$matching = [];
foreach ($tableRowDeptIds as $rid) {
$rid = (int) $rid;
@@ -4069,6 +4126,7 @@ class YejiStatsLogic
if ($flip === []) {
continue;
}
$bestLinkDepth = -1;
foreach ($candidates as $aid => $_) {
$aid = (int) $aid;
if ($aid <= 0) {
@@ -4077,11 +4135,16 @@ class YejiStatsLogic
foreach ($adminDeptIdList[$aid] ?? [] as $d) {
$d = (int) $d;
if ($d > 0 && isset($flip[$d])) {
$matching[] = $rid;
break 2;
$linkDepth = self::deptDepthFromRoot($d, $deptById);
if ($linkDepth > $bestLinkDepth) {
$bestLinkDepth = $linkDepth;
}
}
}
}
if ($bestLinkDepth >= 0) {
$matching[] = ['rid' => $rid, 'link_depth' => $bestLinkDepth];
}
}
if ($matching === []) {
foreach ($candidates as $aid => $_) {
@@ -4099,8 +4162,9 @@ class YejiStatsLogic
}
$best = 0;
$bestDepth = -1;
foreach ($matching as $rid) {
$depth = self::deptDepthFromRoot($rid, $deptById);
foreach ($matching as $m) {
$rid = $m['rid'];
$depth = $m['link_depth'];
if ($depth > $bestDepth || ($depth === $bestDepth && ($best === 0 || $rid < $best))) {
$best = $rid;
$bestDepth = $depth;