user_id = $userId; $userLog->message = $message; $userLog->source_type = $sourceType; $userLog->source_id = $sourceId; $userLog->source_table = $sourceTable; $userLog->save(); return $userLog; } catch (\Exception $e) { Logger::exception('记录用户日志失败', $e, [ 'user_id' => $userId, 'message' => $message, 'source_type' => $sourceType, 'source_id' => $sourceId, 'source_table' => $sourceTable, ]); return null; } } /** * 批量记录用户日志 * * @param array $logs 日志数据数组 * @return bool */ public static function batchLog(array $logs): bool { if (empty($logs)) { return true; } try { DB::beginTransaction(); // 准备批量插入数据 $insertData = []; $now = now(); foreach ($logs as $logData) { $insertData[] = [ 'user_id' => $logData['user_id'], 'message' => $logData['message'], 'source_type' => $logData['source_type'] ?? null, 'source_id' => $logData['source_id'] ?? null, 'source_table' => $logData['source_table'] ?? null, 'original_time' => $logData['original_time'] ?? $now, 'collected_at' => $logData['collected_at'] ?? $now, 'created_at' => $now, ]; } // 使用批量插入,每次最多插入1000条记录 $chunks = array_chunk($insertData, 1000); foreach ($chunks as $chunk) { UserLog::insert($chunk); } DB::commit(); Logger::info('批量记录用户日志成功', [ 'logs_count' => count($logs), 'chunks_count' => count($chunks), ]); return true; } catch (\Exception $e) { DB::rollBack(); Logger::exception('批量记录用户日志失败', $e, [ 'logs_count' => count($logs), 'error' => $e->getMessage(), ]); return false; } } /** * 获取用户日志列表 * * @param int $userId 用户ID * @param int $page 页码 * @param int $pageSize 每页数量 * @param array $filters 过滤条件 * @return LengthAwarePaginator */ public static function getUserLogs( int $userId, int $page = 1, int $pageSize = 20, array $filters = [] ): LengthAwarePaginator { try { // 按原始时间排序,而不是创建时间,确保日志按业务发生时间显示 $query = UserLog::byUser($userId)->orderBy('original_time', 'desc'); // 获取用户的最后清理时间,只返回清理时间之后的日志 $lastClearTime = UserLogClearRecord::getUserLastClearTime($userId); if ($lastClearTime) { // 使用原始时间进行过滤 $query->where('original_time', '>', $lastClearTime); } // 应用过滤条件 if (!empty($filters['source_type'])) { $query->bySourceType($filters['source_type']); } if (!empty($filters['start_time']) && !empty($filters['end_time'])) { // 使用原始时间范围查询 $query->byOriginalTimeRange($filters['start_time'], $filters['end_time']); } if (!empty($filters['keyword'])) { $query->where('message', 'like', '%' . $filters['keyword'] . '%'); } return $query->paginate($pageSize, ['*'], 'page', $page); } catch (\Exception $e) { Logger::exception('获取用户日志列表失败', $e, [ 'user_id' => $userId, 'page' => $page, 'page_size' => $pageSize, 'filters' => $filters, ]); // 返回空的分页结果 return new LengthAwarePaginator([], 0, $pageSize, $page); } } /** * 清空用户日志(逻辑清理) * 不真实删除日志,而是记录清理时间,只返回清理时间之后的日志给用户 * 这样可以避免影响日志收集备份工作 * * @param int $userId 用户ID * @return bool */ public static function clearUserLogs(int $userId): bool { try { // 获取当前时间作为清理时间 $clearTime = now(); // 记录用户的清理时间 UserLogClearRecord::setUserClearTime($userId, $clearTime); Logger::info('清空用户日志成功(逻辑清理)', [ 'user_id' => $userId, 'clear_time' => $clearTime->toDateTimeString(), ]); return true; } catch (\Exception $e) { Logger::exception('清空用户日志失败', $e, [ 'user_id' => $userId, ]); return false; } } /** * 清理过期日志 * * @param int $days 保留天数 * @return int 删除的记录数 */ public static function cleanExpiredLogs(int $days = 30): int { try { $expiredDate = now()->subDays($days); $deletedCount = UserLog::where('created_at', '<', $expiredDate)->delete(); Logger::info('清理过期日志成功', [ 'days' => $days, 'expired_date' => $expiredDate->toDateTimeString(), 'deleted_count' => $deletedCount, ]); return $deletedCount; } catch (\Exception $e) { Logger::exception('清理过期日志失败', $e, [ 'days' => $days, ]); return 0; } } /** * 获取用户日志统计信息 * * @param int $userId 用户ID * @return array */ public static function getUserLogStats(int $userId): array { try { // 获取用户的最后清理时间,只统计清理时间之后的日志 $lastClearTime = UserLogClearRecord::getUserLastClearTime($userId); $baseQuery = UserLog::byUser($userId); if ($lastClearTime) { $baseQuery->where('original_time', '>', $lastClearTime); } $stats = [ 'total_count' => (clone $baseQuery)->count(), 'today_count' => (clone $baseQuery) ->whereDate('original_time', today()) ->count(), 'week_count' => (clone $baseQuery) ->where('original_time', '>=', now()->startOfWeek()) ->count(), 'month_count' => (clone $baseQuery) ->where('original_time', '>=', now()->startOfMonth()) ->count(), ]; // 按来源类型统计 $sourceTypeStats = (clone $baseQuery) ->select('source_type', DB::raw('count(*) as count')) ->whereNotNull('source_type') ->groupBy('source_type') ->pluck('count', 'source_type') ->toArray(); $stats['source_type_stats'] = $sourceTypeStats; return $stats; } catch (\Exception $e) { Logger::exception('获取用户日志统计信息失败', $e, [ 'user_id' => $userId, ]); return []; } } /** * 检查用户是否有日志记录 * * @param int $userId 用户ID * @return bool */ public static function hasUserLogs(int $userId): bool { try { $query = UserLog::byUser($userId); // 获取用户的最后清理时间,只检查清理时间之后的日志 $lastClearTime = UserLogClearRecord::getUserLastClearTime($userId); if ($lastClearTime) { $query->where('original_time', '>', $lastClearTime); } return $query->exists(); } catch (\Exception $e) { Logger::exception('检查用户日志记录失败', $e, [ 'user_id' => $userId, ]); return false; } } /** * 获取最新的用户日志 * * @param int $userId 用户ID * @param int $limit 数量限制 * @return \Illuminate\Database\Eloquent\Collection */ public static function getLatestUserLogs(int $userId, int $limit = 10) { try { // 按原始时间排序,而不是创建时间,确保日志按业务发生时间显示 $query = UserLog::byUser($userId)->orderBy('original_time', 'desc'); // 获取用户的最后清理时间,只返回清理时间之后的日志 $lastClearTime = UserLogClearRecord::getUserLastClearTime($userId); if ($lastClearTime) { $query->where('original_time', '>', $lastClearTime); } return $query->limit($limit)->get(); } catch (\Exception $e) { Logger::exception('获取最新用户日志失败', $e, [ 'user_id' => $userId, 'limit' => $limit, ]); return collect(); } } }