Files
zyt/server/app/common/service/followupaudio/FollowupAudioAccess.php
T

82 lines
3.6 KiB
PHP

<?php
declare(strict_types=1);
namespace app\common\service\followupaudio;
use app\adminapi\logic\firstvisit\MyPatientLogic;
use DomainException;
use think\facade\Db;
/** Every entry point (including the background worker) uses current actor and row scope. */
final class FollowupAudioAccess
{
public static function actor(int $actor): array
{
$row = Db::name('admin')->where('id', $actor)->whereNull('delete_time')->where('disable', 0)->lock(true)->find();
if (!$row) {
throw new DomainException('账号已停用或无权访问');
}
return [
'admin_id' => $actor, 'id' => $actor, 'root' => (int) $row['root'], 'name' => (string) $row['name'],
'role_id' => Db::name('admin_role')->where('admin_id', $actor)->lock(true)->column('role_id'),
'dept_id' => Db::name('admin_dept')->where('admin_id', $actor)->lock(true)->column('dept_id'),
];
}
public static function allowed(int $actor, array $info, string $permission): bool
{
if ($actor <= 0) {
return false;
}
if ((int) ($info['root'] ?? 0) === 1) {
return true;
}
// Explicit current reads also avoid permission-cache and MVCC snapshot staleness.
$roles = Db::name('admin_role')->where('admin_id', $actor)->lock(true)->column('role_id');
$menus = $roles ? Db::name('system_role_menu')->whereIn('role_id', $roles)->lock(true)->column('menu_id') : [];
$permissions = $menus ? Db::name('system_menu')->whereIn('id', array_unique($menus))
->where('is_disable', 0)->lock(true)->column('perms') : [];
return in_array(strtolower($permission), array_map('strtolower', $permissions), true);
}
public static function diagnosis(int $diagnosisId, int $actor, array $info, bool $daily = false, bool $enforcePreviewScope = true): array
{
if ($enforcePreviewScope) { FollowupAudioGate::assertScope($diagnosisId, $actor); }
if ($diagnosisId <= 0) {
throw new DomainException('诊单不存在或无权操作');
}
// Acquire the target row before rebuilding current actor/scope after any lock wait.
$row = Db::name('tcm_diagnosis')->where('id', $diagnosisId)->whereNull('delete_time')->lock(true)->find();
$info = self::actor($actor);
if (!$row || !self::allowed($actor, $info, 'tcm.diagnosis/edit')
|| ($daily && !self::allowed($actor, $info, 'tcm.diagnosis/dailyRecord'))) {
throw new DomainException('诊单不存在或无权操作');
}
$query = Db::name('tcm_diagnosis')->alias('d')->where('d.id', $diagnosisId)
->whereNull('d.delete_time')->where('d.status', 1);
MyPatientLogic::applyScope($query, $actor, $info);
if (!$query->lock(true)->find()) {
throw new DomainException('诊单不存在或无权操作');
}
return $row;
}
public static function task(int $taskId, int $actor, array $info, bool $daily = false): array
{
$task = FollowupAudioStore::task($taskId);
$diagnosis = self::diagnosis((int) $task['diagnosis_id'], $actor, $info, $daily);
if (array_key_exists('patient_id', $task)
&& (int) $task['patient_id'] !== (int) ($diagnosis['patient_id'] ?? 0)) {
throw new DomainException('诊单患者归属已变化,不能访问原回访录音');
}
return $task;
}
public static function canDaily(int $actor, array $info): bool
{
$info = self::actor($actor);
return self::allowed($actor, $info, 'tcm.diagnosis/dailyRecord');
}
}