import type { APIRequestContext } from '@playwright/test' import { adminCredentials } from './helpers' export interface ContentAssetRecord { id: string versionId: string code: string name: string type: string storageUri: string mimeType: string sizeBytes: number sha256: string metadata: Record status: string sortOrder: number } export interface ContentDependencyRecord { id: string targetProjectId: string targetVersionId: string relationType: string required: boolean sortOrder: number } export interface ContentDetailRecord { project: { id: string type: string code: string name: string status: string currentVersionId: string publishedVersionId: string | null version: number } currentVersion: { id: string projectId: string versionNo: number versionCode: string status: string content: Record dependencies: ContentDependencyRecord[] assets: ContentAssetRecord[] } } interface ApiEnvelope { data?: T } export interface ContentApiSession { headers: Record } export type CleanupOutcome = 'removed' | 'not-found' | 'failed' | 'not-created' export interface CleanupRecord { kind: '模型工程' | '场景工程' id: string | null code: string outcome: CleanupOutcome statusCode?: number message?: string } const endpoint = (projectId: string) => `/api/tran/v1/content/projects/${encodeURIComponent(projectId)}` const readData = async (response: Awaited>, operation: string): Promise => { if (!response.ok()) throw new Error(`${operation}失败(HTTP ${response.status()})`) const body = await response.json() as ApiEnvelope if (body.data === undefined) throw new Error(`${operation}失败:响应未包含 data`) return body.data } export async function openContentApiSession(request: APIRequestContext): Promise { const credentials = adminCredentials() const response = await request.post('/api/auth/v1/auth/login', { data: { username: credentials.username, password: credentials.password, roleCode: 'admin', rememberMe: false, }, }) const body = await readData<{ accessToken?: string }>(response, 'E2E API 登录') if (!body.accessToken) throw new Error('E2E API 登录失败:响应未返回访问令牌') return { headers: { Authorization: `Bearer ${body.accessToken}` } } } export async function closeContentApiSession( request: APIRequestContext, session: ContentApiSession | null, ): Promise { if (!session) return await request.post('/api/auth/v1/auth/logout', { headers: session.headers }).catch(() => undefined) } export async function readContentDetail( request: APIRequestContext, session: ContentApiSession, projectId: string, ): Promise { const response = await request.get(endpoint(projectId), { headers: session.headers }) return readData(response, '读取内容工程') } export async function removeContentProject( request: APIRequestContext, session: ContentApiSession, kind: CleanupRecord['kind'], projectId: string | null, code: string, ): Promise { if (!projectId) return { kind, id: null, code, outcome: 'not-created' } try { const detailResponse = await request.get(endpoint(projectId), { headers: session.headers }) if (detailResponse.status() === 404) return { kind, id: projectId, code, outcome: 'not-found' } if (!detailResponse.ok()) { return { kind, id: projectId, code, outcome: 'failed', statusCode: detailResponse.status(), message: '清理前读取工程失败', } } const detailBody = await detailResponse.json() as ApiEnvelope const projectVersion = detailBody.data?.project.version if (!Number.isInteger(projectVersion)) { return { kind, id: projectId, code, outcome: 'failed', message: '清理前未取得工程乐观锁版本' } } const deleteResponse = await request.delete(endpoint(projectId), { headers: session.headers, params: { version: String(projectVersion) }, }) if (deleteResponse.ok()) return { kind, id: projectId, code, outcome: 'removed' } if (deleteResponse.status() === 404) return { kind, id: projectId, code, outcome: 'not-found' } return { kind, id: projectId, code, outcome: 'failed', statusCode: deleteResponse.status(), message: '服务端拒绝删除测试工程', } } catch { return { kind, id: projectId, code, outcome: 'failed', message: '清理 API 调用异常' } } }