feat: filter approval records by status and decision source
This commit is contained in:
1 parent
80a085d157
commit
a91c0a7223
8 files changed
+431
-47
No files matched your search
+12
-4
@@ -42,6 +42,7 @@ import type {
|
||||
FindingTraffic,
|
||||
FindingTrafficDetail,
|
||||
IntentAsset,
|
||||
InterceptApprovalFilter,
|
||||
InterceptApprovalRow,
|
||||
InterceptDetail,
|
||||
InterceptPending,
|
||||
@@ -204,6 +205,13 @@ function findingFilterParams(q: Omit<FindingQuery, "page" | "pageSize">): URLSea
|
||||
return p;
|
||||
}
|
||||
|
||||
function interceptPageQuery(page: number, size: number, filter: InterceptApprovalFilter) {
|
||||
const query = new URLSearchParams({ page: String(page), size: String(size) });
|
||||
if (filter.status) query.set("status", filter.status);
|
||||
if (filter.decision_source) query.set("decision_source", filter.decision_source);
|
||||
return query.toString();
|
||||
}
|
||||
|
||||
export const api = {
|
||||
// 后端应用版本号(release 时由 ldflags 注入,默认 "dev")。
|
||||
health: () => get<{ ok: boolean; service: string; version: string }>("/health"),
|
||||
@@ -1065,8 +1073,8 @@ export const api = {
|
||||
get<import("@/lib/types").InterceptExecution>(`/intercept/history/${id}/execution${conversationId ? `?conversation=${conversationId}` : ""}`),
|
||||
interceptDetail: (id: number) => get<InterceptDetail>(`/intercept/history/${id}`),
|
||||
interceptHistory: () => get<{ items: InterceptApprovalRow[] }>("/intercept/history").then((r) => arr(r.items)),
|
||||
interceptHistoryPage: (page = 1, size = 20) =>
|
||||
get<{ items: InterceptApprovalRow[]; total?: number }>(`/intercept/history?page=${page}&size=${size}`).then(
|
||||
interceptHistoryPage: (page = 1, size = 20, filter: InterceptApprovalFilter = {}) =>
|
||||
get<{ items: InterceptApprovalRow[]; total?: number }>(`/intercept/history?${interceptPageQuery(page, size, filter)}`).then(
|
||||
(r) => ({
|
||||
items: arr(r.items),
|
||||
total: r.total ?? r.items?.length ?? 0,
|
||||
@@ -1074,9 +1082,9 @@ export const api = {
|
||||
),
|
||||
interceptTask: (taskId: string) =>
|
||||
get<{ items: InterceptApprovalRow[] }>(`/intercept/task/${taskId}`).then((r) => arr(r.items)),
|
||||
interceptTaskPage: (taskId: string, page = 1, size = 20) =>
|
||||
interceptTaskPage: (taskId: string, page = 1, size = 20, filter: InterceptApprovalFilter = {}) =>
|
||||
get<{ items: InterceptApprovalRow[]; total?: number }>(
|
||||
`/intercept/task/${encodeURIComponent(taskId)}?page=${page}&size=${size}`,
|
||||
`/intercept/task/${encodeURIComponent(taskId)}?${interceptPageQuery(page, size, filter)}`,
|
||||
).then((r) => ({
|
||||
items: arr(r.items),
|
||||
total: r.total ?? r.items?.length ?? 0,
|
||||
|
||||
+11
-15
@@ -2076,21 +2076,17 @@ function route(m: string, path: string, seg: string[], q: URLSearchParams, b: Re
|
||||
}
|
||||
if (seg[0] === "intercept" && seg[1] === "pending" && seg.length === 3 && m === "GET")
|
||||
return mockInterceptPending.find((p) => p.id === Number(seg[2])) ?? null;
|
||||
if (path === "/intercept/history") {
|
||||
if (!q.has("page") && !q.has("size")) return { items: mockInterceptHistory, total: mockInterceptHistory.length };
|
||||
const page = Math.max(1, Number(q.get("page")) || 1);
|
||||
const size = Math.min(100, Math.max(1, Number(q.get("size")) || 20));
|
||||
const offset = (page - 1) * size;
|
||||
return {
|
||||
items: mockInterceptHistory.slice(offset, offset + size),
|
||||
total: mockInterceptHistory.length,
|
||||
page,
|
||||
page_size: size,
|
||||
};
|
||||
}
|
||||
if (seg[0] === "intercept" && seg[1] === "task") {
|
||||
const filtered = mockInterceptHistory.filter((r) => r.task_id === seg[2]);
|
||||
if (!q.has("page") && !q.has("size")) return { items: filtered, total: filtered.length };
|
||||
if (path === "/intercept/history" || (seg[0] === "intercept" && seg[1] === "task")) {
|
||||
const status = q.get("status") || "";
|
||||
const decisionSource = q.get("decision_source") || "";
|
||||
if (status && !["pending", "allowed", "denied", "timeout"].includes(status)) throw new Error("无效审批状态");
|
||||
if (decisionSource && !["model", "rule", "unknown"].includes(decisionSource)) throw new Error("无效判定来源");
|
||||
const filtered = mockInterceptHistory.filter((row) => {
|
||||
const source = row.decision_source || (row.rule_id ? "rule" : row.reason?.startsWith("[模型]") ? "model" : "unknown");
|
||||
return (seg[1] !== "task" || row.task_id === decodeURIComponent(seg[2])) &&
|
||||
(!status || row.status === status) && (!decisionSource || source === decisionSource);
|
||||
});
|
||||
if (!q.has("page") && !q.has("size") && !status && !decisionSource) return { items: filtered, total: filtered.length };
|
||||
const page = Math.max(1, Number(q.get("page")) || 1);
|
||||
const size = Math.min(100, Math.max(1, Number(q.get("size")) || 20));
|
||||
const offset = (page - 1) * size;
|
||||
|
||||
@@ -1195,6 +1195,11 @@ export interface JudgeConfig {
|
||||
ask_timeout_action: "allow" | "deny"; // 审批超时后的默认动作
|
||||
}
|
||||
|
||||
export interface InterceptApprovalFilter {
|
||||
status?: InterceptPending["status"];
|
||||
decision_source?: "rule" | "model" | "unknown";
|
||||
}
|
||||
|
||||
// InterceptApprovalRow enriches InterceptPending with conversation/task and rule context.
|
||||
export interface InterceptApprovalRow extends InterceptPending {
|
||||
conv_title: string; // "" if no linked conversation
|
||||
|
||||
Reference in new issue
Block a user