feat: rwfx 任务深链支持目的树闸门 + 任务↔对话(1:N)绑定
后端 - config.yaml 新增 task_deeplink 段:完整 URL(无 base)+ test/delete_test 两个独立测试开关 + mock 假数据(目的树 / delete 结果,可随时编辑) - 新增 TaskDeeplinkConfig 模型挂到 AppConfig;新增公开只读接口 GET /api/public/config/task-deeplink 暴露给前端(/api/public 已在鉴权白名单) 前端 - 登录深链 goPath=rwfx 进入「任务工作区」:左侧按 metadata.taskId 过滤的对话列表 (新建对话 / 编辑 / 下一步 / 清除任务记录)+ 右侧问答 - 新建对话(含首次进入)顶部展示目的树闸门:有数据弹覆盖确认→调 delete,success 才开始问答; 恢复历史对话不跑闸门 - 新建 thread 打 metadata.taskId 标签实现「一个任务挂多个对话」(1:N) - 地址/开关/假数据全部走后端配置,拿不到时用内置默认值兜底;test/delete_test=true 走假数据 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
5b610fd807
commit
a2d767e2db
15 files changed
+907
-36
No files matched your search
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* 任务深链(``goPath=rwfx``)的运行时配置:从后端公开只读接口
|
||||
* ``GET /api/public/config/task-deeplink`` 读取(地址、测试开关、测试假数据全在后端
|
||||
* ``config.yaml`` 的 ``task_deeplink`` 段,改完即时生效,前端无需重建)。
|
||||
*
|
||||
* 两种用法:
|
||||
* - 命令式(登录深链流程,非 React 环境):``fetchTaskDeeplinkConfig()`` 拉取并模块级缓存;
|
||||
* ``getCachedTaskDeeplinkConfig()`` 同步读缓存(拼跳转地址用)。
|
||||
* - React 组件:``useTaskDeeplinkConfig()``(见 [[task-purpose]] 的面板/列表)。
|
||||
*
|
||||
* 拉取失败 → 用内置默认值(``test=false`` + 真实 URL)兜底,功能不整体挂掉。
|
||||
*/
|
||||
|
||||
import { apiFetch } from "@/core/api/fetch-client";
|
||||
import { getBackendBaseURL } from "@/core/config";
|
||||
|
||||
export interface TaskDeeplinkConfig {
|
||||
/** 目的树查询走假数据。 */
|
||||
test: boolean;
|
||||
/** 删除接口走假数据(该接口未开发完时打开)。 */
|
||||
deleteTest: boolean;
|
||||
/** 目的树查询完整 URL(调用追加 ?taskId=)。 */
|
||||
purposeDetailUrl: string;
|
||||
/** 删除完整 URL(调用追加 ?taskId=)。 */
|
||||
deleteUrl: string;
|
||||
/** B 系统树形完整编辑页 URL(跳转追加 ?id=)。 */
|
||||
treemapEditUrl: string;
|
||||
/** B 系统树形下一步页 URL(跳转追加 ?id=)。 */
|
||||
treemapNextUrl: string;
|
||||
/** 测试假数据:purposeDetail = 原始 {state,msg,data};delete = {message,success}。 */
|
||||
mock: {
|
||||
purposeDetail?: unknown;
|
||||
delete?: { message?: string; success?: boolean };
|
||||
};
|
||||
}
|
||||
|
||||
/** 内置默认值(后端拿不到时兜底;真实 URL + 关闭测试)。 */
|
||||
export const DEFAULT_TASK_DEEPLINK_CONFIG: TaskDeeplinkConfig = {
|
||||
test: false,
|
||||
deleteTest: false,
|
||||
purposeDetailUrl: "https://ch1.b.uat.4.cn/consumer/taskAnalyseSearch/getTaskPurposeDetail",
|
||||
deleteUrl: "https://ch1.b.uat.4.cn/consumer/taskAnalyseSearch/delete",
|
||||
treemapEditUrl: "https://ch1.b.uat.4.cn/web/#/task/item-aicoh/step-mode-treemap-full-edit",
|
||||
treemapNextUrl: "https://ch1.b.uat.4.cn/web/#/task/item-aicoh/step-mode-treemap-full-next",
|
||||
mock: {},
|
||||
};
|
||||
|
||||
/** 后端 snake_case 响应 → 前端 camelCase;缺字段回退默认值。 */
|
||||
function normalize(body: unknown): TaskDeeplinkConfig {
|
||||
const o = body && typeof body === "object" ? (body as Record<string, unknown>) : {};
|
||||
const d = DEFAULT_TASK_DEEPLINK_CONFIG;
|
||||
const str = (v: unknown, fallback: string) =>
|
||||
typeof v === "string" && v.trim() ? v : fallback;
|
||||
const mock = o.mock && typeof o.mock === "object" ? (o.mock as Record<string, unknown>) : {};
|
||||
return {
|
||||
test: o.test === true,
|
||||
deleteTest: o.delete_test === true,
|
||||
purposeDetailUrl: str(o.purpose_detail_url, d.purposeDetailUrl),
|
||||
deleteUrl: str(o.delete_url, d.deleteUrl),
|
||||
treemapEditUrl: str(o.treemap_edit_url, d.treemapEditUrl),
|
||||
treemapNextUrl: str(o.treemap_next_url, d.treemapNextUrl),
|
||||
mock: {
|
||||
purposeDetail: mock.purpose_detail,
|
||||
delete:
|
||||
mock.delete && typeof mock.delete === "object"
|
||||
? (mock.delete as { message?: string; success?: boolean })
|
||||
: undefined,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
let cached: TaskDeeplinkConfig | null = null;
|
||||
let inflight: Promise<TaskDeeplinkConfig> | null = null;
|
||||
|
||||
/** 拉取(带模块级缓存 + 去重);失败回退默认值。 */
|
||||
export async function fetchTaskDeeplinkConfig(): Promise<TaskDeeplinkConfig> {
|
||||
if (cached) return cached;
|
||||
if (inflight) return inflight;
|
||||
inflight = (async () => {
|
||||
try {
|
||||
const res = await apiFetch(`${getBackendBaseURL()}/api/public/config/task-deeplink`);
|
||||
if (!res.ok) throw new Error(`task-deeplink config ${res.status}`);
|
||||
cached = normalize(await res.json());
|
||||
} catch {
|
||||
cached = { ...DEFAULT_TASK_DEEPLINK_CONFIG };
|
||||
} finally {
|
||||
inflight = null;
|
||||
}
|
||||
return cached;
|
||||
})();
|
||||
return inflight;
|
||||
}
|
||||
|
||||
/** 同步读缓存(未拉取过则返回默认值)。 */
|
||||
export function getCachedTaskDeeplinkConfig(): TaskDeeplinkConfig {
|
||||
return cached ?? DEFAULT_TASK_DEEPLINK_CONFIG;
|
||||
}
|
||||
|
||||
/** 在完整 URL(可能含 `#` 片段)后追加查询参数,供跳转/调用拼 id/taskId。 */
|
||||
export function appendQueryParam(url: string, key: string, value: string): string {
|
||||
if (!url) return "";
|
||||
const sep = url.includes("?") ? "&" : "?";
|
||||
return `${url}${sep}${key}=${encodeURIComponent(value)}`;
|
||||
}
|
||||
Reference in new issue
Block a user