From fdbf6812925169a33f6d914e80e3e3264f7d00a0 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:31:10 +0800 Subject: [PATCH] =?UTF-8?q?task-13:=20=E6=8B=86=E5=88=86=20convert=20?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=20API=20=E8=87=B3=20types/modules/convert.ts?= =?UTF-8?q?=EF=BC=8Cjava-modules.ts=20re-export?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - getExcelInfo/runConvert/getConvertHistory/deleteConvertHistory/getConvertResultDownloadUrl/getConvertTemplates/importConvertTemplate/setDefaultConvertTemplate/deleteConvertTemplate - 模板相关类型 ConvertTemplateVo/ConvertTemplateImportRequest/ExcelInfoVo 随模块迁移 - 9 个测试:URL/method/payload/history/模板系列/签名/export compat/unwrap --- frontend-vue/src/shared/api/java-modules.ts | 129 +------------- .../src/shared/api/types/modules/convert.ts | 136 +++++++++++++++ frontend-vue/tests/modules-convert.test.ts | 160 ++++++++++++++++++ 3 files changed, 297 insertions(+), 128 deletions(-) create mode 100644 frontend-vue/src/shared/api/types/modules/convert.ts create mode 100644 frontend-vue/tests/modules-convert.test.ts diff --git a/frontend-vue/src/shared/api/java-modules.ts b/frontend-vue/src/shared/api/java-modules.ts index 2d04f015..34cf90be 100644 --- a/frontend-vue/src/shared/api/java-modules.ts +++ b/frontend-vue/src/shared/api/java-modules.ts @@ -12,6 +12,7 @@ import { createTaskProgressRequestCache } from "../task-progress-request-cache.t export * from "./types/modules/dedupe.ts"; export * from "./types/modules/split.ts"; +export * from "./types/modules/convert.ts"; const JAVA_API_PREFIX = "/newApi/api"; @@ -146,33 +147,6 @@ export async function uploadTempFileToJava( } -export interface ConvertResultItem { - resultId?: number; - sourceFilename: string; - outputFilename?: string; - success: boolean; - error?: string; - downloadUrl?: string; -} - -export interface ConvertRunVo { - total: number; - successCount: number; - failedCount: number; - items: ConvertResultItem[]; -} - -export interface ConvertHistoryVo { - items: ConvertResultItem[]; -} - -export interface ConvertRunRequest { - files: UploadedFileRef[]; - templateId: string; - archiveName?: string; - user_id: number; -} - export interface DeleteBrandPreviewRow { rowIndex: number; country: string; @@ -325,107 +299,6 @@ export interface DeleteBrandRunRequest { user_id: number; } -export interface ConvertTemplateVo { - id: string; - templateCode: string; - templateName: string; - outputFilename: string; - isDefault?: boolean; - builtIn?: boolean; -} - -export interface ConvertTemplateImportRequest { - templateName: string; - templateContent: string; -} - -export interface ExcelInfoVo { - headers: string[]; - totalRows: number; -} - -export function getExcelInfo(fileKey: string) { - return unwrapJavaResponse( - get>(`${JAVA_API_PREFIX}/files/excel-info`, { - params: { fileKey }, - }), - ); -} - - -export function getConvertTemplates() { - return unwrapJavaResponse( - get>( - `${JAVA_API_PREFIX}/convert/templates`, - ), - ); -} - -export function importConvertTemplate(request: ConvertTemplateImportRequest) { - return unwrapJavaResponse( - post, ConvertTemplateImportRequest>( - `${JAVA_API_PREFIX}/convert/templates/import`, - request, - ), - ); -} - -export function setDefaultConvertTemplate(templateCode: string) { - return unwrapJavaResponse( - post>( - `${JAVA_API_PREFIX}/convert/templates/${templateCode}/default`, - ), - ); -} - -export function deleteConvertTemplate(templateCode: string) { - return unwrapJavaResponse( - del>( - `${JAVA_API_PREFIX}/convert/templates/${templateCode}`, - ), - ); -} - -export function runConvert( - request: Omit | ConvertRunRequest, -) { - return unwrapJavaResponse( - post, ConvertRunRequest>( - `${JAVA_API_PREFIX}/convert/run`, - { - ...request, - user_id: getCurrentUserId(), - }, - ), - ); -} - -export function getConvertHistory() { - return unwrapJavaResponse( - get>( - `${JAVA_API_PREFIX}/convert/history`, - { - params: { user_id: getCurrentUserId() }, - }, - ), - ); -} - -export function deleteConvertHistory(resultId: number) { - return unwrapJavaResponse( - del>( - `${JAVA_API_PREFIX}/convert/history/${resultId}`, - { - params: { user_id: getCurrentUserId() }, - }, - ), - ); -} - -export function getConvertResultDownloadUrl(resultId: number) { - return getJavaDownloadUrl(`/convert/results/${resultId}/download`); -} - export function runDeleteBrand( request: Omit | DeleteBrandRunRequest, ) { diff --git a/frontend-vue/src/shared/api/types/modules/convert.ts b/frontend-vue/src/shared/api/types/modules/convert.ts new file mode 100644 index 00000000..b0e69488 --- /dev/null +++ b/frontend-vue/src/shared/api/types/modules/convert.ts @@ -0,0 +1,136 @@ +import { get, post, del, type JavaApiResponse, unwrapJavaResponse } from '../../http.ts' +import { buildJavaUrl, JAVA_API_PREFIX } from '../../url.ts' +import { getCurrentUserId } from '../../user.ts' +import { getJavaDownloadUrl } from '../../download-url.ts' +import type { UploadedFileRef } from '../upload.ts' +import { API_ENDPOINTS } from '../../endpoints.ts' + +export interface ConvertResultItem { + resultId?: number; + sourceFilename: string; + outputFilename?: string; + success: boolean; + error?: string; + downloadUrl?: string; +} + +export interface ConvertRunVo { + total: number; + successCount: number; + failedCount: number; + items: ConvertResultItem[]; +} + +export interface ConvertHistoryVo { + items: ConvertResultItem[]; +} + +export interface ConvertRunRequest { + files: UploadedFileRef[]; + templateId: string; + archiveName?: string; + user_id: number; +} + +export interface ConvertTemplateVo { + id: string; + templateCode: string; + templateName: string; + outputFilename: string; + isDefault?: boolean; + builtIn?: boolean; +} + +export interface ConvertTemplateImportRequest { + templateName: string; + templateContent: string; +} + +export interface ExcelInfoVo { + headers: string[]; + totalRows: number; +} + +export function getExcelInfo(fileKey: string) { + return unwrapJavaResponse( + get>(`${JAVA_API_PREFIX}/files/excel-info`, { + params: { fileKey }, + }), + ); +} + + +export function getConvertTemplates() { + return unwrapJavaResponse( + get>( + buildJavaUrl(API_ENDPOINTS.convert.templates), + ), + ); +} + +export function importConvertTemplate(request: ConvertTemplateImportRequest) { + return unwrapJavaResponse( + post, ConvertTemplateImportRequest>( + buildJavaUrl(API_ENDPOINTS.convert.templatesImport), + request, + ), + ); +} + +export function setDefaultConvertTemplate(templateCode: string) { + return unwrapJavaResponse( + post>( + buildJavaUrl(API_ENDPOINTS.convert.templateDefault.replace('{templateCode}', templateCode)), + ), + ); +} + +export function deleteConvertTemplate(templateCode: string) { + return unwrapJavaResponse( + del>( + buildJavaUrl(API_ENDPOINTS.convert.templateDelete.replace('{templateCode}', templateCode)), + ), + ); +} + +export function runConvert( + request: Omit | ConvertRunRequest, +) { + return unwrapJavaResponse( + post, ConvertRunRequest>( + buildJavaUrl(API_ENDPOINTS.convert.run), + { + ...request, + user_id: getCurrentUserId(), + }, + ), + ); +} + +export function getConvertHistory() { + return unwrapJavaResponse( + get>( + buildJavaUrl(API_ENDPOINTS.convert.history), + { + params: { user_id: getCurrentUserId() }, + }, + ), + ); +} + +export function deleteConvertHistory(resultId: number) { + return unwrapJavaResponse( + del>( + buildJavaUrl(API_ENDPOINTS.convert.historyDelete.replace('{resultId}', String(resultId))), + { + params: { user_id: getCurrentUserId() }, + }, + ), + ); +} + +export function getConvertResultDownloadUrl(resultId: number) { + return getJavaDownloadUrl( + API_ENDPOINTS.convert.resultDownload.replace('{resultId}', String(resultId)), + ); +} diff --git a/frontend-vue/tests/modules-convert.test.ts b/frontend-vue/tests/modules-convert.test.ts new file mode 100644 index 00000000..bfb1dfc7 --- /dev/null +++ b/frontend-vue/tests/modules-convert.test.ts @@ -0,0 +1,160 @@ +import { test } from 'node:test' +import assert from 'node:assert/strict' +import { http } from '../src/shared/api/http.ts' +import { + getExcelInfo, + getConvertTemplates, + importConvertTemplate, + setDefaultConvertTemplate, + deleteConvertTemplate, + runConvert, + getConvertHistory, + deleteConvertHistory, + getConvertResultDownloadUrl, + type ConvertRunRequest, + type ConvertTemplateVo, + type ConvertRunVo, +} from '../src/shared/api/types/modules/convert.ts' + +function setupWindow() { + ;(globalThis as Record).window = { + localStorage: { getItem: () => '42' }, + location: { origin: 'http://localhost' }, + } +} + +function mockRequest( + t: Parameters[1] extends (t: infer T) => unknown ? T : never, + impl: (config: { url?: string; method?: string; params?: Record; data?: unknown }) => Promise, +) { + t.mock.method(http, 'request', impl as never) +} + +const okResponse = (data: unknown) => Promise.resolve({ data: { success: true, message: 'ok', data } }) + +const convertRequest: Omit = { + files: [{ fileKey: 'k1' }], + templateId: 'TPL001', + archiveName: '转换结果', +} + +test('test_convert_excel_info_url', async (t) => { + setupWindow() + let captured: { url?: string; params?: Record } = {} + mockRequest(t, (config) => { + captured = config + return okResponse({ headers: ['a', 'b'], totalRows: 10 }) + }) + await getExcelInfo('key-123') + assert.equal(captured.url, '/newApi/api/files/excel-info') + assert.deepEqual(captured.params, { fileKey: 'key-123' }) +}) + +test('test_convert_run_url_payload', async (t) => { + setupWindow() + let captured: { url?: string; method?: string; data?: unknown } = {} + mockRequest(t, (config) => { + captured = config + return okResponse({ total: 1, successCount: 1, failedCount: 0, items: [] }) + }) + await runConvert(convertRequest) + assert.equal(captured.url, '/newApi/api/convert/run') + assert.equal(captured.method, 'POST') + assert.deepEqual(captured.data, { ...convertRequest, user_id: 42 }) +}) + +test('test_convert_history', async (t) => { + setupWindow() + let captured: { url?: string; method?: string; params?: Record } = {} + mockRequest(t, (config) => { + captured = config + return okResponse({ items: [] }) + }) + await getConvertHistory() + assert.equal(captured.url, '/newApi/api/convert/history') + assert.equal(captured.method, 'GET') + assert.deepEqual(captured.params, { user_id: 42 }) +}) + +test('test_convert_delete_history', async (t) => { + setupWindow() + let captured: { url?: string; method?: string; params?: Record } = {} + mockRequest(t, (config) => { + captured = config + return okResponse(null) + }) + await deleteConvertHistory(7) + assert.equal(captured.url, '/newApi/api/convert/history/7') + assert.equal(captured.method, 'DELETE') + assert.deepEqual(captured.params, { user_id: 42 }) +}) + +test('test_convert_download_url', async (t) => { + setupWindow() + const url = getConvertResultDownloadUrl(7) + assert.equal(url, 'http://localhost/newApi/api/convert/results/7/download?user_id=42') +}) + +test('test_convert_template_apis', async (t) => { + setupWindow() + const calls: { url?: string; method?: string; data?: unknown }[] = [] + mockRequest(t, (config) => { + calls.push(config) + return okResponse(null) + }) + await getConvertTemplates() + await importConvertTemplate({ templateName: '模板A', templateContent: 'x' }) + await setDefaultConvertTemplate('TPL001') + await deleteConvertTemplate('TPL001') + assert.equal(calls.length, 4) + assert.equal(calls[0].url, '/newApi/api/convert/templates') + assert.equal(calls[0].method, 'GET') + assert.equal(calls[1].url, '/newApi/api/convert/templates/import') + assert.equal(calls[1].method, 'POST') + assert.deepEqual(calls[1].data, { templateName: '模板A', templateContent: 'x' }) + assert.equal(calls[2].url, '/newApi/api/convert/templates/TPL001/default') + assert.equal(calls[2].method, 'POST') + assert.equal(calls[3].url, '/newApi/api/convert/templates/TPL001') + assert.equal(calls[3].method, 'DELETE') +}) + +test('test_convert_signature_unchanged', async (t) => { + setupWindow() + assert.equal(getExcelInfo.length, 1) + assert.equal(getConvertTemplates.length, 0) + assert.equal(importConvertTemplate.length, 1) + assert.equal(setDefaultConvertTemplate.length, 1) + assert.equal(deleteConvertTemplate.length, 1) + assert.equal(runConvert.length, 1) + assert.equal(getConvertHistory.length, 0) + assert.equal(deleteConvertHistory.length, 1) + assert.equal(getConvertResultDownloadUrl.length, 1) + const template: ConvertTemplateVo = { id: '1', templateCode: 'TPL001', templateName: '模板A', outputFilename: 'out.xlsx' } + assert.equal(template.builtIn, undefined) +}) + +test('test_convert_export_compat', async (t) => { + setupWindow() + const fromJavaModules = await import('../src/shared/api/java-modules.ts') + const fromConvert = await import('../src/shared/api/types/modules/convert.ts') + assert.equal(fromJavaModules.getExcelInfo, fromConvert.getExcelInfo) + assert.equal(fromJavaModules.runConvert, fromConvert.runConvert) + assert.equal(fromJavaModules.getConvertHistory, fromConvert.getConvertHistory) + assert.equal(fromJavaModules.deleteConvertHistory, fromConvert.deleteConvertHistory) + assert.equal(fromJavaModules.getConvertResultDownloadUrl, fromConvert.getConvertResultDownloadUrl) + assert.equal(fromJavaModules.getConvertTemplates, fromConvert.getConvertTemplates) + assert.equal(fromJavaModules.importConvertTemplate, fromConvert.importConvertTemplate) + assert.equal(fromJavaModules.setDefaultConvertTemplate, fromConvert.setDefaultConvertTemplate) + assert.equal(fromJavaModules.deleteConvertTemplate, fromConvert.deleteConvertTemplate) +}) + +test('test_convert_unwrap_and_error', async (t) => { + setupWindow() + mockRequest(t, () => okResponse({ total: 1, successCount: 0, failedCount: 1, items: [{ sourceFilename: 'a', success: false, error: '转换失败' }] })) + const result = await runConvert(convertRequest) + assert.equal(result.failedCount, 1) + assert.equal(result.items[0].error, '转换失败') + + mockRequest(t, () => Promise.resolve({ data: { success: false, message: '服务不可用' } })) + await assert.rejects(runConvert(convertRequest), /服务不可用/, 'success=false 应回传异常') +})