fix: streamline detail reference template lookup

This commit is contained in:
Your Name 2026-06-02 12:59:14 +08:00
parent 824806406a
commit 84f105d4c8
2 changed files with 107 additions and 116 deletions

View File

@ -33,6 +33,7 @@ const querySchema = z.object({
target: z.enum(["main", "detail", "sku"]),
brandName: z.string().trim().max(64).optional(),
channel: productDetailChannelSchema,
strictQueryType: z.enum(["true", "false", "1", "0"]).optional(),
});
function jsonNoStore(
@ -854,6 +855,8 @@ export async function GET(request: NextRequest) {
target: request.nextUrl.searchParams.get("target"),
brandName: request.nextUrl.searchParams.get("brandName") || undefined,
channel: request.nextUrl.searchParams.get("channel") || undefined,
strictQueryType:
request.nextUrl.searchParams.get("strictQueryType") || undefined,
});
const setting = await getSystemSetting({ key: AGENT_WORKFLOW_MATERIAL_API_CONFIG_KEY });
@ -928,12 +931,15 @@ export async function GET(request: NextRequest) {
});
}
const queryTypesToTry = Array.from(
new Set<AgentWorkflowMaterialQueryType>([
materialQueryType,
materialQueryType === "spu" ? "sku" : "spu",
])
);
const strictQueryType = parsed.strictQueryType === "true" || parsed.strictQueryType === "1";
const queryTypesToTry = strictQueryType
? [materialQueryType]
: Array.from(
new Set<AgentWorkflowMaterialQueryType>([
materialQueryType,
materialQueryType === "spu" ? "sku" : "spu",
])
);
let selectedAttempt:
| Awaited<ReturnType<typeof requestTaskDetailAttempt>>

View File

