['nullable', 'integer', 'min:1'], 'limit' => ['nullable', 'integer', 'min:1', 'max:200'], 'language' => ['nullable', Rule::in(array_keys(LocalizedContent::LANGUAGES))], 'operator_name' => ['nullable', 'string', 'max:100'], 'resource_id' => ['nullable', 'integer', 'min:1'], 'start_date' => ['nullable', 'required_with:end_date', 'date_format:Y-m-d'], 'end_date' => ['nullable', 'required_with:start_date', 'date_format:Y-m-d', 'after_or_equal:start_date'], ])->validate(); $query = DB::table('operation_audits as a')->where('a.resource_type', $type); if ($scope) $scope($query); if (isset($params['operator_name']) && $params['operator_name'] !== '') $query->where('a.operator_name', 'like', '%' . $params['operator_name'] . '%'); if (!empty($params['resource_id'])) $query->where('a.resource_id', $params['resource_id']); if (!empty($params['start_date'])) $query->where('a.created_at', '>=', $params['start_date'] . ' 00:00:00')->where('a.created_at', '<=', $params['end_date'] . ' 23:59:59'); if (!empty($params['language'])) { $language = $params['language']; $query->where(function ($q) use ($language) { $q->whereNotNull('a.changes->before->translations->' . $language)->orWhereNotNull('a.changes->after->translations->' . $language); }); } $total = (clone $query)->count(); $rows = $query->select('a.*')->orderByDesc('a.created_at')->orderByDesc('a.id') ->forPage((int) ($params['page'] ?? 1), (int) ($params['limit'] ?? 20))->get(); $items = []; foreach ($rows as $row) { $change = json_decode($row->changes, true, 512, JSON_THROW_ON_ERROR); $before = $change['before'] ?? []; $after = $change['after'] ?? []; $translations = array_replace($before['translations'] ?? [], $after['translations'] ?? []); $titles = array_map(fn ($block) => $block['title'], $translations); $old = []; $new = []; foreach (array_unique(array_merge(array_keys($before), array_keys($after))) as $field) { if (in_array($field, ['id', 'created_at', 'updated_at', 'request_hash', 'request_id'], true)) continue; $a = $before[$field] ?? null; $b = $after[$field] ?? null; if ($field === 'translations' && !empty($params['language'])) { $a = isset($a[$params['language']]) ? [$params['language'] => $a[$params['language']]] : null; $b = isset($b[$params['language']]) ? [$params['language'] => $b[$params['language']]] : null; } if ($a !== $b) { $old[$field] = $a; $new[$field] = $b; } } $recipientType = $after['recipient_type'] ?? $before['recipient_type'] ?? 'all_users'; $items[] = ['id' => (int) $row->id, 'resource_id' => (int) $row->resource_id, 'created_at' => $row->created_at, 'operator_name' => $row->operator_name, 'action' => $row->action, 'languages' => array_keys($translations), 'titles' => (object) $titles, 'recipient_type' => $recipientType, 'recipient_label' => $type === 'announcement' ? '所有人' : (SiteMessageService::TARGETS[$recipientType] ?? ''), 'old_values' => (object) $old, 'new_values' => (object) $new]; } return ['total' => $total, 'data' => $items]; } }