feat: filter approval records by status and decision source

This commit is contained in:
ji ruo committed 2026-09-16 17:24:15 +08:00
1 parent 80a085d157
commit a91c0a7223
8 files changed
+431 -47

No files matched your search

+12 -4
View File
@@ -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
View File
@@ -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;
+5
View File
@@ -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