Files
deerflow-code/frontend-web/src/core/auth/dashboard-input.ts
T
董宏凯andClaude Opus 4.8 32772b7448 feat(dashboard): 大屏绘制智能体独立页面(按 taskId 存取,不分权)
- 新增带导航+侧边栏的独立页面 DashboardAgentPage:按 taskId 取/存会话,
  有有效 report-json 直显大屏(DashboardApp),否则进生成过程(不自动跳)+输入框可改
- 后端 dashboard_sessions 存储(model/sql/__init__)+路由/by-task(GET/PUT 不分权)
  + deps/app/models 接线;新表靠 create_all 建,无迁移
- 扩展 useStep3Dashboard:buildInitialPrompt(输入任务JSON) + hydrateTranscript(恢复聊天记录)
- 抽出共享 DashboardChatPanel;report-template 增 buildDashboardPromptFromJson
- 输入 seam core/auth/dashboard-input:JSON 接口未就绪先用占位/演示数据兜底
- 路由 StrategyRoutes dashboard-agent + 侧边栏「问答管理→大屏绘制」
- 圆桌业务链卡片「关系图」改横向布局
- 顺带提交:AIWritingContext 登录切换处理、thread-access-error helper

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 14:26:27 +08:00

156 lines
6.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 大屏绘制智能体「独立页面」的**输入 JSON** 适配 seam。
*
* 设计意图:外部系统登录时**后续会**提供一个接口返回一份业务 JSON,本页让
* 智能体分析这份 JSON → 产出 report-json → 绘制大屏。
*
* ⚠️ 该接口现在还没开发好。本模块先做**占位适配**让页面尽快可用:
* - 配了 `VITE_DASHBOARD_INPUT_URL` → 按 taskId 拉取真实 JSON;
* - 没配 → 回退用登录深链already 暂存的任务详情(`login.copTaskDetail`)拼一份占位输入,
* 让智能体至少能基于任务名/内容产出一版可看的大屏。
*
* 接口就绪后:把 `fetchDashboardInputJson` 换成真实拉取;再按规划迁到「后台转换格式
* + 智能体校验」(校验仍由智能体做,不在后台硬抽)。
*/
import { requestCrudService } from "@/service/request-axios-service";
import { readCopTaskDetail } from "@/core/auth/task-deeplink";
/** 本页输入 JSON 暂存的 sessionStorage key(拉取一次后复用,避免重复请求)。 */
export const DASHBOARD_INPUT_STORAGE_KEY = "dashboard.inputJson";
/**
* 🧪 演示用模拟业务 JSON —— JSON 接口未就绪、也没有登录任务详情时的兜底输入。
* 让大屏页可立即跑通:智能体分析这份数据 → report-json → 渲染大屏。
* ⚠️ 接口就绪后删除本常量及其兜底引用。
*/
export const DEMO_DASHBOARD_INPUT = {
taskId: "DEMO-涉台舆情",
taskName: "2026 第一季度涉台舆情态势研判",
taskContent:
"汇总境内外多平台涉台舆情数据,研判热点叙事走势、关键行为体立场与传播渠道分布,识别风险并给出处置建议。",
overview:
"本季涉台舆情整体平稳但局部波动,'两岸交流''和平发展'类正向叙事占比上升,少数账号借军事议题制造紧张;境外智库与媒体联动放大特定框架。",
period: "2026-01-01 ~ 2026-03-31",
kpis: {
总信息量: 1284000,
日均增速: "12%",
正向占比: "58%",
负向占比: "19%",
识别风险点: 7,
覆盖平台: 12,
},
channels: [
{ platform: "微博", share: 34 },
{ platform: "境外社媒(X/FB)", share: 27 },
{ platform: "短视频", share: 18 },
{ platform: "新闻门户", share: 13 },
{ platform: "论坛/贴吧", share: 8 },
],
narratives: [
{ theme: "两岸交流与融合发展", sentiment: "正向", volume: 42, trend: "up" },
{ theme: "和平稳定·避战", sentiment: "正向", volume: 26, trend: "up" },
{ theme: "军事演训与安全", sentiment: "中性偏负", volume: 21, trend: "flat" },
{ theme: "外部势力介入", sentiment: "负向", volume: 11, trend: "down" },
],
actors: [
{ name: "主流官媒", stance: "正面引导", influence: 88, note: "议程设置与权威发布" },
{ name: "岛内民间团体", stance: "中性多元", influence: 64, note: "交流合作类内容活跃" },
{ name: "境外智库", stance: "负面框架", influence: 57, note: "联动媒体放大紧张叙事" },
{ name: "自媒体大V", stance: "立场分化", influence: 49, note: "情绪化传播、易被借势" },
],
risks: [
{ label: "军事议题被恶意剪辑放大", level: "high" },
{ label: "境外智库联动制造紧张框架", level: "high" },
{ label: "个别大V情绪化带节奏", level: "mid" },
{ label: "短视频谣言二次传播", level: "mid" },
{ label: "节假日期间信息量骤增", level: "low" },
],
timeline: [
{ phase: "数据汇聚", desc: "12 平台多源采集与清洗去重" },
{ phase: "叙事识别", desc: "聚类提炼核心叙事与情感倾向" },
{ phase: "行为体画像", desc: "关键账号立场与影响力建模" },
{ phase: "风险研判", desc: "识别高危议题与传播链路" },
{ phase: "处置建议", desc: "分级响应与正向引导预案" },
],
weeklyVolume: [
{ week: "W1", value: 86 },
{ week: "W2", value: 92 },
{ week: "W3", value: 110 },
{ week: "W4", value: 134 },
{ week: "W5", value: 121 },
{ week: "W6", value: 158 },
],
};
function inputBaseUrl(): string {
// TODO: 接口就绪后确认环境变量名/路径。占位:未配则走 copTaskDetail 回退。
const value = (import.meta.env as Record<string, unknown>).VITE_DASHBOARD_INPUT_URL;
return typeof value === "string" ? value.replace(/\/+$/, "") : "";
}
/** 读取暂存的输入 JSON。 */
export function readDashboardInputJson(): unknown | null {
try {
const raw = sessionStorage.getItem(DASHBOARD_INPUT_STORAGE_KEY);
return raw ? JSON.parse(raw) : null;
} catch {
return null;
}
}
/** 暂存输入 JSON(隐私模式/quota 异常静默失败)。 */
export function persistDashboardInputJson(data: unknown): void {
try {
sessionStorage.setItem(DASHBOARD_INPUT_STORAGE_KEY, JSON.stringify(data));
} catch {
/* noop */
}
}
/** 用登录暂存的任务详情拼一份占位输入(接口未就绪时的回退)。 */
function placeholderFromCopTask(taskId: string): unknown | null {
const detail = readCopTaskDetail();
if (!detail) return taskId ? { taskId } : null;
return {
taskId,
taskName: detail.taskName,
taskContent: detail.taskContent,
overview: detail.overview,
taskDirection: detail.taskDirection,
_note: "占位输入(真实业务 JSON 接口未就绪)",
};
}
/**
* 取本页的输入 JSON:优先暂存 → 真实接口(配了地址)→ copTaskDetail 占位。
* 任一步失败都静默退化,最终可能返回 null(调用方据此提示用户/不自动开跑)。
*/
export async function fetchDashboardInputJson(taskId: string): Promise<unknown | null> {
const cached = readDashboardInputJson();
if (cached) return cached;
const base = inputBaseUrl();
if (base && taskId) {
try {
// service 拦截器统一注入鉴权并返回 response.data(整包 body)。
const body = await requestCrudService.get(`${base}`, { taskId });
const data = body && typeof body === "object" ? (body as { data?: unknown }).data ?? body : body;
if (data) {
persistDashboardInputJson(data);
return data;
}
} catch {
/* 拉取失败 → 落占位 */
}
}
const placeholder = placeholderFromCopTask(taskId);
// 占位里有真实任务内容(来自登录详情)才用它;否则回退演示数据,保证页面能跑通看效果。
// ⚠️ 接口就绪后删除 DEMO 兜底。
const detail = readCopTaskDetail();
const resolved = detail ? placeholder : { ...DEMO_DASHBOARD_INPUT, taskId: taskId || DEMO_DASHBOARD_INPUT.taskId };
if (resolved) persistDashboardInputJson(resolved);
return resolved;
}