feat: connect resource library workflows
This commit is contained in:
150
src/lib/validation/library-resource.ts
Normal file
150
src/lib/validation/library-resource.ts
Normal file
@@ -0,0 +1,150 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { RouteError } from "@/lib/http/response";
|
||||
|
||||
export const backendLibraryResourceTypeSchema = z.enum(["model", "scene", "garment"]);
|
||||
export const backendLibraryFileRoleSchema = z.enum(["original", "thumbnail", "gallery"]);
|
||||
|
||||
const presignUploadSchema = z.object({
|
||||
resourceType: backendLibraryResourceTypeSchema,
|
||||
fileRole: backendLibraryFileRoleSchema,
|
||||
fileName: z.string().trim().min(1),
|
||||
contentType: z.string().trim().min(1),
|
||||
});
|
||||
|
||||
const createLibraryFileSchema = z.object({
|
||||
fileRole: backendLibraryFileRoleSchema,
|
||||
storageKey: z.string().trim().min(1),
|
||||
publicUrl: z.string().trim().min(1),
|
||||
mimeType: z.string().trim().min(1),
|
||||
sizeBytes: z.number().int().nonnegative(),
|
||||
sortOrder: z.number().int().nonnegative().default(0),
|
||||
width: z.number().int().positive().optional(),
|
||||
height: z.number().int().positive().optional(),
|
||||
});
|
||||
|
||||
const createLibraryResourceSchema = z.object({
|
||||
name: z.string().trim().min(1),
|
||||
description: z.string().trim().optional().default(""),
|
||||
tags: z.array(z.string().trim()).default([]),
|
||||
gender: z.string().trim().optional(),
|
||||
ageGroup: z.string().trim().optional(),
|
||||
poseId: z.number().int().positive().optional(),
|
||||
environment: z.string().trim().optional(),
|
||||
category: z.string().trim().optional(),
|
||||
files: z.array(createLibraryFileSchema).min(2),
|
||||
});
|
||||
|
||||
const updateLibraryResourceSchema = z
|
||||
.object({
|
||||
name: z.string().trim().min(1).optional(),
|
||||
description: z.string().trim().optional(),
|
||||
tags: z.array(z.string().trim()).optional(),
|
||||
gender: z.string().trim().optional(),
|
||||
ageGroup: z.string().trim().optional(),
|
||||
poseId: z.number().int().positive().optional(),
|
||||
environment: z.string().trim().optional(),
|
||||
category: z.string().trim().optional(),
|
||||
coverFileId: z.number().int().positive().optional(),
|
||||
})
|
||||
.refine((value) => Object.keys(value).length > 0, {
|
||||
message: "至少提供一个可更新字段。",
|
||||
});
|
||||
|
||||
export type CreateLibraryResourcePayload = z.infer<typeof createLibraryResourceSchema>;
|
||||
export type UpdateLibraryResourcePayload = z.infer<typeof updateLibraryResourceSchema>;
|
||||
|
||||
export function parseLibraryUploadPresignPayload(payload: unknown) {
|
||||
const result = presignUploadSchema.safeParse(payload);
|
||||
|
||||
if (!result.success) {
|
||||
throw new RouteError(
|
||||
400,
|
||||
"VALIDATION_ERROR",
|
||||
"上传参数不合法。",
|
||||
result.error.flatten(),
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
resource_type: result.data.resourceType,
|
||||
file_role: result.data.fileRole,
|
||||
file_name: result.data.fileName,
|
||||
content_type: result.data.contentType,
|
||||
};
|
||||
}
|
||||
|
||||
export function parseCreateLibraryResourcePayload(
|
||||
payload: unknown,
|
||||
resourceType: z.infer<typeof backendLibraryResourceTypeSchema>,
|
||||
) {
|
||||
const result = createLibraryResourceSchema.safeParse(payload);
|
||||
|
||||
if (!result.success) {
|
||||
throw new RouteError(
|
||||
400,
|
||||
"VALIDATION_ERROR",
|
||||
"资源参数不合法。",
|
||||
result.error.flatten(),
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
resource_type: resourceType,
|
||||
name: result.data.name,
|
||||
description: result.data.description,
|
||||
tags: result.data.tags.filter(Boolean),
|
||||
gender: result.data.gender,
|
||||
age_group: result.data.ageGroup,
|
||||
pose_id: result.data.poseId,
|
||||
environment: result.data.environment,
|
||||
category: result.data.category,
|
||||
files: result.data.files.map((file) => ({
|
||||
file_role: file.fileRole,
|
||||
storage_key: file.storageKey,
|
||||
public_url: file.publicUrl,
|
||||
mime_type: file.mimeType,
|
||||
size_bytes: file.sizeBytes,
|
||||
sort_order: file.sortOrder,
|
||||
width: file.width,
|
||||
height: file.height,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
export function parseUpdateLibraryResourcePayload(payload: unknown) {
|
||||
const result = updateLibraryResourceSchema.safeParse(payload);
|
||||
|
||||
if (!result.success) {
|
||||
throw new RouteError(
|
||||
400,
|
||||
"VALIDATION_ERROR",
|
||||
"资源更新参数不合法。",
|
||||
result.error.flatten(),
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
...(result.data.name !== undefined ? { name: result.data.name } : {}),
|
||||
...(result.data.description !== undefined
|
||||
? { description: result.data.description }
|
||||
: {}),
|
||||
...(result.data.tags !== undefined
|
||||
? { tags: result.data.tags.filter(Boolean) }
|
||||
: {}),
|
||||
...(result.data.gender !== undefined ? { gender: result.data.gender } : {}),
|
||||
...(result.data.ageGroup !== undefined
|
||||
? { age_group: result.data.ageGroup }
|
||||
: {}),
|
||||
...(result.data.poseId !== undefined ? { pose_id: result.data.poseId } : {}),
|
||||
...(result.data.environment !== undefined
|
||||
? { environment: result.data.environment }
|
||||
: {}),
|
||||
...(result.data.category !== undefined
|
||||
? { category: result.data.category }
|
||||
: {}),
|
||||
...(result.data.coverFileId !== undefined
|
||||
? { cover_file_id: result.data.coverFileId }
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user