task-11: 拆分 dedupe 模块 API 至 types/modules/dedupe.ts(callJava+buildJavaUrl 化),java-modules.ts 改 re-export 保持旧 import 兼容

This commit is contained in:
2026-08-31 19:27:39 +08:00
parent 22422bc9d2
commit a4ca9d5a19
6 changed files with 240 additions and 68 deletions
@@ -0,0 +1,19 @@
import { buildJavaUrl } from './url.ts'
import { getCurrentUserId } from './user.ts'
export function getJavaDownloadUrl(path: string) {
let raw =
path.startsWith("http://") || path.startsWith("https://")
? path
: buildJavaUrl(path);
// pywebview 的 save_file_from_url_new 需要完整且带 schema 的 URL
if (
!raw.startsWith("http://") &&
!raw.startsWith("https://") &&
typeof window !== "undefined"
) {
raw = `${window.location.origin}${raw}`;
}
const separator = raw.includes("?") ? "&" : "?";
return `${raw}${separator}user_id=${encodeURIComponent(String(getCurrentUserId()))}`;
}
+5 -68
View File
@@ -6,9 +6,11 @@
put,
type JavaApiResponse,
unwrapJavaResponse,
} from "@/shared/api/http";
import { getTaskProgressCacheTtlMs, getTaskProgressTimeoutMs } from "@/shared/task-progress-config";
import { createTaskProgressRequestCache } from "@/shared/task-progress-request-cache";
} from "./http.ts";
import { getTaskProgressCacheTtlMs, getTaskProgressTimeoutMs } from "../task-progress-config.ts";
import { createTaskProgressRequestCache } from "../task-progress-request-cache.ts";
export * from "./types/modules/dedupe.ts";
const JAVA_API_PREFIX = "/newApi/api";
@@ -142,35 +144,6 @@ export async function uploadTempFileToJava(
return response.data
}
export interface DedupeResultItem {
resultId?: number;
sourceFilename: string;
outputFilename?: string;
success: boolean;
error?: string;
downloadUrl?: string;
}
export interface DedupeRunVo {
total: number;
successCount: number;
failedCount: number;
items: DedupeResultItem[];
}
export interface DedupeHistoryVo {
items: DedupeResultItem[];
}
export interface DedupeRunRequest {
files: UploadedFileRef[];
selectedColumns: string[];
keepIntegerIds: boolean;
keepUnderscoreIds: boolean;
keepIntegerMainIdsWhenNoSubIds: boolean;
archiveName?: string;
user_id: number;
}
export interface SplitArchiveEntry {
filename: string;
@@ -416,42 +389,6 @@ export function getExcelInfo(fileKey: string) {
);
}
export function runDedupe(
request: Omit<DedupeRunRequest, "user_id"> | DedupeRunRequest,
) {
return unwrapJavaResponse(
post<JavaApiResponse<DedupeRunVo>, DedupeRunRequest>(
`${JAVA_API_PREFIX}/dedupe/run`,
{
...request,
user_id: getCurrentUserId(),
},
),
);
}
export function getDedupeHistory() {
return unwrapJavaResponse(
get<JavaApiResponse<DedupeHistoryVo>>(`${JAVA_API_PREFIX}/dedupe/history`, {
params: { user_id: getCurrentUserId() },
}),
);
}
export function deleteDedupeHistory(resultId: number) {
return unwrapJavaResponse(
del<JavaApiResponse<null>>(
`${JAVA_API_PREFIX}/dedupe/history/${resultId}`,
{
params: { user_id: getCurrentUserId() },
},
),
);
}
export function getDedupeResultDownloadUrl(resultId: number) {
return getJavaDownloadUrl(`/dedupe/results/${resultId}/download`);
}
export function runSplit(
request: Omit<SplitRunRequest, "user_id"> | SplitRunRequest,
@@ -0,0 +1,78 @@
import { get, post, del, type JavaApiResponse, unwrapJavaResponse } from '../../http.ts'
import { buildJavaUrl } 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 DedupeResultItem {
resultId?: number;
sourceFilename: string;
outputFilename?: string;
success: boolean;
error?: string;
downloadUrl?: string;
}
export interface DedupeRunVo {
total: number;
successCount: number;
failedCount: number;
items: DedupeResultItem[];
}
export interface DedupeHistoryVo {
items: DedupeResultItem[];
}
export interface DedupeRunRequest {
files: UploadedFileRef[];
selectedColumns: string[];
keepIntegerIds: boolean;
keepUnderscoreIds: boolean;
keepIntegerMainIdsWhenNoSubIds: boolean;
archiveName?: string;
user_id: number;
}
export function runDedupe(
request: Omit<DedupeRunRequest, "user_id"> | DedupeRunRequest,
) {
return unwrapJavaResponse(
post<JavaApiResponse<DedupeRunVo>, DedupeRunRequest>(
buildJavaUrl(API_ENDPOINTS.dedupe.run),
{
...request,
user_id: getCurrentUserId(),
},
),
);
}
export function getDedupeHistory() {
return unwrapJavaResponse(
get<JavaApiResponse<DedupeHistoryVo>>(
buildJavaUrl(API_ENDPOINTS.dedupe.history),
{
params: { user_id: getCurrentUserId() },
},
),
);
}
export function deleteDedupeHistory(resultId: number) {
return unwrapJavaResponse(
del<JavaApiResponse<null>>(
buildJavaUrl(API_ENDPOINTS.dedupe.historyDelete.replace('{resultId}', String(resultId))),
{
params: { user_id: getCurrentUserId() },
},
),
);
}
export function getDedupeResultDownloadUrl(resultId: number) {
return getJavaDownloadUrl(
API_ENDPOINTS.dedupe.resultDownload.replace('{resultId}', String(resultId)),
);
}
@@ -0,0 +1,5 @@
export interface UploadedFileRef {
fileKey: string;
originalFilename?: string;
relativePath?: string;
}
+11
View File
@@ -0,0 +1,11 @@
export function getCurrentUserId() {
const raw =
typeof window === "undefined"
? ""
: window.localStorage.getItem("uid") || "";
const value = Number(raw);
if (!Number.isFinite(value) || value <= 0) {
throw new Error("未获取到用户ID");
}
return value;
}
+122
View File
@@ -0,0 +1,122 @@
import { test } from 'node:test'
import assert from 'node:assert/strict'
import { http } from '../src/shared/api/http.ts'
import {
runDedupe,
getDedupeHistory,
deleteDedupeHistory,
getDedupeResultDownloadUrl,
type DedupeRunRequest,
type DedupeRunVo,
} from '../src/shared/api/types/modules/dedupe.ts'
function setupWindow() {
;(globalThis as Record<string, unknown>).window = {
localStorage: { getItem: () => '42' },
location: { origin: 'http://localhost' },
}
}
function mockRequest(
t: Parameters<typeof test>[1] extends (t: infer T) => unknown ? T : never,
impl: (config: { url?: string; method?: string; params?: Record<string, unknown>; data?: unknown }) => Promise<unknown>,
) {
t.mock.method(http, 'request', impl as never)
}
const okResponse = (data: unknown) => Promise.resolve({ data: { success: true, message: 'ok', data } })
test('test_dedupe_run_url', async (t) => {
setupWindow()
let captured: { url?: string } = {}
mockRequest(t, (config) => {
captured = config
return okResponse({ total: 1, successCount: 1, failedCount: 0, items: [] })
})
await runDedupe({ files: [{ fileKey: 'k1' }], selectedColumns: ['a'], keepIntegerIds: false, keepUnderscoreIds: false, keepIntegerMainIdsWhenNoSubIds: true })
assert.equal(captured.url, '/newApi/api/dedupe/run')
})
test('test_dedupe_run_method_payload', async (t) => {
setupWindow()
let captured: { method?: string; data?: unknown } = {}
mockRequest(t, (config) => {
captured = config
return okResponse({ total: 0, successCount: 0, failedCount: 0, items: [] })
})
await runDedupe({ files: [{ fileKey: 'k1' }], selectedColumns: ['a'], keepIntegerIds: true, keepUnderscoreIds: true, keepIntegerMainIdsWhenNoSubIds: false })
assert.equal(captured.method, 'POST')
assert.deepEqual(captured.data, {
files: [{ fileKey: 'k1' }],
selectedColumns: ['a'],
keepIntegerIds: true,
keepUnderscoreIds: true,
keepIntegerMainIdsWhenNoSubIds: false,
user_id: 42,
})
})
test('test_dedupe_history_url', async (t) => {
setupWindow()
let captured: { url?: string; method?: string; params?: Record<string, unknown> } = {}
mockRequest(t, (config) => {
captured = config
return okResponse({ items: [] })
})
await getDedupeHistory()
assert.equal(captured.url, '/newApi/api/dedupe/history')
assert.equal(captured.method, 'GET')
assert.deepEqual(captured.params, { user_id: 42 })
})
test('test_dedupe_delete_history', async (t) => {
setupWindow()
let captured: { url?: string; method?: string } = {}
mockRequest(t, (config) => {
captured = config
return okResponse(null)
})
await deleteDedupeHistory(5)
assert.equal(captured.url, '/newApi/api/dedupe/history/5')
assert.equal(captured.method, 'DELETE')
})
test('test_dedupe_download_url', async (t) => {
setupWindow()
const url = getDedupeResultDownloadUrl(5)
assert.equal(url, 'http://localhost/newApi/api/dedupe/results/5/download?user_id=42')
})
test('test_dedupe_signature_unchanged', async (t) => {
setupWindow()
assert.equal(runDedupe.length, 1, 'runDedupe 应保持单参数')
assert.equal(getDedupeHistory.length, 0, 'getDedupeHistory 应保持无参')
assert.equal(deleteDedupeHistory.length, 1, 'deleteDedupeHistory 应保持单参数')
assert.equal(getDedupeResultDownloadUrl.length, 1, 'getDedupeResultDownloadUrl 应保持单参数')
const request: DedupeRunRequest = {
files: [{ fileKey: 'k' }],
selectedColumns: [],
keepIntegerIds: false,
keepUnderscoreIds: false,
keepIntegerMainIdsWhenNoSubIds: false,
user_id: 1,
}
assert.ok(request.user_id === 1)
})
test('test_dedupe_export_compat', async (t) => {
setupWindow()
const fromJavaModules = await import('../src/shared/api/java-modules.ts')
const fromDedupe = await import('../src/shared/api/types/modules/dedupe.ts')
assert.equal(fromJavaModules.runDedupe, fromDedupe.runDedupe, '旧路径 import 应与新路径同引用')
assert.equal(fromJavaModules.getDedupeHistory, fromDedupe.getDedupeHistory)
assert.equal(fromJavaModules.deleteDedupeHistory, fromDedupe.deleteDedupeHistory)
assert.equal(fromJavaModules.getDedupeResultDownloadUrl, fromDedupe.getDedupeResultDownloadUrl)
})
test('test_dedupe_unwrap', async (t) => {
setupWindow()
mockRequest(t, () => okResponse({ total: 2, successCount: 1, failedCount: 1, items: [{ success: true }] }))
const result = await runDedupe<DedupeRunVo>({ files: [{ fileKey: 'k' }], selectedColumns: [], keepIntegerIds: false, keepUnderscoreIds: false, keepIntegerMainIdsWhenNoSubIds: false })
assert.deepEqual(result, { total: 2, successCount: 1, failedCount: 1, items: [{ success: true }] }, '应返回解包后的 data')
})