This commit is contained in:
Your Name
2026-04-27 15:30:28 +08:00
parent 23bd86e056
commit fe14f67965
21 changed files with 2335 additions and 449 deletions
@@ -130,7 +130,7 @@ class OrderController extends BaseAdminController
public function orderStats()
{
$params = $this->request->get();
$result = OrderLogic::orderStats($params);
$result = OrderLogic::orderStats($params, (int) $this->adminId, $this->adminInfo);
return $this->data($result);
}
@@ -59,7 +59,7 @@ class DiagnosisController extends BaseAdminController
public function assistantDiagnosisStats()
{
$params = $this->request->get();
$result = DiagnosisLogic::assistantDiagnosisStats($params);
$result = DiagnosisLogic::assistantDiagnosisStats($params, (int) $this->adminId, $this->adminInfo);
return $this->data($result);
}
@@ -449,7 +449,7 @@ class DiagnosisController extends BaseAdminController
*/
public function getAssistants()
{
$result = DiagnosisLogic::getAssistants();
$result = DiagnosisLogic::getAssistants((int) $this->adminId, $this->adminInfo);
return $this->data($result);
}
+47 -1
View File
@@ -19,11 +19,15 @@ use app\common\lists\ListsExcelInterface;
use app\common\lists\ListsExtendInterface;
use app\common\lists\ListsSearchInterface;
use app\common\lists\ListsSortInterface;
use app\common\lists\Traits\HasDataScopeFilter;
use app\common\model\auth\Admin;
use app\common\model\auth\AdminRole;
use app\common\model\auth\SystemRole;
use app\common\model\dept\Dept;
use app\common\model\dept\Jobs;
use app\common\model\doctor\Appointment;
use app\common\model\tcm\Diagnosis;
use think\facade\Db;
/**
* 管理员列表
@@ -32,6 +36,7 @@ use app\common\model\dept\Jobs;
*/
class AdminLists extends BaseAdminDataLists implements ListsExtendInterface, ListsSearchInterface, ListsSortInterface,ListsExcelInterface
{
use HasDataScopeFilter;
/**
* @notes 设置导出字段
* @return string[]
@@ -116,9 +121,37 @@ class AdminLists extends BaseAdminDataLists implements ListsExtendInterface, Lis
if ($progressBoard) {
// 面诊进度:固定只拉「医生」角色且未禁用,忽略客户端篡改的 role_id
$adminIds = AdminRole::where('role_id', 1)->column('admin_id');
$adminIds = array_map('intval', AdminRole::where('role_id', 1)->column('admin_id'));
// 数据范围:医生与业务/医助通常不在同一部门,按 admin id 直接取交集会全空。
// 这里改为「按当前账号可见的挂号反查涉及的医生」,与 AppointmentLists 的可见性一致:
// 可见挂号 = a.doctor_id ∈ visible OR diagnosis.assistant_id ∈ visible
$visibleIds = $this->getDataScopeVisibleAdminIds();
if ($visibleIds !== null) {
if ($visibleIds === [] || $adminIds === []) {
$adminIds = [];
} else {
$aTbl = (new Appointment())->getTable();
$dTbl = (new Diagnosis())->getTable();
$inList = implode(',', array_map('intval', $visibleIds));
$rows = Db::query(
"SELECT DISTINCT a.doctor_id FROM `{$aTbl}` a "
. "LEFT JOIN `{$dTbl}` u ON a.patient_id = u.id "
. "WHERE (a.doctor_id IN ({$inList}) "
. " OR (u.id IS NOT NULL AND u.delete_time IS NULL AND u.assistant_id IN ({$inList})))"
);
$apptDoctorIds = array_filter(array_map(static function ($r): int {
return (int) ($r['doctor_id'] ?? 0);
}, $rows ?: []));
$adminIds = array_values(array_intersect($adminIds, $apptDoctorIds));
}
}
if (!empty($adminIds)) {
$where[] = ['id', 'in', $adminIds];
} else {
// 数据范围下没有可见医生:强制空集
$where[] = ['id', '=', 0];
}
$where[] = ['disable', '=', 0];
} else {
@@ -134,6 +167,19 @@ class AdminLists extends BaseAdminDataLists implements ListsExtendInterface, Lis
}
}
// 数据范围:仅当调用方主动传 apply_data_scope=1 时启用
// (管理员表被多场景共用:医生/医助选择器、看板、组织架构等;不主动启用避免误伤)
if ((int) ($this->params['apply_data_scope'] ?? 0) === 1) {
$visibleIds = $this->getDataScopeVisibleAdminIds();
if ($visibleIds !== null) {
if ($visibleIds === []) {
$where[] = ['id', '=', 0]; // 强制空
} else {
$where[] = ['id', 'in', $visibleIds];
}
}
}
return $where;
}
+1 -1
View File
@@ -63,7 +63,7 @@ class RoleLists extends BaseAdminDataLists
public function lists(): array
{
$lists = SystemRole::with(['role_menu_index'])
->field('id,name,desc,sort,create_time')
->field('id,name,desc,sort,data_scope,create_time')
->limit($this->limitOffset, $this->limitLength)
->order(['sort' => 'desc', 'id' => 'desc'])
->select()
@@ -9,6 +9,7 @@ use app\common\model\tcm\Prescription;
use app\common\model\auth\AdminRole;
use app\common\lists\ListsExtendInterface;
use app\common\lists\ListsSearchInterface;
use app\common\lists\Traits\HasDataScopeFilter;
/**
* 医生预约列表
@@ -17,6 +18,31 @@ use app\common\lists\ListsSearchInterface;
*/
class AppointmentLists extends BaseAdminDataLists implements ListsSearchInterface, ListsExtendInterface
{
use HasDataScopeFilter;
/**
* 数据隔离:医生或医助(u.assistant_id = diag.assistant_id)命中可见集合;progress_board 场景放开(看板跨医生查看)
*/
private function applyDataScopeForAppointment($query, bool $progressBoard): void
{
if ($progressBoard) {
return;
}
if (!$this->dataScopeShouldApply()) {
return;
}
$ids = $this->getDataScopeVisibleAdminIds();
if ($ids === null) {
return;
}
if ($ids === []) {
$query->whereRaw('0 = 1');
return;
}
$inList = implode(',', $ids);
$query->whereRaw("(a.doctor_id IN ({$inList}) OR u.assistant_id IN ({$inList}))");
}
/**
* @notes 设置搜索条件
* @return array
@@ -114,6 +140,8 @@ class AppointmentLists extends BaseAdminDataLists implements ListsSearchInterfac
}
}
$this->applyDataScopeForAppointment($query, $progressBoard);
// 诊单软删除后不再展示对应挂号(leftJoin 时无诊单或诊单未删)
$query->whereRaw('(u.id IS NULL OR u.delete_time IS NULL)');
@@ -269,6 +297,8 @@ class AppointmentLists extends BaseAdminDataLists implements ListsSearchInterfac
}
}
$this->applyDataScopeForAppointment($query, $progressBoard);
$query->whereRaw('(u.id IS NULL OR u.delete_time IS NULL)');
}
@@ -6,6 +6,7 @@ namespace app\adminapi\lists\order;
use app\adminapi\lists\BaseAdminDataLists;
use app\common\lists\ListsSearchInterface;
use app\common\lists\Traits\HasDataScopeFilter;
use app\common\model\Order;
use app\common\model\auth\AdminRole;
@@ -16,6 +17,7 @@ use app\common\model\auth\AdminRole;
*/
class OrderLists extends BaseAdminDataLists implements ListsSearchInterface
{
use HasDataScopeFilter;
/**
* 将列表「创建时间」参数规范为可解析的日期时间(datetimerange 已是 Y-m-d H:i:s,不能再去拼接 00:00:00,否则会变成非法字符串)
*/
@@ -214,7 +216,9 @@ class OrderLists extends BaseAdminDataLists implements ListsSearchInterface
$this->appendCreateTimeWhere($where);
return Order::where($where)
$query = Order::where($where);
$this->applyDataScopeByOwner($query, 'creator_id');
return $query
->with(['patient', 'creator'])
->order(['create_time' => 'desc'])
->limit($this->limitOffset, $this->pageSize)
@@ -251,6 +255,8 @@ class OrderLists extends BaseAdminDataLists implements ListsSearchInterface
$this->appendCreateTimeWhere($where);
return Order::where($where)->count();
$query = Order::where($where);
$this->applyDataScopeByOwner($query, 'creator_id');
return $query->count();
}
}
@@ -25,6 +25,7 @@ use app\common\model\auth\Admin;
use app\common\model\auth\AdminDept;
use app\common\model\auth\AdminRole;
use app\common\lists\ListsSearchInterface;
use app\common\lists\Traits\HasDataScopeFilter;
/**
* 中医辨房病因诊单列表
@@ -33,6 +34,7 @@ use app\common\lists\ListsSearchInterface;
*/
class DiagnosisLists extends BaseAdminDataLists implements ListsSearchInterface
{
use HasDataScopeFilter;
/**
* @notes 设置搜索条件
* @return array
@@ -70,6 +72,10 @@ class DiagnosisLists extends BaseAdminDataLists implements ListsSearchInterface
$query->where('assistant_id', $this->adminId);
}
if (!$pendingAssign) {
$this->applyDataScopeByOwner($query, 'assistant_id');
}
// 关键字搜索:支持患者姓名或手机号(模糊匹配)
if (isset($this->params['keyword']) && trim((string) $this->params['keyword']) !== '') {
$keyword = trim((string) $this->params['keyword']);
@@ -459,6 +465,10 @@ class DiagnosisLists extends BaseAdminDataLists implements ListsSearchInterface
$query->where('assistant_id', $this->adminId);
}
if (!$pendingAssign) {
$this->applyDataScopeByOwner($query, 'assistant_id');
}
// 关键字搜索:支持患者姓名或手机号(模糊匹配)
if (isset($this->params['keyword']) && trim((string) $this->params['keyword']) !== '') {
$keyword = trim((string) $this->params['keyword']);
@@ -6,6 +6,7 @@ namespace app\adminapi\lists\tcm;
use app\adminapi\lists\BaseAdminDataLists;
use app\common\lists\ListsSearchInterface;
use app\common\lists\Traits\HasDataScopeFilter;
use app\adminapi\logic\tcm\PrescriptionLogic;
use app\common\model\tcm\Prescription;
use app\common\model\tcm\PrescriptionOrder;
@@ -15,6 +16,7 @@ use app\common\model\tcm\PrescriptionOrder;
*/
class PrescriptionLists extends BaseAdminDataLists implements ListsSearchInterface
{
use HasDataScopeFilter;
/**
* @notes 搜索条件
*/
@@ -138,6 +140,7 @@ class PrescriptionLists extends BaseAdminDataLists implements ListsSearchInterfa
$this->applySourceFilter($query);
$this->applyVisibilityScope($query);
$this->applyCreatorIdsFilter($query);
$this->applyDataScopeByOwnerColumns($query, ['creator_id', 'assistant_id']);
$lists = $query
->whereNull('delete_time')
@@ -231,6 +234,7 @@ class PrescriptionLists extends BaseAdminDataLists implements ListsSearchInterfa
$this->applySourceFilter($query);
$this->applyVisibilityScope($query);
$this->applyCreatorIdsFilter($query);
$this->applyDataScopeByOwnerColumns($query, ['creator_id', 'assistant_id']);
return $query->whereNull('delete_time')->count();
}
@@ -9,6 +9,7 @@ use app\adminapi\logic\dept\DeptLogic;
use app\adminapi\logic\tcm\PrescriptionOrderLogic;
use app\common\lists\ListsExtendInterface;
use app\common\lists\ListsSearchInterface;
use app\common\lists\Traits\HasDataScopeFilter;
use app\common\model\Order;
use app\common\model\tcm\Diagnosis;
use app\common\model\auth\AdminDept;
@@ -21,6 +22,8 @@ use think\db\Query;
class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchInterface, ListsExtendInterface
{
use HasDataScopeFilter;
public function setSearch(): array
{
return [
@@ -76,6 +79,36 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
}
});
}
$this->applyDataScopeForPrescriptionOrder($query);
}
/**
* 数据隔离:创建者 ∈ 可见 admin,或 关联诊单医助 ∈ 可见 admin
*/
private function applyDataScopeForPrescriptionOrder($query): void
{
if (!$this->dataScopeShouldApply()) {
return;
}
$ids = $this->getDataScopeVisibleAdminIds();
if ($ids === null) {
return;
}
if ($ids === []) {
$query->whereRaw('0 = 1');
return;
}
$poTbl = (new PrescriptionOrder())->getTable();
$diagTbl = (new Diagnosis())->getTable();
$inList = implode(',', $ids);
$query->where(function ($q) use ($poTbl, $diagTbl, $inList) {
$q->whereIn('creator_id', explode(',', $inList));
$q->whereOrRaw(
"EXISTS (SELECT 1 FROM `{$diagTbl}` dg WHERE dg.`id` = `{$poTbl}`.`diagnosis_id` "
. "AND dg.`delete_time` IS NULL AND dg.`assistant_id` IN ({$inList}))"
);
});
}
/**
@@ -100,12 +133,14 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
$this->applyDoctorAssistantFilters($query);
$this->applyPatientKeywordFilter($query);
if (PrescriptionOrderLogic::canViewOrderListStatsAllScope($this->adminInfo)) {
$this->applyDataScopeForPrescriptionOrder($query);
return $query;
}
$assistantRid = (int) Config::get('project.prescription_order_stats_assistant_role_id', 2);
$myRoles = array_map('intval', $this->adminInfo['role_id'] ?? []);
if ($assistantRid > 0 && in_array($assistantRid, $myRoles, true)) {
$this->applyAssistantDiagnosisOnlyFilter($query);
$this->applyDataScopeForPrescriptionOrder($query);
return $query;
}
@@ -118,6 +153,7 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
}
});
}
$this->applyDataScopeForPrescriptionOrder($query);
return $query;
}
+16 -1
View File
@@ -48,6 +48,7 @@ class RoleLogic extends BaseLogic
'name' => $params['name'],
'desc' => $params['desc'] ?? '',
'sort' => $params['sort'] ?? 0,
'data_scope' => self::normalizeDataScope($params['data_scope'] ?? null),
]);
$data = [];
@@ -90,6 +91,7 @@ class RoleLogic extends BaseLogic
'name' => $params['name'],
'desc' => $params['desc'] ?? '',
'sort' => $params['sort'] ?? 0,
'data_scope' => self::normalizeDataScope($params['data_scope'] ?? null),
]);
if (!empty($menuId)) {
@@ -142,13 +144,26 @@ class RoleLogic extends BaseLogic
*/
public static function detail(int $id): array
{
$detail = SystemRole::field('id,name,desc,sort')->find($id);
$detail = SystemRole::field('id,name,desc,sort,data_scope')->find($id);
$authList = $detail->roleMenuIndex()->select()->toArray();
$menuId = array_column($authList, 'menu_id');
$detail['menu_id'] = $menuId;
return $detail->toArray();
}
/**
* 规范化 data_scope:合法值 1-4,非法或缺省统一回退为 1(全部),保持与历史默认一致不增加风险。
*/
private static function normalizeDataScope($value): int
{
$v = (int) $value;
if ($v >= 1 && $v <= 4) {
return $v;
}
return 1;
}
/**
* @notes 角色数据
+26 -1
View File
@@ -730,7 +730,7 @@ class OrderLogic
* @param array $params order_type(-1全部已支付类型合计,0退款,1挂号费,2问诊费,3药品费用,4首付,5尾款,6其他), days(0今天,7,30)
* @return array
*/
public static function orderStats(array $params = [])
public static function orderStats(array $params = [], int $adminId = 0, ?array $adminInfo = null)
{
$allPaidOrderTypes = [1, 2, 3, 4, 5, 6];
$orderType = array_key_exists('order_type', $params) ? (int) $params['order_type'] : 1;
@@ -740,6 +740,11 @@ class OrderLogic
$orderTypeName = $orderType === -1 ? '全部(已支付)' : self::$orderTypeNames[$orderType];
$days = isset($params['days']) ? (int)$params['days'] : 7;
// 数据范围:当前用户可见 admin id 集合;null 表示全部
$visibleAdminIds = ($adminInfo !== null && $adminId > 0)
? \app\common\service\DataScope\DataScopeService::getVisibleAdminIds($adminId, $adminInfo)
: null;
$endTime = !empty($params['end_time']) ? strtotime($params['end_time']) : time();
if ($days === 0) {
$startTime = strtotime(date('Y-m-d'));
@@ -764,6 +769,13 @@ class OrderLogic
} else {
$query->where('order_type', $orderType)->where('status', 2);
}
if ($visibleAdminIds !== null) {
if ($visibleAdminIds === []) {
$query->whereRaw('0 = 1');
} else {
$query->whereIn('creator_id', $visibleAdminIds);
}
}
$orderRows = $query
->field('creator_id, count(*) as cnt, sum(amount) as total_amount')
->group('creator_id')
@@ -822,6 +834,13 @@ class OrderLogic
} else {
$todayQuery->where('order_type', $orderType)->where('status', 2);
}
if ($visibleAdminIds !== null) {
if ($visibleAdminIds === []) {
$todayQuery->whereRaw('0 = 1');
} else {
$todayQuery->whereIn('creator_id', $visibleAdminIds);
}
}
$todayRows = $todayQuery
->field('creator_id, count(*) as cnt, sum(amount) as total_amount')
->group('creator_id')
@@ -850,6 +869,9 @@ class OrderLogic
}
usort($todayRanking, fn($a, $b) => $b['count'] <=> $a['count']);
// 数据范围启用时(非 root/全部范围),无可见成员的部门不渲染
$hideEmptyDept = $visibleAdminIds !== null;
$deptData = [];
$assignedIds = [];
foreach ($depts as $deptId => $deptName) {
@@ -866,6 +888,9 @@ class OrderLogic
'amount' => number_format((float)($amounts[$adminId] ?? 0), 2, '.', ''),
];
}
if ($hideEmptyDept && $members === []) {
continue;
}
$deptTotal = array_sum(array_column($members, 'count'));
$deptAmount = array_sum(array_map(fn($m) => (float)$m['amount'], $members));
$deptData[] = [
@@ -2115,19 +2115,34 @@ class DiagnosisLogic extends BaseLogic
/**
* @notes 获取医助列表
* @param int $adminId 当前管理员 id(用于数据范围;0 表示不过滤)
* @param array|null $adminInfo 当前管理员信息(用于数据范围;null 表示不过滤)
* @return array
*/
public static function getAssistants()
public static function getAssistants(int $adminId = 0, ?array $adminInfo = null)
{
try {
// 这里不要用 Admin 模型查询,否则会触发其 append(role_id/dept_id/jobs_id)
// 每行再发多次查询,形成严重 N+1,统计页会被拖慢到十几秒。
$assistants = \think\facade\Db::name('admin')
$query = \think\facade\Db::name('admin')
->alias('a')
->join('admin_role ar', 'a.id = ar.admin_id')
->where('ar.role_id', 2)
->where('a.disable', 0)
->whereNull('a.delete_time')
->whereNull('a.delete_time');
// 数据范围:仅展示当前管理员可见的医助;null = 全部不过滤
if ($adminInfo !== null && $adminId > 0) {
$visibleIds = \app\common\service\DataScope\DataScopeService::getVisibleAdminIds($adminId, $adminInfo);
if ($visibleIds !== null) {
if ($visibleIds === []) {
return [];
}
$query->whereIn('a.id', $visibleIds);
}
}
$assistants = $query
->field(['a.id', 'a.name', 'a.account'])
->order('a.id', 'asc')
->distinct(true)
@@ -3105,10 +3120,15 @@ class DiagnosisLogic extends BaseLogic
* @param array $params start_time, end_time, days(可选,默认7)
* @return array
*/
public static function assistantDiagnosisStats(array $params = [])
public static function assistantDiagnosisStats(array $params = [], int $adminId = 0, ?array $adminInfo = null)
{
$days = isset($params['days']) ? (int)$params['days'] : 7;
// 数据范围:当前用户可见 admin id 集合;null 表示全部
$visibleAdminIds = ($adminInfo !== null && $adminId > 0)
? \app\common\service\DataScope\DataScopeService::getVisibleAdminIds($adminId, $adminInfo)
: null;
$endTime = !empty($params['end_time']) ? strtotime($params['end_time']) : time();
if ($days === 0) {
$startTime = strtotime(date('Y-m-d'));
@@ -3127,6 +3147,13 @@ class DiagnosisLogic extends BaseLogic
$assistantIds = \app\common\model\auth\AdminRole::where('role_id', 2)
->column('admin_id');
// 数据范围交集:仅展示「医助 ∩ 当前用户可见」
if ($visibleAdminIds !== null) {
$assistantIds = array_values(array_intersect(
array_map('intval', $assistantIds),
array_map('intval', $visibleAdminIds)
));
}
if (empty($assistantIds)) {
return [
'date_range' => [date('Y-m-d', $startTime), date('Y-m-d', $endTime)],
@@ -3192,6 +3219,9 @@ class DiagnosisLogic extends BaseLogic
usort($todayRanking, fn($a, $b) => $b['count'] <=> $a['count']);
$todayRanking = array_values(array_filter($todayRanking, fn($a) => $a['count'] > 0));
// 数据范围启用时(非 root/全部范围),无可见成员的部门不渲染
$hideEmptyDept = $visibleAdminIds !== null;
$deptData = [];
$assignedAdminIds = [];
foreach ($depts as $deptId => $deptName) {
@@ -3208,6 +3238,9 @@ class DiagnosisLogic extends BaseLogic
'count' => $cnt,
];
}
if ($hideEmptyDept && $assistants === []) {
continue;
}
$deptData[] = [
'dept_id' => (int)$deptId,
'dept_name' => $deptName,
@@ -29,6 +29,7 @@ class RoleValidate extends BaseValidate
'id' => 'require|checkRole',
'name' => 'require|max:64|unique:' . SystemRole::class . ',name',
'menu_id' => 'array',
'data_scope' => 'in:1,2,3,4',
];
protected $message = [
@@ -36,7 +37,8 @@ class RoleValidate extends BaseValidate
'name.require' => '请输入角色名称',
'name.max' => '角色名称最长为16个字符',
'name.unique' => '角色名称已存在',
'menu_id.array' => '权限格式错误'
'menu_id.array' => '权限格式错误',
'data_scope.in' => '数据范围取值不合法',
];
/**
@@ -47,7 +49,7 @@ class RoleValidate extends BaseValidate
*/
public function sceneAdd()
{
return $this->only(['name', 'menu_id']);
return $this->only(['name', 'menu_id', 'data_scope']);
}
/**