Files
deerflow-code/frontend-web/src/core/notifications/api.ts
T
AdministratorandClaude Opus 4.8 df9c59e21c feat(y-log): 问答按 LLM 调用各记一条 + 全量补齐各 API 操作埋点
- 问答日志从「每轮 onFinish 一条」改为 useThreadStream onLangChainEvent
  监听 on_chat_model_end,每次大模型调用结束各上报一条(含子代理)
- 广覆盖各 API client 成功路径:查看(view)/查询(search)/新增(create)/
  编辑(edit)/删除(delete)/启停(enabled·disabled)/导入导出,补齐此前
  整模块零埋点与漏掉的单条详情 GET;跳过高频 typeahead/轮询/bootstrap

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 20:42:54 +08:00

82 lines
2.9 KiB
TypeScript

import { apiFetch } from "@/core/api";
import { getBackendBaseURL } from "@/core/config";
import { OptType, writeYLog } from "@/core/log/y-log";
import type {
NotificationItem,
NotificationListResponse,
NotificationUnreadCountResponse,
} from "./types";
async function parseDetail(res: Response, fallback: string): Promise<Error> {
const data = (await res.json().catch(() => ({}))) as { detail?: unknown };
const detail = typeof data.detail === "string" && data.detail ? data.detail : null;
return new Error(detail ?? `${fallback} (HTTP ${res.status})`);
}
export async function listNotifications(
options: { unreadOnly?: boolean; limit?: number } = {},
): Promise<NotificationItem[]> {
const params = new URLSearchParams();
if (options.unreadOnly) params.set("unread_only", "true");
if (options.limit) params.set("limit", String(options.limit));
const query = params.toString();
const url = `${getBackendBaseURL()}/api/notifications${query ? `?${query}` : ""}`;
const res = await apiFetch(url);
if (!res.ok) throw await parseDetail(res, "Failed to list notifications");
const data = (await res.json()) as NotificationListResponse;
writeYLog({
optType: OptType.search,
optDetail: "B1-CM助手-通知-查询通知列表",
url,
reqParam: options,
returnParam: { count: data.items.length },
});
return data.items;
}
export async function getUnreadCount(): Promise<number> {
const res = await apiFetch(`${getBackendBaseURL()}/api/notifications/unread_count`);
if (!res.ok) throw await parseDetail(res, "Failed to load unread count");
const data = (await res.json()) as NotificationUnreadCountResponse;
return data.unread;
}
export async function markNotificationRead(id: string): Promise<void> {
const url = `${getBackendBaseURL()}/api/notifications/${encodeURIComponent(id)}/read`;
const res = await apiFetch(url, { method: "POST" });
if (!res.ok) throw await parseDetail(res, "Failed to mark notification read");
writeYLog({
optType: OptType.edit,
optDetail: "B1-CM助手-通知-标记已读",
url,
reqParam: { id },
});
}
export async function markAllNotificationsRead(): Promise<number> {
const url = `${getBackendBaseURL()}/api/notifications/read_all`;
const res = await apiFetch(url, { method: "POST" });
if (!res.ok) throw await parseDetail(res, "Failed to mark all read");
const data = (await res.json()) as { updated: number };
writeYLog({
optType: OptType.edit,
optDetail: "B1-CM助手-通知-全部标记已读",
url,
returnParam: data,
});
return data.updated;
}
export async function deleteNotification(id: string): Promise<void> {
const url = `${getBackendBaseURL()}/api/notifications/${encodeURIComponent(id)}`;
const res = await apiFetch(url, { method: "DELETE" });
if (!res.ok) throw await parseDetail(res, "Failed to delete notification");
writeYLog({
optType: OptType.delete,
optDetail: "B1-CM助手-通知-删除通知",
url,
reqParam: { id },
});
}