diff --git a/tests/test_platforms.py b/tests/test_platforms.py index 355ab54..43235eb 100644 --- a/tests/test_platforms.py +++ b/tests/test_platforms.py @@ -177,6 +177,29 @@ class TestTaskCreationGuard: tasks = (await client.get("/api/monitor/tasks")).json()["tasks"] assert {t["platform"] for t in tasks} == {"xhs"} + @pytest.mark.asyncio + async def test_an_explicit_platform_is_honoured_on_create(self, client): + """建任务时给的平台必须落到那个平台。 + + 缺省值是小红的(接口早期的兼容行为),所以「在抖音页面建任务」如果没有显式 + 带上 platform,就会安安静静地变成一个小红书任务 —— 不报错,只是出现在另一 + 个列表里。前端那半边已经改成必传;这里守住后端这一半:给了就必须用。 + """ + sec_uid = "MS4wLjABAAAATJPY7LAlaa5X-c8uNdWkvz0jUGgpw4eeXIwu_8BhvqE" + created = await client.post( + "/api/monitor/tasks", + json={"name": "抖音任务", "mode": "creator", "platform": "dy", "targets": [sec_uid]}, + ) + assert created.status_code == 201 + + assert (await client.get("/api/monitor/tasks", params={"platform": "xhs"})).json()[ + "tasks" + ] == [] + dy_tasks = ( + await client.get("/api/monitor/tasks", params={"platform": "dy"}) + ).json()["tasks"] + assert [t["name"] for t in dy_tasks] == ["抖音任务"] + class TestPlatformScoping: async def _seed_two_platforms(self, client): diff --git a/webui/src/components/monitor/TaskCard.tsx b/webui/src/components/monitor/TaskCard.tsx index e370813..43e453d 100644 --- a/webui/src/components/monitor/TaskCard.tsx +++ b/webui/src/components/monitor/TaskCard.tsx @@ -3,6 +3,7 @@ import { Bell, CalendarClock, Pencil, Play, Trash2 } from 'lucide-react' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { useDeleteTask, useRunTaskNow, useUpdateTask } from '@/hooks/useMonitor' +import { useCurrentPlatform } from '@/hooks/usePlatform' import { formatRelative } from '@/lib/monitorFormat' import type { MonitorTask } from '@/types/monitor' @@ -46,6 +47,12 @@ export function TaskCard({ task, selected, onSelect, onEdit }: TaskCardProps) { const updateTask = useUpdateTask() const deleteTask = useDeleteTask() const runNow = useRunTaskNow() + // 按**任务自己的**平台取措辞,而不是当前平台 —— 卡片未必只出现在同平台的列表里。 + // 抖音管它们叫「作品」,小红书叫「笔记」,写死一个对另一个就是错的。 + const { platforms } = useCurrentPlatform() + const noteNoun = + platforms.find((entry) => entry.value === task.platform)?.target_hints?.note_label ?? + '笔记' // A suspected cookie failure surfaces here so it is visible without opening // the event feed. @@ -65,7 +72,7 @@ export function TaskCard({ task, selected, onSelect, onEdit }: TaskCardProps) {
{task.name} - {task.mode === 'creator' ? '博主' : '笔记'} + {task.mode === 'creator' ? '博主' : noteNoun} {STATUS_LABEL[task.last_status] ?? task.last_status} diff --git a/webui/src/components/monitor/TaskEditorDialog.tsx b/webui/src/components/monitor/TaskEditorDialog.tsx index 72b48df..056ed4d 100644 --- a/webui/src/components/monitor/TaskEditorDialog.tsx +++ b/webui/src/components/monitor/TaskEditorDialog.tsx @@ -109,7 +109,7 @@ export function TaskEditorDialog({ open, onOpenChange, task }: TaskEditorDialogP const { data: settings } = useSettings() // 示例链接和措辞都由服务端的能力矩阵给 —— 前端不自己判断平台,否则加一个平台 // 就要改这里一次,而且很容易漏。 - const { capability } = useCurrentPlatform() + const { capability, platform } = useCurrentPlatform() const hints = capability?.target_hints const [name, setName] = useState('') @@ -179,6 +179,9 @@ export function TaskEditorDialog({ open, onOpenChange, task }: TaskEditorDialogP const handleSubmit = () => { const payload: TaskCreatePayload = { name: name.trim(), + // 当前平台。漏掉这一项,后端会退回小红书 —— 表现是「在抖音页面建的任务 + // 跑到小红书列表里去了」,而且不报任何错。 + platform, mode, interval_minutes: Number(intervalMinutes), schedule_mode: scheduleMode, @@ -199,7 +202,11 @@ export function TaskEditorDialog({ open, onOpenChange, task }: TaskEditorDialogP const done = () => onOpenChange(false) if (isEdit && task) { - updateTask.mutate({ id: task.id, payload }, { onSuccess: done }) + // 平台创建后不可更改,更新请求里就不带它了 —— 带着会让「平台能被改」这件事 + // 看起来像是真的。 + const updatePayload: Partial = { ...payload } + delete updatePayload.platform + updateTask.mutate({ id: task.id, payload: updatePayload }, { onSuccess: done }) } else { createTask.mutate(payload, { onSuccess: done }) } @@ -241,7 +248,7 @@ export function TaskEditorDialog({ open, onOpenChange, task }: TaskEditorDialogP 博主(监控其作品) - 笔记(批量监控指定内容) + {`${hints?.note_label ?? '笔记'}(批量监控指定内容)`} {isEdit && ( @@ -432,7 +439,7 @@ export function TaskEditorDialog({ open, onOpenChange, task }: TaskEditorDialogP ) : (

- 笔记模式下此项不生效:你列出的每个链接都会被逐条抓取。 + {hints?.note_label ?? '笔记'}模式下此项不生效:你列出的每个链接都会被逐条抓取。

)}
diff --git a/webui/src/types/monitor.ts b/webui/src/types/monitor.ts index 49a3815..bf3ae0e 100644 --- a/webui/src/types/monitor.ts +++ b/webui/src/types/monitor.ts @@ -250,6 +250,13 @@ export interface MonitorOverview { export interface TaskCreatePayload { name: string + /** + * 任务归属的平台。 + * + * **必须显式带上。** 后端在缺省时会退回小红书 —— 那是接口早期的兼容行为, + * 于是「在抖音页面上建任务」会安安静静地建出一个小红书任务(遇到过)。 + */ + platform: string mode: MonitorMode interval_minutes: number schedule_mode: ScheduleMode