From e5ebdf0a8ed0801cbad5a4a8b60d0ba4e4d8061a Mon Sep 17 00:00:00 2001 From: kdletters Date: Wed, 5 Aug 2026 21:48:30 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=BC=80=E5=8F=91=E8=80=85?= =?UTF-8?q?=E5=AF=86=E9=92=A5=E6=97=B6=E9=97=B4=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 复用个人中心时间解析逻辑格式化秒级小数时间戳 统一展示密钥创建时间和最近使用时间 补充弹窗回归测试与接入文档约束 --- ...构】外部OpenAPI与APIKey接入方案-2026-06-19.md | 2 +- .../PlatformProfileApiKeysModal.tsx | 10 +---- .../platformProfileApiKeysModal.test.ts | 45 +++++++++++++++++++ 3 files changed, 48 insertions(+), 9 deletions(-) create mode 100644 src/components/platform-entry/platformProfileApiKeysModal.test.ts diff --git a/docs/【后端架构】外部OpenAPI与APIKey接入方案-2026-06-19.md b/docs/【后端架构】外部OpenAPI与APIKey接入方案-2026-06-19.md index 876b2e86b..37a438a64 100644 --- a/docs/【后端架构】外部OpenAPI与APIKey接入方案-2026-06-19.md +++ b/docs/【后端架构】外部OpenAPI与APIKey接入方案-2026-06-19.md @@ -123,7 +123,7 @@ POST /api/profile/api-keys DELETE /api/profile/api-keys/{keyId} ``` -前端入口位于登录后个人中心的 `我的 → 开发者 API Key`,用于查看当前 Key、创建新 Key、复制一次性明文和撤销已创建 Key。外部 OpenAPI 只描述 `/api/external/v1` 下可由 API Key 调用的接口,不混入登录态 API Key 管理接口。 +前端入口位于登录后个人中心的 `我的 → 开发者 API Key`,用于查看当前 Key、创建新 Key、复制一次性明文和撤销已创建 Key。Key 卡片中的创建时间和最近使用时间必须格式化为 `YYYY-MM-DD`,不得直接展示后端时间戳。外部 OpenAPI 只描述 `/api/external/v1` 下可由 API Key 调用的接口,不混入登录态 API Key 管理接口。 ## 版本与兼容策略 diff --git a/src/components/platform-entry/PlatformProfileApiKeysModal.tsx b/src/components/platform-entry/PlatformProfileApiKeysModal.tsx index 1ee0a4758..108280cef 100644 --- a/src/components/platform-entry/PlatformProfileApiKeysModal.tsx +++ b/src/components/platform-entry/PlatformProfileApiKeysModal.tsx @@ -21,6 +21,7 @@ import { PlatformStatusMessage } from '../common/PlatformStatusMessage'; import { PlatformTextField } from '../common/PlatformTextField'; import { useCopyFeedback } from '../common/useCopyFeedback'; import { PlatformProfileSecondaryModalShell } from './PlatformProfileModalShell'; +import { formatPlatformProfileTime } from './platformProfileFundsModel'; type PlatformProfileApiKeysModalProps = { onClose: () => void; @@ -34,14 +35,7 @@ function buildApiKeyTimeLabel(value: string | null) { if (!value) { return '尚未使用'; } - const date = new Date(value); - if (Number.isNaN(date.getTime())) { - return value; - } - const year = date.getUTCFullYear(); - const month = String(date.getUTCMonth() + 1).padStart(2, '0'); - const day = String(date.getUTCDate()).padStart(2, '0'); - return `${year}-${month}-${day}`; + return formatPlatformProfileTime(value); } function buildApiKeyScopeLabel(scopes: string[]) { diff --git a/src/components/platform-entry/platformProfileApiKeysModal.test.ts b/src/components/platform-entry/platformProfileApiKeysModal.test.ts new file mode 100644 index 000000000..ebd15fec5 --- /dev/null +++ b/src/components/platform-entry/platformProfileApiKeysModal.test.ts @@ -0,0 +1,45 @@ +/* @vitest-environment jsdom */ + +import { render, screen } from '@testing-library/react'; +import { createElement } from 'react'; +import { beforeEach, describe, expect, test, vi } from 'vitest'; + +import { PlatformProfileApiKeysModal } from './PlatformProfileApiKeysModal'; + +const listExternalApiKeysMock = vi.hoisted(() => vi.fn()); + +vi.mock('../../services/platform-entry/platformProfileClient', () => ({ + createPlatformProfileExternalApiKey: vi.fn(), + listPlatformProfileExternalApiKeys: listExternalApiKeysMock, + revokePlatformProfileExternalApiKey: vi.fn(), +})); + +describe('PlatformProfileApiKeysModal', () => { + beforeEach(() => { + listExternalApiKeysMock.mockReset(); + }); + + test('将 API Key 时间戳格式化为日期', async () => { + listExternalApiKeysMock.mockResolvedValueOnce({ + keys: [ + { + keyId: 'api-key-1', + name: '外部 API Key', + keyPrefix: 'tnr_sk_example', + scopes: ['editor:project'], + createdAt: '1785913407.182731Z', + lastUsedAt: null, + revokedAt: null, + updatedAt: '1785913407.182731Z', + }, + ], + }); + + render(createElement(PlatformProfileApiKeysModal, { onClose: vi.fn() })); + + expect( + await screen.findByText('创建 2026-08-05 · 最近使用 尚未使用'), + ).toBeTruthy(); + expect(screen.queryByText(/1785913407\.182731Z/u)).toBeNull(); + }); +});