1. Y-Log 上报 optDetail 前缀 B1-CM助手 → CM助手-全量(全量替换 44 个前端文件 + 文档) 2. 知识库入库 nodeName 改为只取 md5(标题).md(去掉时间戳),同一篇 md 每次入库一致,避免重复记录 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
82 lines
3.0 KiB
TypeScript
82 lines
3.0 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: "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: "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: "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: "CM助手-全量-通知-删除通知",
|
|
url,
|
|
reqParam: { id },
|
|
});
|
|
}
|