'channel']); $year = trim($_REQUEST['year'] ?? ''); if ($year !== '' && (!ctype_digit($year) || (int)$year < 2000 || (int)$year > 2100)) { $year = ''; } $pdo = DB::getInstance()->getPdo(); // 可选年份(企业/人员创建年份并集) $years = $pdo->query( "SELECT DISTINCT y FROM ( SELECT YEAR(created_at) AS y FROM companies WHERE is_active = 1 AND created_at IS NOT NULL UNION SELECT YEAR(created_at) AS y FROM persons WHERE is_active = 1 AND created_at IS NOT NULL ) t ORDER BY y DESC" )->fetchAll(PDO::FETCH_COLUMN); /** 单维度渠道分析 */ function channelAnalysis($pdo, $table, $year) { // REV-9:表名白名单,杜绝 SQL 插值 if (!in_array($table, ['companies', 'persons'], true)) { Response::error('参数错误', 400); } $where = 'is_active = 1 AND source_channel IS NOT NULL AND source_channel <> \'\''; $params = []; if ($year !== '') { $where .= ' AND YEAR(created_at) = ?'; $params[] = (int)$year; } $stmt = $pdo->prepare( "SELECT source_channel AS channel, COUNT(*) AS cnt FROM $table WHERE $where GROUP BY source_channel ORDER BY cnt DESC" ); $stmt->execute($params); $rows = $stmt->fetchAll(); $total = 0; foreach ($rows as $r) { $total += (int)$r['cnt']; } $result = []; foreach ($rows as $r) { $channel = $r['channel']; // 该渠道 source_detail TOP10 $detailStmt = $pdo->prepare( "SELECT COALESCE(NULLIF(TRIM(source_detail), ''), '(未填写)') AS source, COUNT(*) AS cnt FROM $table WHERE is_active = 1 AND source_channel = ?" . ($year !== '' ? ' AND YEAR(created_at) = ?' : '') . " GROUP BY source ORDER BY cnt DESC LIMIT 10" ); $dParams = [$channel]; if ($year !== '') { $dParams[] = (int)$year; } $detailStmt->execute($dParams); $result[] = [ 'channel' => $channel, 'count' => (int)$r['cnt'], 'percent' => $total > 0 ? round(((int)$r['cnt'] / $total) * 100, 1) : 0, 'details' => $detailStmt->fetchAll(), ]; } return $result; } Response::success([ 'years' => $years, 'company' => channelAnalysis($pdo, 'companies', $year), 'person' => channelAnalysis($pdo, 'persons', $year), ]);