import axios from "axios"; const api = axios.create({ baseURL: "/v1/admin" }); export type KgProjectContext = { tenantId: string; projectId: string; graphName: string; }; export const PROJECT_CONTEXT_KEY = "znkg.currentProjectContext"; export const DEFAULT_PROJECT_CONTEXT: KgProjectContext = { tenantId: "CityGraph-new2", projectId: "CityGraph-new2", graphName: "guiyang_new2", }; const PROJECT_DISPLAY_NAMES: Record = { "CityGraph-new2": "城市图谱", guiyang_new2: "贵阳城市图谱", city_graph: "城市图谱", travel_agency: "旅行社", travel_graph: "旅行社线路制定", travel_fixed_route_item: "旅行社固定线路资源图谱", travel_agency_2_0_test: "旅行社2.0测试", baixinghui_travel_agency: "百姓惠旅行社", }; const GRAPH_DISPLAY_NAMES: Record = { guiyang_new2: "贵阳城市图谱", travel_agency: "旅行社资料库", travel_graph: "旅行社线路制定", travel_fixed_route_item: "旅行社固定线路资源图谱", travel_agency_2_0_test: "旅行社2.0测试图谱", baixinghui_travel_agency: "百姓惠旅行社图谱", }; const STATUS_DISPLAY_NAMES: Record = { active: "启用中", published: "已发布", draft: "草稿", archived: "已归档", }; export function displayProjectName(value?: string | null): string { const key = String(value || "").trim(); return PROJECT_DISPLAY_NAMES[key] || key || "-"; } export function displayGraphName(value?: string | null): string { const key = String(value || "").trim(); return GRAPH_DISPLAY_NAMES[key] || PROJECT_DISPLAY_NAMES[key] || key || "-"; } export function displayTenantName(value?: string | null): string { return displayProjectName(value); } export function displayStatusName(value?: string | null): string { const key = String(value || "").trim(); return STATUS_DISPLAY_NAMES[key] || key || "-"; } export function getProjectContext(): KgProjectContext { try { const parsed = JSON.parse(localStorage.getItem(PROJECT_CONTEXT_KEY) || "{}"); return { tenantId: parsed.tenantId || DEFAULT_PROJECT_CONTEXT.tenantId, projectId: parsed.projectId || DEFAULT_PROJECT_CONTEXT.projectId, graphName: parsed.graphName || DEFAULT_PROJECT_CONTEXT.graphName, }; } catch { return DEFAULT_PROJECT_CONTEXT; } } export function setProjectContext(context: KgProjectContext) { localStorage.setItem(PROJECT_CONTEXT_KEY, JSON.stringify(context)); window.dispatchEvent(new CustomEvent("znkg-project-context", { detail: context })); } api.interceptors.request.use((config) => { const token = localStorage.getItem("token"); if (token) config.headers.Authorization = `Bearer ${token}`; const context = getProjectContext(); config.headers["X-KG-Tenant-Id"] = context.tenantId; config.headers["X-KG-Project-Id"] = context.projectId; config.headers["X-KG-Graph-Name"] = context.graphName; return config; }); api.interceptors.response.use( (r) => r, (err) => { if (err.response?.status === 401) { localStorage.removeItem("token"); window.location.href = "/admin/login"; } return Promise.reject(err); } ); export default api; // ── Auth ── export const login = (username: string, password: string) => api.post("/auth/login", { username, password }); export const whoami = () => api.get("/auth/me"); // ── Projects / Graph releases ── export const listProjects = () => api.get("/projects"); export const createProject = (data: unknown) => api.post("/projects", data); export const listGraphReleases = (projectId: string) => api.get(`/projects/${encodeURIComponent(projectId)}/graph-releases`); export const createGraphRelease = (projectId: string, data: unknown) => api.post(`/projects/${encodeURIComponent(projectId)}/graph-releases`, data); export const listOntologySchemas = () => api.get("/ontology-schemas"); export const getCurrentOntologySchema = () => api.get("/ontology-schemas/current"); export const getOntologySchema = (schemaId: string | number) => api.get(`/ontology-schemas/${encodeURIComponent(String(schemaId))}`); // ── Source Profiles (STEP 01) ── export const listSourceProfiles = (params?: Record) => api.get("/source-profiles", { params }); export const createSourceProfile = (data: unknown) => api.post("/source-profiles", data); export const updateSourceProfile = (id: number, data: unknown) => api.patch(`/source-profiles/${id}`, data); // ── Batches (STEP 01) ── export const listBatches = (params?: Record) => api.get("/batches", { params }); export const getBatch = (id: number) => api.get(`/batches/${id}`); export const getBatchRawRecords = (id: number, params?: Record) => api.get(`/batches/${id}/raw-records`, { params }); export const getBatchQuality = (id: number) => api.get(`/batches/${id}/quality-summary`); // ── Question Traces (STEP 02) ── export const createQuestionTrace = (data: unknown) => api.post("/question-traces", data); export const listQuestionTraces = (params?: Record) => api.get("/question-traces", { params }); export const getQuestionTrace = (id: number) => api.get(`/question-traces/${id}`); export const simulateQuestions = () => api.post("/question-traces/simulate"); // ── Simulation Panel (STEP 02) ── export const listSimQuestions = () => api.get("/simulation-panel"); export const createSimQuestion = (data: unknown) => api.post("/simulation-panel", data); export const updateSimQuestion = (id: number, data: unknown) => api.patch(`/simulation-panel/${id}`, data); export const deleteSimQuestion = (id: number) => api.delete(`/simulation-panel/${id}`); export const importSimQuestions = (text: string) => api.post("/simulation-panel/import", { text }); export const adoptHitQuestions = () => api.post("/simulation-panel/adopt-hits"); export const aiGenerateSimQuestions = (count: number, topic?: string) => api.post("/simulation-panel/ai-generate", { count, topic }); // ── Audit (STEP 02) ── export const runAudit = () => api.post("/audit/run"); export const listAuditReports = () => api.get("/audit/reports"); export const latestAuditReport = () => api.get("/audit/reports/latest"); export const listAuditGaps = () => api.get("/audit/gaps"); export const getAuditRun = (id: number) => api.get(`/audit-runs/${id}`); export const latestAuditRun = () => api.get("/audit-runs/latest"); // ── Plaza (STEP 02) ── export const plazaOverview = () => api.get("/plaza/overview"); export const plazaUsage = () => api.get("/plaza/usage"); export const plazaAlerts = () => api.get("/plaza/alerts"); export const plazaAmapConfig = () => api.get("/plaza/amap-config"); export const plazaMapPois = () => api.get("/plaza/map-pois"); export const plazaBusRoutes = () => api.get("/plaza/bus-routes"); export const plazaMapPoiDetail = (placeId: string) => api.get(`/plaza/map-pois/${encodeURIComponent(placeId)}`); export const plazaUserQuery = (data: unknown) => api.post("/plaza/user-query", data); export const travelAssistantQuery = (data: unknown) => api.post("/travel/assistant-query", data); // ── OCR (旅行社名单录入) ── export const ocrPassport = (file: File) => { const form = new FormData(); form.append("file", file); return api.post("/ocr/passport", form, { headers: { "Content-Type": "multipart/form-data" }, }); }; export const ocrPassportByUrl = (url: string) => { const form = new FormData(); form.append("url", url); return api.post("/ocr/passport", form); }; export const graphOverview = () => api.get("/graph/overview"); export const graphQuery = (cypher: string, limit = 200) => api.post("/graph/query", { cypher, limit }); // ── Entities (STEP 03) ── export const listCandidates = (params?: Record) => api.get("/entities", { params }); export const getEntity = (id: number) => api.get(`/entities/${id}`); export const getEntityProvenance = (id: number) => api.get(`/entities/${id}/provenance`); export const listPoiEvidenceQuality = (params?: Record) => api.get("/evidence/poi-quality", { params }); export const getPoiEvidenceQuality = (placeKey: string, params?: Record) => api.get(`/evidence/poi-quality/${encodeURIComponent(placeKey)}`, { params }); export const approveEntity = (id: number) => api.post(`/entities/${id}/approve`); export const rejectEntity = (id: number, note?: string) => api.post(`/entities/${id}/reject`, null, { params: { note } }); export const fieldDecisions = (id: number, data: unknown) => api.patch(`/entities/${id}/field-decisions`, data); export const mergeEntities = (id: number, targetId: number, note?: string) => api.post(`/entities/${id}/merge-with/${targetId}`, null, { params: { note } }); // ── Acquisition Tasks (STEP 04) ── export const listAcquisitionTasks = (params?: Record) => api.get("/acquisition-tasks", { params }); export const createAcquisitionTask = (data: unknown) => api.post("/acquisition-tasks", data); export const createTaskFromGap = (data: unknown) => api.post("/acquisition-tasks/from-gap", data); export const getAcquisitionTask = (id: number) => api.get(`/acquisition-tasks/${id}`); export const assignTask = (id: number, assignee: string) => api.post(`/acquisition-tasks/${id}/assign`, { assignee }); export const startTask = (id: number) => api.post(`/acquisition-tasks/${id}/start`); export const completeTask = (id: number, resultSummary?: string) => api.post(`/acquisition-tasks/${id}/complete`, { result_summary: resultSummary }); export const cancelTask = (id: number, note?: string) => api.post(`/acquisition-tasks/${id}/cancel`, { note }); // work-order review state machine + urge export const reviewApproveTask = (id: number, note?: string) => api.post(`/acquisition-tasks/${id}/review-approve`, { note }); export const reviewRejectTask = (id: number, note?: string) => api.post(`/acquisition-tasks/${id}/review-reject`, { note }); export const urgeTask = (id: number, note?: string) => api.post(`/acquisition-tasks/${id}/urge`, { note }); // ── Inventory (STEP 04) ── export const listInventoryIssues = (params?: Record) => api.get("/inventory/issues", { params }); export const runInventoryScan = () => api.post("/inventory/scan"); export const resolveIssue = (id: number, note?: string) => api.post(`/inventory/issues/${id}/resolve`, { resolution_note: note }); // ── Treatment History (STEP 04) ── export const listTreatmentHistory = (params?: Record) => api.get("/treatment-history", { params }); // ── Vocabulary (STEP 05) ── export const listVocabulary = (params?: Record) => api.get("/vocabulary", { params }); export const createVocabularyTerm = (data: unknown) => api.post("/vocabulary", data); export const updateVocabularyTerm = (id: number, data: unknown) => api.patch(`/vocabulary/${id}`, data); export const lookupVocabulary = (name: string) => api.get("/vocabulary/lookup", { params: { name } }); export const mergeVocabulary = (id: number, targetId: number) => api.post(`/vocabulary/${id}/merge-into/${targetId}`); // ── Conflicts (STEP 05) ── export const listConflicts = () => api.get("/conflicts"); export const resolveConflict = (id: number, data: unknown) => api.post(`/conflicts/${id}/resolve`, data); // ── Aligner (STEP 05) ── export const alignSuggest = (candidateIds: number[]) => api.post("/aligner/suggest", { candidate_ids: candidateIds }); export const autoMerge = (candidateIds: number[]) => api.post("/aligner/auto-merge", { candidate_ids: candidateIds }); // ── Publish Jobs (STEP 05) ── export const listPublishJobs = () => api.get("/publish-jobs"); export const getPublishJob = (id: number) => api.get(`/publish-jobs/${id}`); export const createPublishJob = (candidateIds: number[]) => api.post("/publish-jobs", { candidate_ids: candidateIds }); export const getPublishDiff = (id: number) => api.get(`/publish-jobs/${id}/diff`); export const rollbackPublish = (id: number, reason?: string) => api.post(`/publish-jobs/${id}/rollback`, { reason }); // ── RBAC & Accounts (P1) ── export const listRoles = () => api.get("/roles"); export const createRole = (data: unknown) => api.post("/roles", data); export const updateRole = (roleKey: string, data: unknown) => api.patch(`/roles/${roleKey}`, data); export const deleteRole = (roleKey: string) => api.delete(`/roles/${roleKey}`); export const listCapabilities = () => api.get("/capabilities"); export const createCapability = (data: unknown) => api.post("/capabilities", data); export const deleteCapability = (capKey: string) => api.delete(`/capabilities/${capKey}`); export const getPermissionMatrix = () => api.get("/permission-matrix"); export const setPermissionCell = ( roleKey: string, capKey: string, value: string ) => api.put("/permission-matrix", { role_key: roleKey, cap_key: capKey, value }); export const listUsers = () => api.get("/users"); export const createUser = (data: unknown) => api.post("/users", data); export const updateUser = (id: number, data: unknown) => api.patch(`/users/${id}`, data); export const deleteUser = (id: number) => api.delete(`/users/${id}`); // ── City Areas & responsibility (P3) ── export const listAreas = () => api.get("/areas"); export const syncAreasFromGraph = () => api.post("/areas/sync-from-graph"); export const updateArea = (areaId: string, data: unknown) => api.patch(`/areas/${areaId}`, data); export const getAreaTree = () => api.get("/areas/tree"); export const getUserAreas = (userId: number) => api.get(`/users/${userId}/areas`); export const setUserAreas = ( userId: number, areaIds: string[], customAreas: string[] ) => api.put(`/users/${userId}/areas`, { area_ids: areaIds, custom_areas: customAreas }); // ── Notifications / inbox (P4) ── export const listNotifications = (onlyUnread = false) => api.get("/notifications", { params: { only_unread: onlyUnread } }); export const unreadCount = () => api.get("/notifications/unread-count"); export const markNotificationRead = (id: number) => api.post(`/notifications/${id}/read`); export const markAllNotificationsRead = () => api.post("/notifications/read-all"); // ── Agent settings ── export const getAgentSettings = () => api.get("/agent-settings"); export const saveAgentSettings = (config: unknown) => api.put("/agent-settings", config); // ── Super Agent (autonomous KG curator) ── export const superAgentRun = () => api.post("/super-agent/run", {}); export const superAgentLatest = () => api.get("/super-agent/runs/latest"); export const superAgentGet = (id: number) => api.get(`/super-agent/runs/${id}`); export const superAgentStop = (id: number) => api.post(`/super-agent/runs/${id}/stop`); export const superAgentCoverage = () => api.get("/super-agent/coverage"); export const superAgentTasks = (runId?: number) => api.get("/super-agent/tasks", { params: runId ? { run_id: runId } : {} });