From f63ab837f5fc82dfd08e21bbf47de0a1a6ed8bdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E8=87=AA=E8=BE=BE?= <980324341@qq.com> Date: Mon, 31 Aug 2026 19:16:19 +0800 Subject: [PATCH] =?UTF-8?q?task-2:=20buildJavaUrl()=20=E7=BB=9F=E4=B8=80?= =?UTF-8?q?=20URL=20=E6=9E=84=E9=80=A0=EF=BC=88query=20=E7=BC=96=E7=A0=81?= =?UTF-8?q?=E3=80=81=E5=B0=BE=E6=96=9C=E6=9D=A0=E8=A7=84=E8=8C=83=E5=8C=96?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend-vue/src/shared/api/url.ts | 25 +++++++++++++++ frontend-vue/tests/url.test.ts | 51 ++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 frontend-vue/src/shared/api/url.ts create mode 100644 frontend-vue/tests/url.test.ts diff --git a/frontend-vue/src/shared/api/url.ts b/frontend-vue/src/shared/api/url.ts new file mode 100644 index 00000000..96e6673d --- /dev/null +++ b/frontend-vue/src/shared/api/url.ts @@ -0,0 +1,25 @@ +/** Java 主后端 URL 构造:常量表值已含 /api 前缀,此处只加 /newApi 网关前缀 */ +export const JAVA_API_PREFIX = '/newApi/api' + +function buildQueryString(query?: Record): string { + if (!query) return '' + const params = new URLSearchParams() + for (const [key, value] of Object.entries(query)) { + if (value !== undefined && value !== null) { + params.append(key, String(value)) + } + } + const text = params.toString().replace(/\+/g, '%20') + return text ? `?${text}` : '' +} + +export function buildJavaUrl( + endpoint: string, + query?: Record, +): string { + const normalized = endpoint.endsWith('/') ? endpoint.slice(0, -1) : endpoint + const prefix = normalized.startsWith('/api') + ? '/newApi' + : JAVA_API_PREFIX + return `${prefix}${normalized}${buildQueryString(query)}` +} diff --git a/frontend-vue/tests/url.test.ts b/frontend-vue/tests/url.test.ts new file mode 100644 index 00000000..5e6149c1 --- /dev/null +++ b/frontend-vue/tests/url.test.ts @@ -0,0 +1,51 @@ +import { test } from 'node:test' +import assert from 'node:assert/strict' +import { buildJavaUrl } from '../src/shared/api/url.ts' + +test('test_build_url_prefixed', () => { + assert.equal(buildJavaUrl('/api/dedupe/run'), '/newApi/api/dedupe/run') + assert.equal(buildJavaUrl('/api/similar-asin/parse'), '/newApi/api/similar-asin/parse') +}) + +test('test_build_url_with_query', () => { + const url = buildJavaUrl('/api/dedupe/history', { resultId: 42 }) + assert.equal(url, '/newApi/api/dedupe/history?resultId=42') +}) + +test('test_build_url_no_query', () => { + const url = buildJavaUrl('/api/publish/dashboard') + assert.ok(!url.includes('?'), '无 query 时不应追加 ?') + assert.equal(url, '/newApi/api/publish/dashboard') +}) + +test('test_build_url_query_encoding', () => { + const url = buildJavaUrl('/api/query-asin/tasks', { + shopName: 'a b&c', + keyword: '中文', + }) + const encoded = encodeURIComponent('a b&c') + assert.equal(url, `/newApi/api/query-asin/tasks?shopName=${encoded}&keyword=${encodeURIComponent('中文')}`) +}) + +test('test_build_url_empty_query_object', () => { + const url = buildJavaUrl('/api/withdraw/candidates', {}) + assert.ok(!url.includes('?'), '空对象不应追加 ?') + assert.equal(url, '/newApi/api/withdraw/candidates') +}) + +test('test_build_url_trailing_slash_endpoint', () => { + const url = buildJavaUrl('/api/split/run/') + assert.equal(url, '/newApi/api/split/run', 'endpoint 尾斜杠应被规范化') +}) + +test('test_build_url_undefined_query', () => { + const url = buildJavaUrl('/api/files/upload', undefined) + assert.ok(!url.includes('?'), 'undefined query 不应追加 ?') + assert.equal(url, '/newApi/api/files/upload') +}) + +test('test_build_url_join_no_duplicate_slash', () => { + assert.equal(buildJavaUrl('/api/dedupe/run'), '/newApi/api/dedupe/run') + assert.ok(!buildJavaUrl('/api/dedupe/run').includes('/newApi//api')) + assert.ok(!buildJavaUrl('/api/dedupe/run').includes('//api/')) +})