feat: bootstrap auto virtual tryon admin frontend
This commit is contained in:
121
src/lib/adapters/orders.ts
Normal file
121
src/lib/adapters/orders.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import type {
|
||||
AssetDto,
|
||||
AssetType,
|
||||
OrderDetailResponseDto,
|
||||
OrderListItemDto,
|
||||
WorkflowStepName,
|
||||
} from "@/lib/types/backend";
|
||||
import { getOrderStatusMeta, getWorkflowStepMeta } from "@/lib/types/status";
|
||||
import {
|
||||
businessEmptyState,
|
||||
READY_STATE,
|
||||
type AssetViewModel,
|
||||
type OrderDetailVM,
|
||||
type OrderSummaryVM,
|
||||
} from "@/lib/types/view-models";
|
||||
|
||||
const ASSET_TYPE_LABELS: Record<AssetType, string> = {
|
||||
prepared_model: "模型准备图",
|
||||
tryon: "试穿图",
|
||||
scene: "场景图",
|
||||
texture: "纹理图",
|
||||
face: "面部图",
|
||||
fusion: "融合图",
|
||||
qc_candidate: "质检候选图",
|
||||
manual_revision: "人工修订稿",
|
||||
final: "最终图",
|
||||
};
|
||||
|
||||
function isMockUri(uri: string): boolean {
|
||||
return uri.startsWith("mock://");
|
||||
}
|
||||
|
||||
function getAssetLabel(assetType: AssetType, stepName: WorkflowStepName | null) {
|
||||
if (stepName) {
|
||||
return `${getWorkflowStepMeta(stepName).label}产物`;
|
||||
}
|
||||
|
||||
return ASSET_TYPE_LABELS[assetType];
|
||||
}
|
||||
|
||||
export function adaptAsset(asset: AssetDto): AssetViewModel {
|
||||
const stepMeta = getWorkflowStepMeta(asset.step_name);
|
||||
|
||||
return {
|
||||
id: asset.id,
|
||||
orderId: asset.order_id,
|
||||
type: asset.asset_type,
|
||||
stepName: asset.step_name,
|
||||
parentAssetId: asset.parent_asset_id,
|
||||
rootAssetId: asset.root_asset_id,
|
||||
versionNo: asset.version_no,
|
||||
isCurrentVersion: asset.is_current_version,
|
||||
stepLabel: stepMeta.label,
|
||||
label: getAssetLabel(asset.asset_type, asset.step_name),
|
||||
uri: asset.uri,
|
||||
metadata: asset.metadata_json,
|
||||
createdAt: asset.created_at,
|
||||
isMock: isMockUri(asset.uri),
|
||||
};
|
||||
}
|
||||
|
||||
export function adaptOrderSummary(
|
||||
order: Pick<
|
||||
OrderDetailResponseDto | OrderListItemDto,
|
||||
"order_id" | "workflow_id" | "status" | "current_step" | "updated_at"
|
||||
>,
|
||||
): OrderSummaryVM {
|
||||
return {
|
||||
orderId: order.order_id,
|
||||
workflowId: order.workflow_id,
|
||||
status: order.status,
|
||||
statusMeta: getOrderStatusMeta(order.status),
|
||||
currentStep: order.current_step,
|
||||
currentStepLabel: getWorkflowStepMeta(order.current_step).label,
|
||||
updatedAt: order.updated_at,
|
||||
};
|
||||
}
|
||||
|
||||
export function adaptOrderDetail(
|
||||
order: OrderDetailResponseDto,
|
||||
assets: AssetDto[] = [],
|
||||
): OrderDetailVM {
|
||||
const finalAsset = order.final_asset ? adaptAsset(order.final_asset) : null;
|
||||
const assetItems = assets.map(adaptAsset);
|
||||
const hasMockAssets = [finalAsset, ...assetItems].some(
|
||||
(asset) => asset?.isMock,
|
||||
);
|
||||
|
||||
return {
|
||||
orderId: order.order_id,
|
||||
workflowId: order.workflow_id,
|
||||
customerLevel: order.customer_level,
|
||||
serviceMode: order.service_mode,
|
||||
status: order.status,
|
||||
statusMeta: getOrderStatusMeta(order.status),
|
||||
currentStep: order.current_step,
|
||||
currentStepLabel: getWorkflowStepMeta(order.current_step).label,
|
||||
modelId: order.model_id,
|
||||
poseId: order.pose_id,
|
||||
garmentAssetId: order.garment_asset_id,
|
||||
sceneRefAssetId: order.scene_ref_asset_id,
|
||||
currentRevisionAssetId: order.current_revision_asset_id,
|
||||
currentRevisionVersion: order.current_revision_version,
|
||||
latestRevisionAssetId: order.latest_revision_asset_id,
|
||||
latestRevisionVersion: order.latest_revision_version,
|
||||
revisionCount: order.revision_count,
|
||||
reviewTaskStatus: order.review_task_status,
|
||||
pendingManualConfirm: order.pending_manual_confirm,
|
||||
createdAt: order.created_at,
|
||||
updatedAt: order.updated_at,
|
||||
finalAsset,
|
||||
finalAssetState: finalAsset
|
||||
? READY_STATE
|
||||
: businessEmptyState("最终图暂未生成", "当前订单还没有可展示的最终结果。"),
|
||||
assets: assetItems,
|
||||
assetGalleryState: assetItems.length
|
||||
? READY_STATE
|
||||
: businessEmptyState("暂无资产", "当前订单还没有生成可查看的资产列表。"),
|
||||
hasMockAssets,
|
||||
};
|
||||
}
|
||||
69
src/lib/adapters/reviews.ts
Normal file
69
src/lib/adapters/reviews.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import type {
|
||||
PendingReviewResponseDto,
|
||||
SubmitReviewResponseDto,
|
||||
} from "@/lib/types/backend";
|
||||
import {
|
||||
getOrderStatusMeta,
|
||||
getReviewDecisionMeta,
|
||||
getWorkflowStepMeta,
|
||||
} from "@/lib/types/status";
|
||||
import {
|
||||
businessEmptyState,
|
||||
READY_STATE,
|
||||
type ReviewQueueItemVM,
|
||||
type ReviewQueueVM,
|
||||
type ReviewSubmissionVM,
|
||||
} from "@/lib/types/view-models";
|
||||
|
||||
function adaptPendingReviewItem(
|
||||
review: PendingReviewResponseDto,
|
||||
): ReviewQueueItemVM {
|
||||
return {
|
||||
reviewTaskId: review.review_task_id,
|
||||
orderId: review.order_id,
|
||||
workflowId: review.workflow_id,
|
||||
workflowType: null,
|
||||
currentStep: review.current_step,
|
||||
currentStepLabel: getWorkflowStepMeta(review.current_step).label,
|
||||
createdAt: review.created_at,
|
||||
status: "waiting_review",
|
||||
statusMeta: getOrderStatusMeta("waiting_review"),
|
||||
reviewTaskStatus: review.review_task_status,
|
||||
latestRevisionAssetId: review.latest_revision_asset_id,
|
||||
currentRevisionAssetId: review.current_revision_asset_id,
|
||||
latestRevisionVersion: review.latest_revision_version,
|
||||
revisionCount: review.revision_count,
|
||||
pendingManualConfirm: review.pending_manual_confirm,
|
||||
hasMockAssets: false,
|
||||
failureCount: 0,
|
||||
};
|
||||
}
|
||||
|
||||
export function adaptPendingReviews(
|
||||
reviews: PendingReviewResponseDto[],
|
||||
): ReviewQueueVM {
|
||||
const items = reviews.map(adaptPendingReviewItem);
|
||||
|
||||
return {
|
||||
items,
|
||||
state: items.length
|
||||
? READY_STATE
|
||||
: businessEmptyState(
|
||||
"暂无待审核订单",
|
||||
"当前没有等待人工处理的审核任务。",
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
export function adaptReviewSubmission(
|
||||
submission: SubmitReviewResponseDto,
|
||||
): ReviewSubmissionVM {
|
||||
return {
|
||||
orderId: submission.order_id,
|
||||
workflowId: submission.workflow_id,
|
||||
revisionAssetId: submission.revision_asset_id,
|
||||
decision: submission.decision,
|
||||
decisionMeta: getReviewDecisionMeta(submission.decision),
|
||||
status: submission.status,
|
||||
};
|
||||
}
|
||||
44
src/lib/adapters/revisions.ts
Normal file
44
src/lib/adapters/revisions.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import type {
|
||||
RegisterRevisionResponseDto,
|
||||
RevisionChainResponseDto,
|
||||
} from "@/lib/types/backend";
|
||||
import type {
|
||||
RevisionChainVM,
|
||||
RevisionRegistrationVM,
|
||||
} from "@/lib/types/view-models";
|
||||
|
||||
export function adaptRevisionRegistration(
|
||||
payload: RegisterRevisionResponseDto,
|
||||
): RevisionRegistrationVM {
|
||||
return {
|
||||
orderId: payload.order_id,
|
||||
workflowId: payload.workflow_id,
|
||||
assetId: payload.asset_id,
|
||||
parentAssetId: payload.parent_asset_id,
|
||||
rootAssetId: payload.root_asset_id,
|
||||
versionNo: payload.version_no,
|
||||
reviewTaskStatus: payload.review_task_status,
|
||||
latestRevisionAssetId: payload.latest_revision_asset_id,
|
||||
revisionCount: payload.revision_count,
|
||||
};
|
||||
}
|
||||
|
||||
export function adaptRevisionChain(
|
||||
payload: RevisionChainResponseDto,
|
||||
): RevisionChainVM {
|
||||
return {
|
||||
orderId: payload.order_id,
|
||||
latestRevisionAssetId: payload.latest_revision_asset_id,
|
||||
revisionCount: payload.revision_count,
|
||||
items: payload.items.map((item) => ({
|
||||
assetId: item.asset_id,
|
||||
orderId: item.order_id,
|
||||
parentAssetId: item.parent_asset_id,
|
||||
rootAssetId: item.root_asset_id,
|
||||
versionNo: item.version_no,
|
||||
isCurrentVersion: item.is_current_version,
|
||||
uri: item.uri,
|
||||
createdAt: item.created_at,
|
||||
})),
|
||||
};
|
||||
}
|
||||
154
src/lib/adapters/workflows.ts
Normal file
154
src/lib/adapters/workflows.ts
Normal file
@@ -0,0 +1,154 @@
|
||||
import type {
|
||||
JsonObject,
|
||||
JsonValue,
|
||||
WorkflowListItemDto,
|
||||
WorkflowStatusResponseDto,
|
||||
} from "@/lib/types/backend";
|
||||
import {
|
||||
getOrderStatusMeta,
|
||||
getStepStatusMeta,
|
||||
getWorkflowStepMeta,
|
||||
} from "@/lib/types/status";
|
||||
import {
|
||||
businessEmptyState,
|
||||
READY_STATE,
|
||||
type WorkflowDetailVM,
|
||||
type WorkflowLookupItemVM,
|
||||
type WorkflowStepVM,
|
||||
} from "@/lib/types/view-models";
|
||||
|
||||
type WorkflowAssetUriField =
|
||||
| "asset_uri"
|
||||
| "candidate_uri"
|
||||
| "preview_uri"
|
||||
| "result_uri"
|
||||
| "source_uri";
|
||||
|
||||
const WORKFLOW_ASSET_URI_FIELDS = new Set<WorkflowAssetUriField>([
|
||||
"asset_uri",
|
||||
"candidate_uri",
|
||||
"preview_uri",
|
||||
"result_uri",
|
||||
"source_uri",
|
||||
]);
|
||||
|
||||
function collectKnownAssetUris(
|
||||
value: JsonValue | undefined,
|
||||
results: string[] = [],
|
||||
): string[] {
|
||||
if (!value || typeof value !== "object") {
|
||||
return results;
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
for (const item of value) {
|
||||
collectKnownAssetUris(item, results);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
for (const [key, nestedValue] of Object.entries(value)) {
|
||||
if (
|
||||
WORKFLOW_ASSET_URI_FIELDS.has(key as WorkflowAssetUriField) &&
|
||||
typeof nestedValue === "string" &&
|
||||
nestedValue.startsWith("mock://")
|
||||
) {
|
||||
results.push(nestedValue);
|
||||
continue;
|
||||
}
|
||||
|
||||
collectKnownAssetUris(nestedValue, results);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
function uniqueMockUris(...payloads: Array<JsonObject | null>): string[] {
|
||||
return [...new Set(payloads.flatMap((payload) => collectKnownAssetUris(payload)))];
|
||||
}
|
||||
|
||||
function adaptWorkflowStep(
|
||||
currentStep: WorkflowStatusResponseDto["current_step"],
|
||||
step: WorkflowStatusResponseDto["steps"][number],
|
||||
): WorkflowStepVM {
|
||||
const stepMeta = getWorkflowStepMeta(step.step_name);
|
||||
const mockAssetUris = uniqueMockUris(step.input_json, step.output_json);
|
||||
|
||||
return {
|
||||
id: step.id,
|
||||
workflowRunId: step.workflow_run_id,
|
||||
name: step.step_name,
|
||||
label: stepMeta.label,
|
||||
status: step.step_status,
|
||||
statusMeta: getStepStatusMeta(step.step_status),
|
||||
input: step.input_json,
|
||||
output: step.output_json,
|
||||
errorMessage: step.error_message,
|
||||
startedAt: step.started_at,
|
||||
endedAt: step.ended_at,
|
||||
containsMockAssets: mockAssetUris.length > 0,
|
||||
mockAssetUris,
|
||||
isCurrent: currentStep === step.step_name,
|
||||
isFailed: step.step_status === "failed",
|
||||
};
|
||||
}
|
||||
|
||||
export function adaptWorkflowLookupItem(
|
||||
workflow: Pick<
|
||||
WorkflowStatusResponseDto | WorkflowListItemDto,
|
||||
| "order_id"
|
||||
| "workflow_id"
|
||||
| "workflow_type"
|
||||
| "workflow_status"
|
||||
| "current_step"
|
||||
| "updated_at"
|
||||
>,
|
||||
): WorkflowLookupItemVM {
|
||||
return {
|
||||
orderId: workflow.order_id,
|
||||
workflowId: workflow.workflow_id,
|
||||
workflowType: workflow.workflow_type,
|
||||
status: workflow.workflow_status,
|
||||
statusMeta: getOrderStatusMeta(workflow.workflow_status),
|
||||
currentStep: workflow.current_step,
|
||||
currentStepLabel: getWorkflowStepMeta(workflow.current_step).label,
|
||||
updatedAt: workflow.updated_at,
|
||||
};
|
||||
}
|
||||
|
||||
export function adaptWorkflowDetail(
|
||||
workflow: WorkflowStatusResponseDto,
|
||||
): WorkflowDetailVM {
|
||||
const steps = workflow.steps.map((step) =>
|
||||
adaptWorkflowStep(workflow.current_step, step),
|
||||
);
|
||||
|
||||
return {
|
||||
orderId: workflow.order_id,
|
||||
workflowId: workflow.workflow_id,
|
||||
workflowType: workflow.workflow_type,
|
||||
status: workflow.workflow_status,
|
||||
statusMeta: getOrderStatusMeta(workflow.workflow_status),
|
||||
currentStep: workflow.current_step,
|
||||
currentStepLabel: getWorkflowStepMeta(workflow.current_step).label,
|
||||
currentRevisionAssetId: workflow.current_revision_asset_id,
|
||||
currentRevisionVersion: workflow.current_revision_version,
|
||||
latestRevisionAssetId: workflow.latest_revision_asset_id,
|
||||
latestRevisionVersion: workflow.latest_revision_version,
|
||||
revisionCount: workflow.revision_count,
|
||||
reviewTaskStatus: workflow.review_task_status,
|
||||
pendingManualConfirm: workflow.pending_manual_confirm,
|
||||
createdAt: workflow.created_at,
|
||||
updatedAt: workflow.updated_at,
|
||||
steps,
|
||||
stepTimelineState: steps.length
|
||||
? READY_STATE
|
||||
: businessEmptyState(
|
||||
"暂无流程记录",
|
||||
"当前工作流还没有可展示的步骤执行记录。",
|
||||
),
|
||||
failureCount: steps.filter((step) => step.isFailed).length,
|
||||
hasMockAssets: steps.some((step) => step.containsMockAssets),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user