From b02d8f81fcbdd93babbe5e4f712abff3152ada60 Mon Sep 17 00:00:00 2001 From: pnoker Date: Mon, 17 Aug 2026 21:37:49 +0800 Subject: [PATCH] refactor!(web): parse identifiers as strings and drop json-bigint BREAKING CHANGE: frontend identifier types converge to string and the json-bigint dependency is removed; the backend now emits string ids. --- AGENTS.md | 3 +- dc3-web/package.json | 2 -- dc3-web/pnpm-lock.yaml | 23 -------------- dc3-web/src/api/dashboard/alert.ts | 6 ++-- .../components/agentic/AgenticAssistant.vue | 2 +- dc3-web/src/composables/useEntityNames.ts | 30 +++++++++---------- dc3-web/src/config/axios/index.ts | 26 +--------------- dc3-web/src/config/types/agentic.ts | 6 ++-- dc3-web/src/config/types/auth.ts | 8 ++--- dc3-web/src/config/types/dashboard.ts | 20 ++++++------- dc3-web/src/config/types/manager.ts | 2 +- .../src/mock/seed/agentic-conversations.ts | 6 ++-- dc3-web/src/mock/seed/auth.ts | 12 ++++---- dc3-web/src/mock/seed/manager.ts | 20 ++++++------- dc3-web/src/mock/seed/menuTree.ts | 4 +-- dc3-web/src/store/modules/agentic.ts | 8 ++--- dc3-web/src/store/modules/menu.ts | 6 ++-- dc3-web/src/utils/jumpUtil.ts | 4 +-- dc3-web/src/views/device/edit/index.ts | 8 ++--- .../src/views/home/components/AlertList.vue | 6 ++-- .../views/home/components/LiveDataFeed.vue | 6 ++-- dc3-web/src/views/profile/Profile.vue | 6 ++-- .../settings/agentic/AgenticSettings.vue | 2 +- .../alarm/components/AlertCorrelation.vue | 4 +-- .../alarm/components/AlertStormSources.vue | 2 +- .../settings/alarm/components/EventTable.vue | 6 ++-- .../alarm/components/RecentUnconfirmed.vue | 4 +-- .../role/assign/RoleAssignResources.vue | 2 +- dc3-web/tests/e2e/fixtures/app.ts | 6 ++-- dc3-web/tests/unit/axios.test.ts | 4 +-- 30 files changed, 97 insertions(+), 147 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index b12ec43de..60c3eaa52 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -54,6 +54,7 @@ iot-dc3/ ├── dc3-coverage/ aggregate JaCoCo report and absolute coverage gate ├── dc3-e2e/ Testcontainers-backed end-to-end tests ├── dc3/ compose files, environment files, scripts, and generated release notes +├── docs/design/ design documents (e.g. `mq-abstraction.md` — proposed pluggable broker port) ├── Makefile preferred backend/container command entrypoint └── .mvn/settings.xml local Maven mirror configuration ``` @@ -213,7 +214,7 @@ Key rules: - `verbatimModuleSyntax` is enabled. Use `import type` for every type-only import; Vue components, functions, and icons remain normal value imports. - Use `Form` for create/update payloads and `Record` for read responses. -- Represent Java 64-bit IDs as strings and preserve the existing JSONBigInt handling. +- Represent Java 64-bit IDs as strings. The backend emits identifiers as JSON strings on the HTTP contract, so standard JSON parsing (no JSONBigInt) is sufficient. - API wrappers mirror backend cardinality: `getXxx` for one value, `listXxx` for collections/maps/pages, and `addXxx`/`updateXxx`/`deleteXxx` for mutations. - Reuse CRUD helpers from `src/api/common.ts` and API bases from `src/config/constant/api.ts`; keep API wrappers thin. diff --git a/dc3-web/package.json b/dc3-web/package.json index b298b01b5..85220abb2 100644 --- a/dc3-web/package.json +++ b/dc3-web/package.json @@ -64,7 +64,6 @@ "highlight.js": "^11.11.1", "js-base64": "^3.9.1", "js-cookie": "^3.0.8", - "json-bigint": "^1.0.0", "marked": "^18.0.6", "mitt": "^3.0.1", "nprogress": "^0.2.0", @@ -78,7 +77,6 @@ "@eslint/js": "^10.0.1", "@playwright/test": "^1.62.0", "@types/js-cookie": "^3.0.6", - "@types/json-bigint": "^1.0.4", "@types/node": "^26.1.1", "@types/nprogress": "^0.2.3", "@typescript-eslint/eslint-plugin": "^8.64.0", diff --git a/dc3-web/pnpm-lock.yaml b/dc3-web/pnpm-lock.yaml index 644471136..d05293754 100644 --- a/dc3-web/pnpm-lock.yaml +++ b/dc3-web/pnpm-lock.yaml @@ -45,9 +45,6 @@ importers: js-cookie: specifier: ^3.0.8 version: 3.0.8 - json-bigint: - specifier: ^1.0.0 - version: 1.0.0 marked: specifier: ^18.0.6 version: 18.0.6 @@ -82,9 +79,6 @@ importers: '@types/js-cookie': specifier: ^3.0.6 version: 3.0.6 - '@types/json-bigint': - specifier: ^1.0.4 - version: 1.0.4 '@types/node': specifier: ^26.1.1 version: 26.1.1 @@ -774,9 +768,6 @@ packages: '@types/jsesc@2.5.1': resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==} - '@types/json-bigint@1.0.4': - resolution: {integrity: sha512-ydHooXLbOmxBbubnA7Eh+RpBzuaIiQjh8WGJYQB50JFGFrdxW7JzVlyEV7fAXw0T2sqJ1ysTneJbiyNLqZRAag==} - '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -1185,9 +1176,6 @@ packages: bidi-js@1.0.3: resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} - bignumber.js@9.0.0: - resolution: {integrity: sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==} - birpc@2.6.1: resolution: {integrity: sha512-LPnFhlDpdSH6FJhJyn4M0kFO7vtQ5iPw24FnG0y21q09xC7e8+1LeR31S1MAIrDAHp4m7aas4bEkTDTvMAtebQ==} @@ -1841,9 +1829,6 @@ packages: engines: {node: '>=6'} hasBin: true - json-bigint@1.0.0: - resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} - json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} @@ -3418,8 +3403,6 @@ snapshots: '@types/jsesc@2.5.1': {} - '@types/json-bigint@1.0.4': {} - '@types/json-schema@7.0.15': {} '@types/lodash-es@4.17.12': @@ -3960,8 +3943,6 @@ snapshots: dependencies: require-from-string: 2.0.2 - bignumber.js@9.0.0: {} - birpc@2.6.1: {} boolbase@1.0.0: {} @@ -4610,10 +4591,6 @@ snapshots: jsesc@3.1.0: {} - json-bigint@1.0.0: - dependencies: - bignumber.js: 9.0.0 - json-buffer@3.0.1: {} json-schema-traverse@0.4.1: {} diff --git a/dc3-web/src/api/dashboard/alert.ts b/dc3-web/src/api/dashboard/alert.ts index 331fb95e7..b2f695610 100644 --- a/dc3-web/src/api/dashboard/alert.ts +++ b/dc3-web/src/api/dashboard/alert.ts @@ -21,13 +21,13 @@ import type {AlertPageQuery, AlertSource} from '@/config/types/dashboard'; export const alertPage = (body: AlertPageQuery = {}) => httpPost(`${API_DATA_BASE}/dashboard/alert/page`, body); -export const alertConfirm = (source: AlertSource, id: string | number) => +export const alertConfirm = (source: AlertSource, id: string) => httpPost(`${API_DATA_BASE}/dashboard/alert/confirm`, undefined, {params: {source, id}}); -export const alertUnconfirm = (source: AlertSource, id: string | number) => +export const alertUnconfirm = (source: AlertSource, id: string) => httpPost(`${API_DATA_BASE}/dashboard/alert/unconfirm`, undefined, {params: {source, id}}); -export const alertBulkConfirm = (items: Array<{ source: AlertSource; id: string | number }>, confirm: boolean) => +export const alertBulkConfirm = (items: Array<{ source: AlertSource; id: string }>, confirm: boolean) => httpPost(`${API_DATA_BASE}/dashboard/alert/bulk_confirm`, {items, confirm}); export const alertTrend = (days = 30) => httpGet(`${API_DATA_BASE}/dashboard/alert/trend`, {params: {days}}); diff --git a/dc3-web/src/components/agentic/AgenticAssistant.vue b/dc3-web/src/components/agentic/AgenticAssistant.vue index e10d43805..8d74f97e6 100644 --- a/dc3-web/src/components/agentic/AgenticAssistant.vue +++ b/dc3-web/src/components/agentic/AgenticAssistant.vue @@ -801,7 +801,7 @@ const handlePromptClick = (item: AssistantPromptItem) => { draft.value = `${item.label || ''}${description}`.trim(); }; -const handleModelChange = (model: string | number) => { +const handleModelChange = (model: string) => { void agenticStore.setSelectedModel(model); }; diff --git a/dc3-web/src/composables/useEntityNames.ts b/dc3-web/src/composables/useEntityNames.ts index 48584bf8a..50a8ce66f 100644 --- a/dc3-web/src/composables/useEntityNames.ts +++ b/dc3-web/src/composables/useEntityNames.ts @@ -86,7 +86,7 @@ const nameField: Record = { point: 'pointName', }; -async function fetchMissing(kind: EntityKind, rawIds: Array): Promise { +async function fetchMissing(kind: EntityKind, rawIds: Array): Promise { if (!rawIds || rawIds.length === 0) return; const locale = String(i18n.global.locale.value); const cacheKey = (id: string) => `${locale}:${id}`; @@ -150,33 +150,33 @@ async function fetchMissing(kind: EntityKind, rawIds: Array): P export type AlertSourceKind = 'point' | 'device' | 'driver'; export const useEntityNames = () => { - const cachedName = (kind: EntityKind, id: string | number): string => + const cachedName = (kind: EntityKind, id: string): string => cache[kind][`${String(i18n.global.locale.value)}:${String(id)}`] ?? String(id); - const resolveDevices = (ids: Array) => fetchMissing('device', ids); - const resolveDrivers = (ids: Array) => fetchMissing('driver', ids); - const resolveProfiles = (ids: Array) => fetchMissing('profile', ids); - const resolvePoints = (ids: Array) => fetchMissing('point', ids); + const resolveDevices = (ids: Array) => fetchMissing('device', ids); + const resolveDrivers = (ids: Array) => fetchMissing('driver', ids); + const resolveProfiles = (ids: Array) => fetchMissing('profile', ids); + const resolvePoints = (ids: Array) => fetchMissing('point', ids); - const deviceName = (id: string | number | undefined | null): string => + const deviceName = (id: string | undefined | null): string => id == null ? '' : cachedName('device', id); - const driverName = (id: string | number | undefined | null): string => + const driverName = (id: string | undefined | null): string => id == null ? '' : cachedName('driver', id); - const profileName = (id: string | number | undefined | null): string => + const profileName = (id: string | undefined | null): string => id == null ? '' : cachedName('profile', id); - const pointName = (id: string | number | undefined | null): string => + const pointName = (id: string | undefined | null): string => id == null ? '' : cachedName('point', id); /** Resolve a mixed batch of (source, id) rows in one call. */ const resolveBySource = async ( rows: Array<{ source: AlertSourceKind; - sourceId: string | number; + sourceId: string; }> ): Promise => { - const ptIds: Array = []; - const devIds: Array = []; - const drvIds: Array = []; + const ptIds: Array = []; + const devIds: Array = []; + const drvIds: Array = []; for (const r of rows) { if (r.source === 'point') ptIds.push(r.sourceId); else if (r.source === 'device') devIds.push(r.sourceId); @@ -185,7 +185,7 @@ export const useEntityNames = () => { await Promise.all([resolvePoints(ptIds), resolveDevices(devIds), resolveDrivers(drvIds)]); }; - const nameBySource = (source: AlertSourceKind, id: string | number): string => { + const nameBySource = (source: AlertSourceKind, id: string): string => { if (source === 'point') return pointName(id); if (source === 'driver') return driverName(id); return deviceName(id); diff --git a/dc3-web/src/config/axios/index.ts b/dc3-web/src/config/axios/index.ts index 846476292..7da4b7c63 100644 --- a/dc3-web/src/config/axios/index.ts +++ b/dc3-web/src/config/axios/index.ts @@ -25,40 +25,16 @@ import {failMessage, warnMessage} from '@/utils/notificationUtil'; import {getStorage, removeStorage} from '@/utils/storageUtil'; import {isNull} from '@/utils/validationUtil'; import router from '@/config/router'; -import JSONBigInt from 'json-bigint'; - -/** - * JSONBigInt parser instance with storeAsString option - */ -const JSONBigIntStr = JSONBigInt({storeAsString: true}); - -/** - * Transform response data using JSONBigInt to handle large integers - * - * @param data Raw response data - * @returns Parsed data - */ -function transformResponse(data: any): any { - if (typeof data !== 'string' || data === '') { - return data; - } - try { - return JSONBigIntStr.parse(data); - } catch { - return data; - } -} /** * Custom Axios instance with default configuration - * Handles large integers via JSONBigInt and includes authentication headers + * Includes authentication headers */ const request: AxiosInstance = axios.create({ timeout: AXIOS_CONFIG.TIMEOUT, withCredentials: true, headers: {Accept: AXIOS_CONFIG.HEADERS.ACCEPT, 'Content-Type': AXIOS_CONFIG.HEADERS.CONTENT_TYPE}, validateStatus: (status) => status >= AXIOS_CONFIG.MIN_STATUS && status <= AXIOS_CONFIG.MAX_STATUS, - transformResponse: [transformResponse], }); /** diff --git a/dc3-web/src/config/types/agentic.ts b/dc3-web/src/config/types/agentic.ts index 4ba462cdd..6c25b9db8 100644 --- a/dc3-web/src/config/types/agentic.ts +++ b/dc3-web/src/config/types/agentic.ts @@ -111,7 +111,7 @@ export interface AgenticMessage { export interface AgenticMessageContent { text?: string; format?: string; - attachments?: number[]; + attachments?: string[]; tools?: string[]; traces?: AgenticTraceEvent[]; charts?: AgenticVisualizationSpec[]; @@ -165,7 +165,7 @@ export interface AgenticMessageTokens { } export interface AgenticAttachment { - id: number; + id: string; conversationId: string; fileName: string; contentType: string; @@ -209,7 +209,7 @@ export interface AgenticChatCompletionRequest { * Leaving this empty is rejected by the backend with HTTP 400. */ conversationId: string; - attachments?: number[]; + attachments?: string[]; reasoning?: boolean; } diff --git a/dc3-web/src/config/types/auth.ts b/dc3-web/src/config/types/auth.ts index ec9ac9214..3bf657d7a 100644 --- a/dc3-web/src/config/types/auth.ts +++ b/dc3-web/src/config/types/auth.ts @@ -43,7 +43,7 @@ export interface UserRecord extends UserForm { export interface RoleForm { id?: string; - parentRoleId?: number | string; + parentRoleId?: string; roleName?: string; roleCode?: string; enableFlag?: string; @@ -63,7 +63,7 @@ export interface RoleRecord extends RoleForm { export interface MenuForm { id?: string; - parentMenuId?: number | string; + parentMenuId?: string; menuName?: string; menuCode?: string; menuTypeFlag?: string; @@ -93,13 +93,13 @@ export interface MenuRecord extends MenuForm { export interface ResourceForm { id?: string; - parentResourceId?: number | string; + parentResourceId?: string; resourceName?: string; resourceCode?: string; serviceName?: string; resourceTypeFlag?: string; resourceScopeFlag?: string; - entityId?: string | number; + entityId?: string; resourceExt?: Record; enableFlag?: string; remark?: string; diff --git a/dc3-web/src/config/types/dashboard.ts b/dc3-web/src/config/types/dashboard.ts index a6f91e694..036cebfb5 100644 --- a/dc3-web/src/config/types/dashboard.ts +++ b/dc3-web/src/config/types/dashboard.ts @@ -112,24 +112,24 @@ export interface TopologyResponse { export interface FlappingSource { source: AlertSource; - sourceId: number | string; + sourceId: string; eventTypeFlag: number; count: number; } export interface CorrelationPair { aSource: AlertSource; - aSourceId: number | string; + aSourceId: string; aEventType: number; bSource: AlertSource; - bSourceId: number | string; + bSourceId: string; bEventType: number; coCount: number; } export interface PeerDeviation { - profileId: number | string; - deviceId: number | string; + profileId: string; + deviceId: string; alarmCount: number; peerMedian: number; ratio: number; @@ -159,20 +159,20 @@ export interface ProtocolHealth { export interface ChangeImpact { kind: ChangeKind; - entityId: number | string; + entityId: string; operateTime: string; } export interface SilentSource { - deviceId: number | string; - pointId: number | string; + deviceId: string; + pointId: string; lastSeen: string; silentSeconds: number; } export interface CoverageGapItem { - pointId: number | string; - profileId: number | string; + pointId: string; + profileId: string; } export interface CoverageGap { diff --git a/dc3-web/src/config/types/manager.ts b/dc3-web/src/config/types/manager.ts index 28570f367..64a19336f 100644 --- a/dc3-web/src/config/types/manager.ts +++ b/dc3-web/src/config/types/manager.ts @@ -172,7 +172,7 @@ export interface EventInfoForm { export interface GroupForm { id?: string; - parentGroupId?: number | string | null; + parentGroupId?: string | null; groupTypeFlag?: string; groupName?: string; groupCode?: string; diff --git a/dc3-web/src/mock/seed/agentic-conversations.ts b/dc3-web/src/mock/seed/agentic-conversations.ts index 1bab0f7fa..becdec163 100644 --- a/dc3-web/src/mock/seed/agentic-conversations.ts +++ b/dc3-web/src/mock/seed/agentic-conversations.ts @@ -295,7 +295,7 @@ export const agenticActions: LocalizedAction[] = [ ]; export const agenticAttachments: AgenticAttachment[] = [ - {id: 91001, conversationId: sessionDefs[3]!.id, fileName: 'opcua-handshake-20260811.log', contentType: 'text/plain', size: 2516582, createTime: at(1, 10, 58)}, - {id: 91002, conversationId: sessionDefs[5]!.id, fileName: 'M-104-vibration-spectrum.csv', contentType: 'text/csv', size: 486210, createTime: at(3, 9, 54)}, - {id: 91003, conversationId: sessionDefs[6]!.id, fileName: 'canary-rollout-metrics.json', contentType: 'application/json', size: 128044, createTime: at(4, 16, 50)}, + {id: '91001', conversationId: sessionDefs[3]!.id, fileName: 'opcua-handshake-20260811.log', contentType: 'text/plain', size: 2516582, createTime: at(1, 10, 58)}, + {id: '91002', conversationId: sessionDefs[5]!.id, fileName: 'M-104-vibration-spectrum.csv', contentType: 'text/csv', size: 486210, createTime: at(3, 9, 54)}, + {id: '91003', conversationId: sessionDefs[6]!.id, fileName: 'canary-rollout-metrics.json', contentType: 'application/json', size: 128044, createTime: at(4, 16, 50)}, ]; diff --git a/dc3-web/src/mock/seed/auth.ts b/dc3-web/src/mock/seed/auth.ts index 86f52915f..9f61da42b 100644 --- a/dc3-web/src/mock/seed/auth.ts +++ b/dc3-web/src/mock/seed/auth.ts @@ -115,7 +115,7 @@ export const users: UserRecord[] = userDefs.map((u, i) => ({ // nested under ROLE_OPERATOR / ROLE_ADMIN to exercise the role tree. interface RoleDef { - parentRoleId: number; + parentRoleId: string; roleName: string; roleCode: string; remark: string; @@ -123,11 +123,11 @@ interface RoleDef { } const roleDefs: RoleDef[] = [ - {parentRoleId: 0, roleName: '系统管理员', roleCode: 'ROLE_ADMIN', remark: '内置超级管理员,拥有全部权限'}, - {parentRoleId: 0, roleName: '运维人员', roleCode: 'ROLE_OPERATOR', remark: '负责设备与驱动的日常运维'}, - {parentRoleId: 0, roleName: '只读访客', roleCode: 'ROLE_VIEWER', remark: '仅可查看监控数据'}, - {parentRoleId: 2, roleName: '运维组长', roleCode: 'ROLE_OPERATOR_LEAD', remark: '运维组长,可管理运维人员'}, - {parentRoleId: 1, roleName: '审计员', roleCode: 'ROLE_AUDITOR', remark: '审计员,查看审计与日志', enabled: false}, + {parentRoleId: '0', roleName: '系统管理员', roleCode: 'ROLE_ADMIN', remark: '内置超级管理员,拥有全部权限'}, + {parentRoleId: '0', roleName: '运维人员', roleCode: 'ROLE_OPERATOR', remark: '负责设备与驱动的日常运维'}, + {parentRoleId: '0', roleName: '只读访客', roleCode: 'ROLE_VIEWER', remark: '仅可查看监控数据'}, + {parentRoleId: '2', roleName: '运维组长', roleCode: 'ROLE_OPERATOR_LEAD', remark: '运维组长,可管理运维人员'}, + {parentRoleId: '1', roleName: '审计员', roleCode: 'ROLE_AUDITOR', remark: '审计员,查看审计与日志', enabled: false}, ]; export const roles: RoleRecord[] = roleDefs.map((r, i) => ({ diff --git a/dc3-web/src/mock/seed/manager.ts b/dc3-web/src/mock/seed/manager.ts index ac4b3a4e1..640a02c38 100644 --- a/dc3-web/src/mock/seed/manager.ts +++ b/dc3-web/src/mock/seed/manager.ts @@ -36,7 +36,7 @@ const KEEP = {content: {keep: ''}}; export const groups: GroupRecord[] = [ { id: '1', - parentGroupId: 0, + parentGroupId: '0', groupTypeFlag: 'DRIVER', groupName: '驱动分组', groupCode: 'DRIVER-GROUP', @@ -49,7 +49,7 @@ export const groups: GroupRecord[] = [ }, { id: '2', - parentGroupId: 1, + parentGroupId: '1', groupTypeFlag: 'DRIVER', groupName: 'Modbus 驱动组', groupCode: 'DRIVER-MODBUS', @@ -62,7 +62,7 @@ export const groups: GroupRecord[] = [ }, { id: '3', - parentGroupId: 0, + parentGroupId: '0', groupTypeFlag: 'DEVICE', groupName: '设备分组', groupCode: 'DEVICE-GROUP', @@ -75,7 +75,7 @@ export const groups: GroupRecord[] = [ }, { id: '4', - parentGroupId: 3, + parentGroupId: '3', groupTypeFlag: 'DEVICE', groupName: '温湿度设备组', groupCode: 'DEVICE-TH', @@ -88,7 +88,7 @@ export const groups: GroupRecord[] = [ }, { id: '5', - parentGroupId: 3, + parentGroupId: '3', groupTypeFlag: 'DEVICE', groupName: '电力设备组', groupCode: 'DEVICE-POWER', @@ -238,7 +238,7 @@ export const apis: ApiRecord[] = [ export const resources: ResourceRecord[] = [ { id: '5001', - parentResourceId: 0, + parentResourceId: '0', resourceName: '根资源', resourceCode: 'ROOT', serviceName: 'dc3-manager', @@ -253,7 +253,7 @@ export const resources: ResourceRecord[] = [ }, { id: '5002', - parentResourceId: 5001, + parentResourceId: '5001', resourceName: '驱动新增', resourceCode: 'driver_add', serviceName: 'dc3-manager', @@ -268,7 +268,7 @@ export const resources: ResourceRecord[] = [ }, { id: '5003', - parentResourceId: 5001, + parentResourceId: '5001', resourceName: '设备查询', resourceCode: 'device_list', serviceName: 'dc3-manager', @@ -283,7 +283,7 @@ export const resources: ResourceRecord[] = [ }, { id: '5004', - parentResourceId: 5001, + parentResourceId: '5001', resourceName: '位号更新', resourceCode: 'point_update', serviceName: 'dc3-data', @@ -298,7 +298,7 @@ export const resources: ResourceRecord[] = [ }, { id: '5005', - parentResourceId: 5001, + parentResourceId: '5001', resourceName: '模板删除', resourceCode: 'profile_delete', serviceName: 'dc3-manager', diff --git a/dc3-web/src/mock/seed/menuTree.ts b/dc3-web/src/mock/seed/menuTree.ts index 0354ea9ee..3ba4570f2 100644 --- a/dc3-web/src/mock/seed/menuTree.ts +++ b/dc3-web/src/mock/seed/menuTree.ts @@ -34,8 +34,8 @@ interface NodeOpts { * static demo matches what the backend serves; resolveMenuTitle prefers content.titles. */ const mk = (menuCode: string, opts: NodeOpts = {}): MenuNode => ({ - id: ++seq, - parentMenuId: 0, + id: String(++seq), + parentMenuId: '0', menuName: opts.menuName ?? menuCode, menuCode, menuIndex: opts.menuIndex, diff --git a/dc3-web/src/store/modules/agentic.ts b/dc3-web/src/store/modules/agentic.ts index bdbc880e1..08ee5b499 100644 --- a/dc3-web/src/store/modules/agentic.ts +++ b/dc3-web/src/store/modules/agentic.ts @@ -71,7 +71,7 @@ export const useAgenticStore = defineStore('agentic', () => { const currentAbortController = ref(); const messagesByConversation = ref>(readCachedMessages()); const attachmentsByConversation = ref>({}); - const pendingAttachmentIdsByConversation = ref>({}); + const pendingAttachmentIdsByConversation = ref>({}); const pendingActionsByConversation = ref>({}); const traceEventsByConversation = ref>({}); @@ -206,7 +206,7 @@ export const useAgenticStore = defineStore('agentic', () => { persistMessages(); }; - const selectSession = async (conversationId?: string | number) => { + const selectSession = async (conversationId?: string) => { if (!conversationId) { return; } @@ -405,7 +405,7 @@ export const useAgenticStore = defineStore('agentic', () => { ]; }; - const removeLocalAttachment = (attachmentId: number) => { + const removeLocalAttachment = (attachmentId: string) => { const conversationId = activeConversationId.value; pendingAttachmentIdsByConversation.value[conversationId] = ( pendingAttachmentIdsByConversation.value[conversationId] || [] @@ -472,7 +472,7 @@ export const useAgenticStore = defineStore('agentic', () => { } }; - const setSelectedModel = async (model: string | number) => { + const setSelectedModel = async (model: string) => { const nextModel = resolveModelName(String(model || '')); selectedModel.value = nextModel; applyModelCapabilities(); diff --git a/dc3-web/src/store/modules/menu.ts b/dc3-web/src/store/modules/menu.ts index 487360479..b5de73a25 100644 --- a/dc3-web/src/store/modules/menu.ts +++ b/dc3-web/src/store/modules/menu.ts @@ -20,8 +20,8 @@ import {defineStore} from 'pinia'; import {listMenuTree} from '@/api/menu'; export interface MenuNode { - id: number | string; - parentMenuId: number | string; + id: string; + parentMenuId: string; menuName: string; menuCode: string; menuTypeFlag?: string; @@ -86,7 +86,7 @@ export const useMenuStore = defineStore('menu', { */ findById: (state) => - (id: number | string): MenuNode | undefined => { + (id: string): MenuNode | undefined => { const key = String(id); const walk = (nodes: MenuNode[]): MenuNode | undefined => { for (const n of nodes) { diff --git a/dc3-web/src/utils/jumpUtil.ts b/dc3-web/src/utils/jumpUtil.ts index fec0396b5..d33072b51 100644 --- a/dc3-web/src/utils/jumpUtil.ts +++ b/dc3-web/src/utils/jumpUtil.ts @@ -40,7 +40,7 @@ export type AlertSourceKind = 'point' | 'device' | 'driver'; * kind. Router errors (e.g. aborted nav during transitions) are swallowed * so callers don't have to .catch() themselves. */ -export const jumpToEntity = (router: Router, kind: JumpKind, id: string | number): void => { +export const jumpToEntity = (router: Router, kind: JumpKind, id: string): void => { const idStr = String(id); if (!idStr) return; switch (kind) { @@ -66,7 +66,7 @@ export const jumpToEntity = (router: Router, kind: JumpKind, id: string | number /** * Jump to the per-source event list with the source id pre-filtered. */ -export const jumpToSourceEvents = (router: Router, source: AlertSourceKind, sourceId: string | number): void => { +export const jumpToSourceEvents = (router: Router, source: AlertSourceKind, sourceId: string): void => { let name: string; if (source === 'point') name = 'settingsPointAlarm'; else if (source === 'driver') name = 'settingsDriverAlarm'; diff --git a/dc3-web/src/views/device/edit/index.ts b/dc3-web/src/views/device/edit/index.ts index 807b8ffa7..08ba73315 100644 --- a/dc3-web/src/views/device/edit/index.ts +++ b/dc3-web/src/views/device/edit/index.ts @@ -720,7 +720,7 @@ export default defineComponent({ listDriverInfoByDeviceId(reactiveData.id) .then((res) => { const formData: AttributeFormData = reactiveData.driverFormData; - res.data.forEach((info: { attributeId: string | number; id: any; configValue: any }) => { + res.data.forEach((info: { attributeId: string; id: any; configValue: any }) => { const attributeCode = reactiveData.driverAttributeTable[info.attributeId]; const attribute = reactiveData.driverAttributes.find((item) => item.attributeCode === attributeCode); if (attribute) { @@ -778,7 +778,7 @@ export default defineComponent({ return listPointInfoByDeviceId(reactiveData.id) .then((infoRes) => { (infoRes.data || []).forEach( - (info: { pointId: string; attributeId: string | number; id: string; configValue: unknown }) => { + (info: { pointId: string; attributeId: string; id: string; configValue: unknown }) => { const attributeCode = reactiveData.pointAttributeTable[info.attributeId]; const attribute = reactiveData.pointAttributes.find((item) => item.attributeCode === attributeCode); const row = rowTable[info.pointId]; @@ -842,7 +842,7 @@ export default defineComponent({ return listCommandInfoByDeviceId(reactiveData.id) .then((infoRes) => { (infoRes.data || []).forEach( - (info: { commandId: string; attributeId: string | number; id: string; configValue: unknown }) => { + (info: { commandId: string; attributeId: string; id: string; configValue: unknown }) => { const attributeCode = reactiveData.commandAttributeTable[info.attributeId]; const attribute = reactiveData.commandAttributes.find((item) => item.attributeCode === attributeCode); const row = rowTable[String(info.commandId)]; @@ -903,7 +903,7 @@ export default defineComponent({ return listEventInfoByDeviceId(reactiveData.id) .then((infoRes) => { (infoRes.data || []).forEach( - (info: { eventId: string; attributeId: string | number; id: string; configValue: unknown }) => { + (info: { eventId: string; attributeId: string; id: string; configValue: unknown }) => { const attributeCode = reactiveData.eventAttributeTable[info.attributeId]; const attribute = reactiveData.eventAttributes.find((item) => item.attributeCode === attributeCode); const row = rowTable[String(info.eventId)]; diff --git a/dc3-web/src/views/home/components/AlertList.vue b/dc3-web/src/views/home/components/AlertList.vue index 0ca216a68..053540f8b 100644 --- a/dc3-web/src/views/home/components/AlertList.vue +++ b/dc3-web/src/views/home/components/AlertList.vue @@ -69,10 +69,10 @@ import {useEntityNames} from '@/composables/useEntityNames'; import type {AlertSource} from '@/config/types/dashboard'; interface AlertRow { - id: number | string; + id: string; source: AlertSource; - sourceId: number | string; - pointId: number | string; + sourceId: string; + pointId: string; eventTypeFlag: number; confirmFlag: string; createTime: string; diff --git a/dc3-web/src/views/home/components/LiveDataFeed.vue b/dc3-web/src/views/home/components/LiveDataFeed.vue index 69ff6236d..189a16319 100644 --- a/dc3-web/src/views/home/components/LiveDataFeed.vue +++ b/dc3-web/src/views/home/components/LiveDataFeed.vue @@ -70,9 +70,9 @@ import {streamLatest} from '@/api/dashboard'; import DashboardCard from '@/components/card/dashboard/DashboardCard.vue'; interface Row { - deviceId: number | string; - pointId: number | string; - driverId?: number | string; + deviceId: string; + pointId: string; + driverId?: string; // driverName / deviceName / pointName are populated server-side by // DashboardServiceImpl.latestStream via the metadata facades, so the // feed can render the full tuple without a separate lookup round-trip. diff --git a/dc3-web/src/views/profile/Profile.vue b/dc3-web/src/views/profile/Profile.vue index e51068055..26f7268ab 100644 --- a/dc3-web/src/views/profile/Profile.vue +++ b/dc3-web/src/views/profile/Profile.vue @@ -132,7 +132,7 @@ const addThing = (form: unknown, done: () => void) => { }); }; -const disableThing = (id: number | string, done: () => void) => { +const disableThing = (id: string, done: () => void) => { updateProfile({id: String(id), enableFlag: 'DISABLE'}) .then(() => { successMessage(); @@ -145,7 +145,7 @@ const disableThing = (id: number | string, done: () => void) => { }); }; -const enableThing = (id: number | string, done: () => void) => { +const enableThing = (id: string, done: () => void) => { updateProfile({id: String(id), enableFlag: 'ENABLE'}) .then(() => { successMessage(); @@ -158,7 +158,7 @@ const enableThing = (id: number | string, done: () => void) => { }); }; -const deleteThing = (id: number | string, done: () => void) => { +const deleteThing = (id: string, done: () => void) => { deleteProfile(String(id)) .then(() => { successMessage(); diff --git a/dc3-web/src/views/settings/agentic/AgenticSettings.vue b/dc3-web/src/views/settings/agentic/AgenticSettings.vue index a2717f1ad..7b8e958f6 100644 --- a/dc3-web/src/views/settings/agentic/AgenticSettings.vue +++ b/dc3-web/src/views/settings/agentic/AgenticSettings.vue @@ -118,7 +118,7 @@ const providers = ref([]); interface ModelConfigQuery { model?: string; - providerId?: string | number; + providerId?: string; enableFlag?: string; } diff --git a/dc3-web/src/views/settings/alarm/components/AlertCorrelation.vue b/dc3-web/src/views/settings/alarm/components/AlertCorrelation.vue index 066dd93f9..0efb5b88d 100644 --- a/dc3-web/src/views/settings/alarm/components/AlertCorrelation.vue +++ b/dc3-web/src/views/settings/alarm/components/AlertCorrelation.vue @@ -87,7 +87,7 @@ watch(locale, load); onMounted(load); onUnmounted(() => graph?.destroy()); -const nodeId = (source: string, id: number | string) => `${source}:${id}`; +const nodeId = (source: string, id: string) => `${source}:${id}`; const renderGraph = () => { const container = graphRef.value; @@ -95,7 +95,7 @@ const renderGraph = () => { graph?.destroy(); // Convert pairs → unique nodes + weighted edges. - const nodeMap = new Map(); + const nodeMap = new Map(); const edges: Array<{ source: string; target: string; data: { weight: number } }> = []; for (const p of pairs.value) { const aId = nodeId(p.aSource, p.aSourceId); diff --git a/dc3-web/src/views/settings/alarm/components/AlertStormSources.vue b/dc3-web/src/views/settings/alarm/components/AlertStormSources.vue index e95c228f2..91b33e66e 100644 --- a/dc3-web/src/views/settings/alarm/components/AlertStormSources.vue +++ b/dc3-web/src/views/settings/alarm/components/AlertStormSources.vue @@ -61,7 +61,7 @@ import type {AlertSource} from '@/config/types/dashboard'; interface StormRow { source: AlertSource; - sourceId: number | string; + sourceId: string; count: number; } diff --git a/dc3-web/src/views/settings/alarm/components/EventTable.vue b/dc3-web/src/views/settings/alarm/components/EventTable.vue index 05cb72e6c..e497fc018 100644 --- a/dc3-web/src/views/settings/alarm/components/EventTable.vue +++ b/dc3-web/src/views/settings/alarm/components/EventTable.vue @@ -191,10 +191,10 @@ import type {RangeKey} from '@/components/segmented/RangeSegmented.vue'; import RangeSegmented from '@/components/segmented/RangeSegmented.vue'; interface Row { - id: number | string; + id: string; source: 'point' | 'device' | 'driver'; - sourceId: number | string; - pointId: number | string; + sourceId: string; + pointId: string; eventTypeFlag: number; alarmLevelFlag?: number; confirmFlag: string; diff --git a/dc3-web/src/views/settings/alarm/components/RecentUnconfirmed.vue b/dc3-web/src/views/settings/alarm/components/RecentUnconfirmed.vue index 88265b9ea..0945cdf3d 100644 --- a/dc3-web/src/views/settings/alarm/components/RecentUnconfirmed.vue +++ b/dc3-web/src/views/settings/alarm/components/RecentUnconfirmed.vue @@ -60,9 +60,9 @@ import {useEntityNames} from '@/composables/useEntityNames'; import type {AlertSource} from '@/config/types/dashboard'; interface Row { - id: number | string; + id: string; source: AlertSource; - sourceId: number | string; + sourceId: string; createTime: string; message?: string; } diff --git a/dc3-web/src/views/settings/role/assign/RoleAssignResources.vue b/dc3-web/src/views/settings/role/assign/RoleAssignResources.vue index dae100081..4df944cde 100644 --- a/dc3-web/src/views/settings/role/assign/RoleAssignResources.vue +++ b/dc3-web/src/views/settings/role/assign/RoleAssignResources.vue @@ -101,7 +101,7 @@ import {listResourceByRoleId, listRoleResourceBind} from '@/api/roleResourceBind interface ResourceNode { id: string; - parentResourceId?: string | number; + parentResourceId?: string; resourceName: string; resourceCode: string; resourceTypeFlag: string; diff --git a/dc3-web/tests/e2e/fixtures/app.ts b/dc3-web/tests/e2e/fixtures/app.ts index b33eb0608..73c6aa89b 100644 --- a/dc3-web/tests/e2e/fixtures/app.ts +++ b/dc3-web/tests/e2e/fixtures/app.ts @@ -16,7 +16,6 @@ */ import {expect, type Locator, type Page} from '@playwright/test'; -import JSONBigInt from 'json-bigint'; import type {RouteIds} from './routes'; export interface PageHealth { @@ -60,7 +59,6 @@ export interface E2eDataContext { cleanup: () => Promise; } -const JSONBigIntStr = JSONBigInt({storeAsString: true}); const E2E_CREDENTIALS = { tenant: process.env.E2E_TENANT || 'default', name: process.env.E2E_USERNAME || 'dc3', @@ -194,7 +192,7 @@ export async function apiPost( let data: unknown; try { - data = JSONBigIntStr.parse(response.text); + data = JSON.parse(response.text); } catch { data = response.text; } @@ -244,7 +242,7 @@ export async function apiGet( let data: unknown; try { - data = JSONBigIntStr.parse(response.text); + data = JSON.parse(response.text); } catch { data = response.text; } diff --git a/dc3-web/tests/unit/axios.test.ts b/dc3-web/tests/unit/axios.test.ts index 7532b3b95..de5867284 100644 --- a/dc3-web/tests/unit/axios.test.ts +++ b/dc3-web/tests/unit/axios.test.ts @@ -52,7 +52,7 @@ describe('axios request instance', () => { notificationSpies.warnMessage.mockClear(); }); - it('injects auth headers and parses large integer JSON responses as strings', async () => { + it('injects auth headers and preserves string identifiers in JSON responses', async () => { setStorage(AUTH_HEADERS.TENANT, 'default'); setStorage(AUTH_HEADERS.LOGIN, 'dc3'); @@ -62,7 +62,7 @@ describe('axios request instance', () => { // Token is NOT injected — it travels in an httpOnly cookie. expect(config.headers.get(AUTH_HEADERS.TOKEN)).toBeUndefined(); - return responseOf(config, 200, '{"ok":true,"code":0,"message":"success","data":{"id":9007199254740993}}'); + return responseOf(config, 200, '{"ok":true,"code":0,"message":"success","data":{"id":"9007199254740993"}}'); }); const response = await request({