params['doctor_id'] ?? ''; $timeType = $this->params['time_type'] ?? 'today'; $startDate = $this->params['start_date'] ?? ''; $endDate = $this->params['end_date'] ?? ''; // 计算时间范围 list($startDate, $endDate, $timeRangeText) = $this->getTimeRange($timeType, $startDate, $endDate); // 获取医生列表(role_id=1 表示医生角色) $doctorAdminIds = \app\common\model\auth\AdminRole::where('role_id', 1)->column('admin_id'); if (empty($doctorAdminIds)) { return []; } // 查询1:获取预约统计数据(快速查询,只查appointment表) $statisticsData = \think\facade\Db::name('doctor_appointment') ->field([ 'doctor_id', 'COUNT(*) as total_count', 'SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) as registered_count', 'SUM(CASE WHEN status = 3 THEN 1 ELSE 0 END) as completed_count', 'SUM(CASE WHEN status = 4 THEN 1 ELSE 0 END) as missed_count', 'SUM(CASE WHEN status = 2 THEN 1 ELSE 0 END) as cancelled_count' ]) ->whereIn('doctor_id', $doctorAdminIds) ->where('appointment_date', '>=', $startDate) ->where('appointment_date', '<=', $endDate); if ($doctorId) { $statisticsData->where('doctor_id', $doctorId); } $statisticsData = $statisticsData->group('doctor_id')->select()->toArray(); // 将统计数据按 doctor_id 索引 $statsMap = []; foreach ($statisticsData as $stat) { $statsMap[$stat['doctor_id']] = $stat; } // 获取医生信息 $doctorQuery = Admin::field('id,name')->whereIn('id', $doctorAdminIds); if ($doctorId) { $doctorQuery->where('id', $doctorId); } $doctors = $doctorQuery->select()->toArray(); // 查询2:获取部门统计信息(通过assistant_id直接关联,按部门分组统计数量) $doctorIdsWithAppointments = array_keys($statsMap); $deptStatsMap = []; $channelStatsMap = []; if (!empty($doctorIdsWithAppointments)) { // 通过 assistant_id → admin_dept → dept 获取部门统计 $deptStats = \think\facade\Db::name('doctor_appointment') ->alias('apt') ->leftJoin('admin_dept ad', 'apt.assistant_id = ad.admin_id') ->leftJoin('dept dept', 'ad.dept_id = dept.id') ->field('apt.doctor_id, dept.name as dept_name, COUNT(*) as dept_count') ->whereIn('apt.doctor_id', $doctorIdsWithAppointments) ->where('apt.appointment_date', '>=', $startDate) ->where('apt.appointment_date', '<=', $endDate) ->whereNotNull('dept.id') ->group('apt.doctor_id, dept.id') ->select() ->toArray(); // 组装部门统计数据:医生ID => "部门1(数量1) 部门2(数量2)" foreach ($deptStats as $stat) { if (!isset($deptStatsMap[$stat['doctor_id']])) { $deptStatsMap[$stat['doctor_id']] = []; } $deptStatsMap[$stat['doctor_id']][] = $stat['dept_name'] . '(' . $stat['dept_count'] . ')'; } // 获取渠道字典数据 $channelDict = \think\facade\Db::name('dict_data') ->where('type_value', 'channels') ->column('name', 'value'); // 获取渠道统计信息(channels字段,按渠道分组统计数量) $channelStats = \think\facade\Db::name('doctor_appointment') ->field('doctor_id, channels, COUNT(*) as channel_count') ->whereIn('doctor_id', $doctorIdsWithAppointments) ->where('appointment_date', '>=', $startDate) ->where('appointment_date', '<=', $endDate) ->whereNotNull('channels') ->where('channels', '<>', '') ->group('doctor_id, channels') ->select() ->toArray(); // 组装渠道统计数据:医生ID => "渠道名称1(数量1) 渠道名称2(数量2)" foreach ($channelStats as $stat) { if (!isset($channelStatsMap[$stat['doctor_id']])) { $channelStatsMap[$stat['doctor_id']] = []; } $channelName = $channelDict[$stat['channels']] ?? $stat['channels']; $channelStatsMap[$stat['doctor_id']][] = $channelName . '(' . $stat['channel_count'] . ')'; } } // 组装结果 $result = []; foreach ($doctors as $doctor) { $stat = $statsMap[$doctor['id']] ?? [ 'total_count' => 0, 'registered_count' => 0, 'completed_count' => 0, 'missed_count' => 0, 'cancelled_count' => 0 ]; // 计算完成率 $completionRate = $stat['total_count'] > 0 ? round(($stat['completed_count'] / $stat['total_count']) * 100, 2) : 0; $result[] = [ 'doctor_id' => $doctor['id'], 'doctor_name' => $doctor['name'], 'total_count' => (int)$stat['total_count'], 'registered_count' => (int)$stat['registered_count'], 'completed_count' => (int)$stat['completed_count'], 'missed_count' => (int)$stat['missed_count'], 'cancelled_count' => (int)$stat['cancelled_count'], 'completion_rate' => $completionRate, 'dept_stats' => isset($deptStatsMap[$doctor['id']]) ? implode(' ', $deptStatsMap[$doctor['id']]) : '未分配', 'channel_stats' => isset($channelStatsMap[$doctor['id']]) ? implode(' ', $channelStatsMap[$doctor['id']]) : '未设置', 'time_range' => $timeRangeText ]; } return $result; } /** * @notes 获取时间范围 * @param string $timeType * @param string $customStartDate * @param string $customEndDate * @return array */ private function getTimeRange($timeType, $customStartDate, $customEndDate) { $today = date('Y-m-d'); switch ($timeType) { case 'today': $startDate = $today; $endDate = $today; $timeRangeText = '今天 (' . $today . ')'; break; case 'week': $startDate = date('Y-m-d', strtotime('-6 days')); $endDate = $today; $timeRangeText = '最近7天 (' . $startDate . ' 至 ' . $endDate . ')'; break; case 'month': $startDate = date('Y-m-d', strtotime('-29 days')); $endDate = $today; $timeRangeText = '最近30天 (' . $startDate . ' 至 ' . $endDate . ')'; break; case 'custom': if ($customStartDate && $customEndDate) { $startDate = $customStartDate; $endDate = $customEndDate; $timeRangeText = '自定义 (' . $startDate . ' 至 ' . $endDate . ')'; } else { $startDate = $today; $endDate = $today; $timeRangeText = '今天 (' . $today . ')'; } break; default: $startDate = $today; $endDate = $today; $timeRangeText = '今天 (' . $today . ')'; } return [$startDate, $endDate, $timeRangeText]; } /** * @notes 获取数量 * @return int */ public function count(): int { return count($this->lists()); } }