补齐后台埋点导出和任务事件选择

后台埋点查询支持日期筛选和导出全部模式

新增后台真实 event key 列表接口

任务配置页改用真实 user 埋点 key 候选

更新后台埋点查询文档和定向测试
This commit is contained in:
2026-06-26 16:49:33 +08:00
parent 607042a250
commit a8c3f8cf0f
10 changed files with 613 additions and 88 deletions
@@ -1,22 +1,25 @@
import {ChevronDown, PowerOff, RefreshCcw, Save} from 'lucide-react';
import {FormEvent, useEffect, useMemo, useState} from 'react';
import { ChevronDown, PowerOff, RefreshCcw, Save } from 'lucide-react';
import { FormEvent, useEffect, useMemo, useState } from 'react';
import {
disableProfileTaskConfig,
listAdminTrackingEventKeys,
listProfileTaskConfigs,
upsertProfileTaskConfig,
} from '../api/adminApiClient';
import type {
AdminTrackingEventKeyPayload,
ProfileTaskConfigAdminResponse,
ProfileTaskCycle,
TrackingScopeKind,
} from '../api/adminApiTypes';
import {useAdminWriteConfirm} from '../components/useAdminWriteConfirm';
import { useAdminWriteConfirm } from '../components/useAdminWriteConfirm';
import {
filterAdminProfileTaskTrackingEventDefinitions,
buildAdminTrackingEventKeyOptions,
filterAdminTrackingEventKeyOptions,
findAdminTrackingEventDefinition,
} from '../config/trackingEventDefinitions';
import {handlePageError} from './pageUtils';
import { handlePageError } from './pageUtils';
interface AdminTaskConfigPageProps {
token: string;
@@ -25,8 +28,8 @@ interface AdminTaskConfigPageProps {
onResultChange: (result: ProfileTaskConfigAdminResponse) => void;
}
const taskCycles: Array<{value: ProfileTaskCycle; label: string}> = [
{value: 'daily', label: '每日'},
const taskCycles: Array<{ value: ProfileTaskCycle; label: string }> = [
{ value: 'daily', label: '每日' },
];
const profileTaskScopeKind = 'user' satisfies TrackingScopeKind;
@@ -43,6 +46,9 @@ export function AdminTaskConfigPage({
const [description, setDescription] = useState('');
const [eventKey, setEventKey] = useState('daily_login');
const [eventKeySearch, setEventKeySearch] = useState('每日登录');
const [eventKeys, setEventKeys] = useState<AdminTrackingEventKeyPayload[]>(
[],
);
const [isEventKeyPickerOpen, setIsEventKeyPickerOpen] = useState(false);
const [cycle, setCycle] = useState<ProfileTaskCycle>('daily');
const [threshold, setThreshold] = useState('1');
@@ -56,22 +62,53 @@ export function AdminTaskConfigPage({
const [isSaving, setIsSaving] = useState(false);
const [isDisabling, setIsDisabling] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const {confirmWrite, confirmDialog} = useAdminWriteConfirm();
const { confirmWrite, confirmDialog } = useAdminWriteConfirm();
useEffect(() => {
void refreshTaskConfigs();
void refreshTrackingEventKeys();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [token]);
const selectedEventDefinition = useMemo(
() => findAdminTrackingEventDefinition(eventKey),
[eventKey],
const eventKeyOptions = useMemo(
() =>
buildAdminTrackingEventKeyOptions(eventKeys).filter((option) =>
option.scopeKinds.includes(profileTaskScopeKind),
),
[eventKeys],
);
const selectedEventOption = useMemo(
() =>
eventKeyOptions.find((option) => option.key === eventKey) ??
findAdminTrackingEventDefinition(eventKey),
[eventKey, eventKeyOptions],
);
const filteredEventDefinitions = useMemo(
() => filterAdminProfileTaskTrackingEventDefinitions(eventKeySearch),
[eventKeySearch],
() => filterAdminTrackingEventKeyOptions(eventKeyOptions, eventKeySearch),
[eventKeyOptions, eventKeySearch],
);
useEffect(() => {
if (eventKeySearch !== eventKey) {
return;
}
const nextOption =
eventKeyOptions.find((option) => option.key === eventKey) ??
findAdminTrackingEventDefinition(eventKey);
if (nextOption) {
setEventKeySearch(nextOption.title);
}
}, [eventKey, eventKeyOptions, eventKeySearch]);
async function refreshTrackingEventKeys() {
try {
const response = await listAdminTrackingEventKeys(token);
setEventKeys(response.eventKeys);
} catch {
setEventKeys([]);
}
}
async function refreshTaskConfigs() {
setIsLoading(true);
setListErrorMessage('');
@@ -182,16 +219,20 @@ export function AdminTaskConfigPage({
setSortOrder(String(entry.sortOrder));
setEnabled(entry.enabled);
setDisableTaskId(entry.taskId);
const nextDefinition = findAdminTrackingEventDefinition(entry.eventKey);
setEventKeySearch(nextDefinition?.title ?? entry.eventKey);
const nextOption =
eventKeyOptions.find((option) => option.key === entry.eventKey) ??
findAdminTrackingEventDefinition(entry.eventKey);
setEventKeySearch(nextOption?.title ?? entry.eventKey);
setIsEventKeyPickerOpen(false);
}
function selectEventKey(nextEventKey: string) {
const nextDefinition = findAdminTrackingEventDefinition(nextEventKey);
const nextOption =
eventKeyOptions.find((option) => option.key === nextEventKey) ??
findAdminTrackingEventDefinition(nextEventKey);
setEventKey(nextEventKey);
if (nextDefinition) {
setEventKeySearch(nextDefinition.title);
if (nextOption) {
setEventKeySearch(nextOption.title);
} else {
setEventKeySearch(nextEventKey);
}
@@ -322,9 +363,9 @@ export function AdminTaskConfigPage({
</div>
) : null}
</div>
{selectedEventDefinition ? (
{selectedEventOption ? (
<small className="admin-field-note">
{selectedEventDefinition.remark}
{selectedEventOption.remark}
</small>
) : (
<small className="admin-field-note">