From 841bd3e0bd381ddac5ef1d0cb711b46688b2ae6b Mon Sep 17 00:00:00 2001 From: huangping Date: Mon, 6 Apr 2026 22:40:28 +0800 Subject: [PATCH] feat(web): I5 callback inbox and integration catalog UI Made-with: Cursor --- web/delivery-platform-ui/src/api/platform.js | 69 +++++ .../src/layout/MainLayout.vue | 9 + web/delivery-platform-ui/src/router/index.js | 24 ++ .../src/utils/redactPayload.js | 72 +++++ .../src/views/CallbackInboxDetailView.vue | 253 ++++++++++++++++++ .../src/views/CallbackInboxView.vue | 159 +++++++++++ .../src/views/IntegrationEnvironmentsView.vue | 98 +++++++ .../src/views/IntegrationProductLinesView.vue | 100 +++++++ 8 files changed, 784 insertions(+) create mode 100644 web/delivery-platform-ui/src/utils/redactPayload.js create mode 100644 web/delivery-platform-ui/src/views/CallbackInboxDetailView.vue create mode 100644 web/delivery-platform-ui/src/views/CallbackInboxView.vue create mode 100644 web/delivery-platform-ui/src/views/IntegrationEnvironmentsView.vue create mode 100644 web/delivery-platform-ui/src/views/IntegrationProductLinesView.vue diff --git a/web/delivery-platform-ui/src/api/platform.js b/web/delivery-platform-ui/src/api/platform.js index a99be43..295b06a 100644 --- a/web/delivery-platform-ui/src/api/platform.js +++ b/web/delivery-platform-ui/src/api/platform.js @@ -202,3 +202,72 @@ export function updateLicenseSn(id, body) { export function patchLicenseSnStatus(id, body) { return axios.patch(`/api/v1/license-sns/${id}/status`, body); } + +/* —— I5 Callback Inbox & M6 integration read APIs (paths per docs/engineering/iterations/I5_I6_DESIGN.md A.3) —— */ + +/** + * @param {{ + * page?: number, + * size?: number, + * status?: string, + * eventType?: string, + * snCode?: string, + * projectId?: string | number, + * productLineId?: string | number, + * environmentId?: string | number, + * from?: string, + * to?: string, + * }} params + */ +export function listCallbackInbox(params) { + return axios.get("/api/v1/callback-inbox", { params }); +} + +export function getCallbackInbox(id) { + return axios.get(`/api/v1/callback-inbox/${id}`); +} + +/** + * @param {string | number} id + * @param {{ status: string }} body + */ +export function patchCallbackInboxStatus(id, body) { + return axios.patch(`/api/v1/callback-inbox/${id}/status`, body); +} + +/** + * 人工挂接(M5-F04)。body 字段以 OpenAPI 为准。 + * @param {string | number} id + * @param {Record} body + */ +export function patchCallbackInboxLink(id, body) { + return axios.patch(`/api/v1/callback-inbox/${id}/link`, body); +} + +/** + * @param {{ page?: number, size?: number }} params + */ +export function listIntegrationEnvironments(params) { + return axios.get("/api/v1/integration/environments", { params }); +} + +/** + * @param {string | number} id + */ +export function getIntegrationEnvironment(id) { + return axios.get(`/api/v1/integration/environments/${id}`); +} + +/** + * @param {{ page?: number, size?: number }} params + */ +export function listProductLines(params) { + return axios.get("/api/v1/integration/product-lines", { params }); +} + +/** + * @param {string | number} id + */ +export function getProductLine(id) { + return axios.get(`/api/v1/integration/product-lines/${id}`); +} diff --git a/web/delivery-platform-ui/src/layout/MainLayout.vue b/web/delivery-platform-ui/src/layout/MainLayout.vue index 2988bf3..f48a719 100644 --- a/web/delivery-platform-ui/src/layout/MainLayout.vue +++ b/web/delivery-platform-ui/src/layout/MainLayout.vue @@ -21,6 +21,15 @@ 许可 SN + + Callback 收件箱 + + + 集成环境 + + + 产品线 + diff --git a/web/delivery-platform-ui/src/router/index.js b/web/delivery-platform-ui/src/router/index.js index df90a0b..f947211 100644 --- a/web/delivery-platform-ui/src/router/index.js +++ b/web/delivery-platform-ui/src/router/index.js @@ -62,6 +62,30 @@ const routes = [ component: () => import("../views/LicenseSnListView.vue"), meta: { roles: ["SYS_ADMIN", "DEVELOPER"], title: "许可 SN" }, }, + { + path: "integration/environments", + name: "integration-environments", + component: () => import("../views/IntegrationEnvironmentsView.vue"), + meta: { roles: ["SYS_ADMIN", "DEVELOPER"], title: "集成环境" }, + }, + { + path: "integration/product-lines", + name: "integration-product-lines", + component: () => import("../views/IntegrationProductLinesView.vue"), + meta: { roles: ["SYS_ADMIN", "DEVELOPER"], title: "产品线" }, + }, + { + path: "callbacks/:id", + name: "callback-inbox-detail", + component: () => import("../views/CallbackInboxDetailView.vue"), + meta: { roles: ["SYS_ADMIN", "DEVELOPER"], title: "Callback 详情" }, + }, + { + path: "callbacks", + name: "callback-inbox", + component: () => import("../views/CallbackInboxView.vue"), + meta: { roles: ["SYS_ADMIN", "DEVELOPER"], title: "Callback 收件箱" }, + }, { path: "contracts/new", name: "contract-new", diff --git a/web/delivery-platform-ui/src/utils/redactPayload.js b/web/delivery-platform-ui/src/utils/redactPayload.js new file mode 100644 index 0000000..fa00df3 --- /dev/null +++ b/web/delivery-platform-ui/src/utils/redactPayload.js @@ -0,0 +1,72 @@ +const SENSITIVE_KEY_RE = /(authorization|bearer|token|secret|password|api[_-]?key|access[_-]?token|refresh[_-]?token|idempotency)/i; + +/** + * Recursively redact object values for safe display (tokens / auth-like keys). + * @param {unknown} value + * @param {string} [keyHint] + * @returns {unknown} + */ +function redactValue(value, keyHint = "") { + if (value === null || value === undefined) return value; + if (typeof value === "string") { + const k = keyHint; + if (SENSITIVE_KEY_RE.test(k)) return "[REDACTED]"; + if (value.length > 48) return `${value.slice(0, 8)}…[${value.length} chars]`; + return value; + } + if (typeof value === "number" || typeof value === "boolean") return value; + if (Array.isArray(value)) return value.map((item, i) => redactValue(item, `${keyHint}[${i}]`)); + if (typeof value === "object") { + /** @type {Record} */ + const out = {}; + for (const [k, v] of Object.entries(value)) { + out[k] = redactValue(v, k); + } + return out; + } + return value; +} + +/** + * Pretty JSON string with redaction; falls back to regex pass on non-JSON text. + * @param {unknown} raw — object or JSON string from API + * @returns {string} + */ +export function formatRedactedPayloadJson(raw) { + if (raw == null) return ""; + let obj = raw; + if (typeof raw === "string") { + const trimmed = raw.trim(); + try { + obj = JSON.parse(trimmed); + } catch { + return redactRawJsonString(trimmed); + } + } + const redacted = redactValue(obj, ""); + try { + return JSON.stringify(redacted, null, 2); + } catch { + return String(raw); + } +} + +/** + * Best-effort redaction when the body is not valid JSON. + * @param {string} s + */ +function redactRawJsonString(s) { + let out = s; + out = out.replace(/"((?:[^"\\]|\\.)*)"\s*:\s*"((?:[^"\\]|\\.)*)"/g, (match, key, val) => { + const keyStr = String(key).replace(/\\"/g, '"'); + if (SENSITIVE_KEY_RE.test(keyStr)) { + return `"${key}":"[REDACTED]"`; + } + const unescaped = val.replace(/\\"/g, '"').replace(/\\\\/g, "\\"); + if (unescaped.length > 48) { + return `"${key}":"${unescaped.slice(0, 8)}…[${unescaped.length} chars]"`; + } + return match; + }); + return out; +} diff --git a/web/delivery-platform-ui/src/views/CallbackInboxDetailView.vue b/web/delivery-platform-ui/src/views/CallbackInboxDetailView.vue new file mode 100644 index 0000000..fc20a43 --- /dev/null +++ b/web/delivery-platform-ui/src/views/CallbackInboxDetailView.vue @@ -0,0 +1,253 @@ + + + + + diff --git a/web/delivery-platform-ui/src/views/CallbackInboxView.vue b/web/delivery-platform-ui/src/views/CallbackInboxView.vue new file mode 100644 index 0000000..924cba2 --- /dev/null +++ b/web/delivery-platform-ui/src/views/CallbackInboxView.vue @@ -0,0 +1,159 @@ + + + + + diff --git a/web/delivery-platform-ui/src/views/IntegrationEnvironmentsView.vue b/web/delivery-platform-ui/src/views/IntegrationEnvironmentsView.vue new file mode 100644 index 0000000..cfc75b9 --- /dev/null +++ b/web/delivery-platform-ui/src/views/IntegrationEnvironmentsView.vue @@ -0,0 +1,98 @@ + + + + + diff --git a/web/delivery-platform-ui/src/views/IntegrationProductLinesView.vue b/web/delivery-platform-ui/src/views/IntegrationProductLinesView.vue new file mode 100644 index 0000000..9e00552 --- /dev/null +++ b/web/delivery-platform-ui/src/views/IntegrationProductLinesView.vue @@ -0,0 +1,100 @@ + + + + +