@ -1609,6 +1609,14 @@ export function AgentGenerationWorkspaceDarkV2({
[getLatestWorkflowContext, workflowContext]
);
const workflowBrandName = getCurrentWorkflowBrandName();
const getCurrentWorkflowPrimarySpu = useCallback(
() =>
String((getLatestWorkflowContext?.() ?? workflowContext)?.primarySku || "")
.trim(),
[getLatestWorkflowContext, workflowContext]
);
const workflowPrimarySpu = getCurrentWorkflowPrimarySpu();
const normalizedWorkflowPrimarySpu = workflowPrimarySpu.toUpperCase();
const shouldAllowModelOverride =
!isWorkflowModelLocked &&
(isDirectModelGenerationMode || !isSceneGenerationPreset || presetModeConfig.scene.allowModelOverride);
@ -4836,19 +4844,6 @@ export function AgentGenerationWorkspaceDarkV2({
setPendingGeneration(null);
}
}
const handleDetailReplacementQueryTypeChange = useCallback(
(queryType: AgentWorkflowReferenceQueryType) => {
updateDetailReplacementState((current) => ({
...current,
queryType,
candidates: current.queryType === queryType ? current.candidates : [],
selectedCandidateId:
current.queryType === queryType ? current.selectedCandidateId : null,
references: current.queryType === queryType ? current.references : [],
}));
},
[updateDetailReplacementState]
);
const handleDetailReplacementProductDetailChannelChange = useCallback(
(channel: AgentWorkflowProductDetailChannel) => {
updateDetailReplacementState((current) => ({
@ -4960,25 +4955,22 @@ export function AgentGenerationWorkspaceDarkV2({
},
[detailReplacementReferenceCards.length, updateDetailReplacementState]
);
const handleDetailReplacementSearch = useCallback(async () => {
const handleDetailReplacementSearch = useCallback(async (
queryType: Extract<AgentWorkflowReferenceQueryType, "spu" | "productId">
) => {
const rawPrimarySpu = getCurrentWorkflowPrimarySpu();
const normalized =
detailReplacementQueryType === "productId"
? detailReplacementQueryInput.trim()
: detailReplacementQueryInput.trim().toUpperCase();
queryType === "productId" ? rawPrimarySpu.trim() : rawPrimarySpu.trim().toUpperCase();
const currentWorkflowBrandName = getCurrentWorkflowBrandName();
if (!currentWorkflowBrandName && detailReplacementQueryType !== "productId") {
toast.error("请先在第一步完成品牌选择后再查询参考图");
if (!currentWorkflowBrandName && queryType !== "productId") {
toast.error("请先在第一步完成品牌选择后再检索可用模版");
return;
}
if (!normalized) {
toast.error(
`请输入${
detailReplacementQueryType === "productId"
? "商品ID"
: detailReplacementQueryType === "sku"
? "SKU"
: "SPU"
}后再查询${detailReferenceSourceLabel}`
queryType === "productId"
? "请先在第一步输入主商品市场ID后再查询详情"
: "请先在第一步输入主商品SPU后再检索可用模版"
);
return;
}
@ -4991,14 +4983,16 @@ export function AgentGenerationWorkspaceDarkV2({
try {
const searchParams = new URLSearchParams({
keyword: normalized,
queryType: detailReplacementQueryType,
queryType,
target: detailReferenceTarget,
});
if (currentWorkflowBrandName) {
searchParams.set("brandName", currentWorkflowBrandName);
}
if (detailReplacementQueryType === "productId") {
if (queryType === "productId") {
searchParams.set("channel", detailReplacementProductDetailChannel);
} else {
searchParams.set("strictQueryType", "true");
}
const response = await fetch(
`/api/agent-workflow/reference-search?${searchParams.toString()}`,
@ -5012,7 +5006,7 @@ export function AgentGenerationWorkspaceDarkV2({
updateDetailReplacementState({
query: normalized,
queryType: result.data.queryType || detailReplacementQueryType,
queryType: result.data.queryType || queryType,
productDetailChannel:
result.data.channel || detailReplacementProductDetailChannel,
candidates: result.data.candidates || [],
@ -5036,8 +5030,7 @@ export function AgentGenerationWorkspaceDarkV2({
detailReferenceSourceLabel,
detailReferenceTarget,
detailReplacementProductDetailChannel,
detailReplacementQueryInput,
detailReplacementQueryType,
getCurrentWorkflowPrimarySpu,
getCurrentWorkflowBrandName,
updateDetailReplacementState,
]);
@ -6241,87 +6234,79 @@ export function AgentGenerationWorkspaceDarkV2({
</div>
<div className="space-y-4 p-5">
<div className="flex flex-wrap gap-2">
{([
["spu", "按SPU查询"],
["sku", "按SKU查询"],
["productId", "按商品ID查询"],
] as Array<[AgentWorkflowReferenceQueryType, string]>).map(([queryType, label]) => (
<button
key={queryType}
type="button"
onClick={() => handleDetailReplacementQueryTypeChange(queryType)}
className={cn(
"rounded-full border px-3 py-1.5 text-xs transition-colors",
detailReplacementQueryType === queryType
? "border-emerald-400/45 bg-emerald-500/[0.12] text-emerald-200"
: "border-white/10 bg-white/[0.03] text-white/58 hover:border-white/18 hover:text-white"
)}
>
{label}
</button>
))}
<input
ref={detailReferenceUploadInputRef}
type="file"
accept="image/*"
multiple
className="hidden"
onChange={handleDetailReferenceUploadChange}
/>
<div className="rounded-[24px] border border-white/10 bg-black/20 p-4">
<div className="flex flex-col gap-4 xl:flex-row xl:items-center xl:justify-between">
<div className="min-w-0">
<div className="text-xs text-white/42">
自动复用第一步主商品信息检索参考详情,不需要重复输入 SPU / SKU。
</div>
<div className="mt-2 flex flex-wrap gap-2">
<span className="rounded-full border border-emerald-400/20 bg-emerald-500/[0.08] px-3 py-1 text-xs text-emerald-100">
主 SPU / {normalizedWorkflowPrimarySpu || "未填写"}
</span>
<span className="rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-xs text-white/60">
品牌 / {workflowBrandName || "未选择"}
</span>
<span className="rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-xs text-white/60">
目标 / {detailReferenceSourceLabel}
</span>
</div>
</div>
<div className="flex flex-col gap-2 sm:flex-row">
<Select
value={detailReplacementProductDetailChannel}
onValueChange={(value) =>
handleDetailReplacementProductDetailChannelChange(
value as AgentWorkflowProductDetailChannel
)
}
>
<SelectTrigger className="h-12 rounded-[22px] border-white/10 bg-black/20 text-sm text-white focus:border-emerald-400/30 sm:w-[150px]">
<SelectValue placeholder="渠道" />
</SelectTrigger>
<SelectContent>
{PRODUCT_DETAIL_CHANNEL_OPTIONS.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
<Button
type="button"
onClick={() => handleDetailReplacementSearch("productId")}
disabled={detailReplacementSearchState.isLoading}
className="h-12 rounded-[22px] bg-white/[0.04] px-5 text-sm text-white hover:bg-white/[0.08]"
>
{detailReplacementSearchState.isLoading ? (
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
) : null}
市场ID查询详情
</Button>
<Button
type="button"
onClick={() => handleDetailReplacementSearch("spu")}
disabled={detailReplacementSearchState.isLoading}
className="h-12 rounded-[22px] bg-emerald-500 px-5 font-semibold text-sm text-black shadow-[0_0_24px_rgba(16,185,129,0.22)] hover:bg-emerald-400"
>
{detailReplacementSearchState.isLoading ? (
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
) : null}
检索可用模版
</Button>
</div>
</div>
</div>
<div className="flex flex-col gap-2 lg:flex-row">
<input
ref={detailReferenceUploadInputRef}
type="file"
accept="image/*"
multiple
className="hidden"
onChange={handleDetailReferenceUploadChange}
/>
{detailReplacementQueryType === "productId" ? (
<Select
value={detailReplacementProductDetailChannel}
onValueChange={(value) =>
handleDetailReplacementProductDetailChannelChange(
value as AgentWorkflowProductDetailChannel
)
}
>
<SelectTrigger className="h-12 rounded-[22px] border-white/10 bg-black/20 text-sm text-white focus:border-emerald-400/30 lg:w-[150px]">
<SelectValue placeholder="渠道" />
</SelectTrigger>
<SelectContent>
{PRODUCT_DETAIL_CHANNEL_OPTIONS.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
) : null}
<Input
value={detailReplacementQueryInput}
onChange={(e) =>
updateDetailReplacementState({
query: e.target.value,
})
}
placeholder={
detailReplacementQueryType === "productId"
? "输入商品ID,查询并拉取详情图片"
: `输入 ${detailReplacementQueryType === "sku" ? "SKU" : "SPU"},查询任务整套中的${detailReferenceSourceLabel}参考图`
}
className="h-12 rounded-[22px] border-white/10 bg-black/20 text-sm text-white placeholder:text-white/22 focus:border-emerald-400/30"
/>
<Button
type="button"
onClick={handleDetailReplacementSearch}
disabled={detailReplacementSearchState.isLoading}
className="h-12 rounded-[22px] bg-white/[0.04] px-5 text-sm text-white hover:bg-white/[0.08]"
>
{detailReplacementSearchState.isLoading ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
查询中
</>
) : (
`查询${detailReferenceSourceLabel}`
)}
</Button>
<Button
type="button"
variant="outline"
@ -6897,7 +6882,7 @@ export function AgentGenerationWorkspaceDarkV2({
<div className="flex min-h-[220px] flex-col items-center justify-center rounded-[22px] border border-dashed border-white/12 bg-black/20 px-5 text-center">
<div className="text-sm font-medium text-white/72">等待{detailReferenceSourceLabel}参考图</div>
<div className="mt-2 text-xs leading-6 text-white/40">
输入 SPU 或 SKU 后,这里会展示任务整套里的真实{detailReferenceSourceLabel}参考图。
点击“检索可用模版”后,这里会展示当前品牌下主 SPU 对应的真实{detailReferenceSourceLabel}参考图。
删除不需要的参考图后,列表会自动上移补位。
</div>
</div>