82 lines
3.6 KiB
PHP
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');
|
|
}
|
|
}
|