gongxiangp2/scripts/backfill-task-detail-full-set.ts
chenpei 4712175cb0 feat(素材链路): 发布白底图双轨与AI生图回传能力
- 完成白底图 WebP 双轨接口、上传链路与预览缓存改造
- 新增 AI 生图平台成品回传接口、任务状态与表单规则
- 补齐 AI 生成标签、任务详情补全、相关测试与对接文档
2026-03-25 10:28:02 +08:00

160 lines
4.1 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 按“主图 + SKU 图 + 详情图齐全”规则回填任务-详情的 isFullSet。
*
* 默认只预览结果,不写库。
* 真正写入请加:--apply
* 可选限制数量:--limit=200
*/
import { PrismaClient } from '@prisma/client';
import { isTaskDetailFullSetFromAssets } from '../lib/task-detail-full-set';
const prisma = new PrismaClient();
function parseLimitArg(): number | undefined {
const limitArg = process.argv.find((arg) => arg.startsWith('--limit='));
if (!limitArg) {
return undefined;
}
const parsed = Number(limitArg.split('=')[1]);
if (!Number.isFinite(parsed) || parsed <= 0) {
throw new Error(`无效的 --limit 参数:${limitArg}`);
}
return Math.floor(parsed);
}
async function main() {
const shouldApply = process.argv.includes('--apply');
const limit = parseLimitArg();
console.log('='.repeat(72));
console.log('任务-详情 isFullSet 回填脚本');
console.log(`模式: ${shouldApply ? '写入数据库' : '仅预览(dry-run)'}`);
if (limit) {
console.log(`限制数量: ${limit}`);
}
console.log('规则: 同时拥有主图、SKU 图、详情图 => 整套');
console.log('='.repeat(72));
const materials = await prisma.material.findMany({
where: {
materialType: '任务-详情',
deletedAt: null,
},
select: {
id: true,
taskNo: true,
productModel: true,
isFullSet: true,
createdAt: true,
assets: {
select: {
assetType: true,
},
},
},
orderBy: {
createdAt: 'desc',
},
...(limit ? { take: limit } : {}),
});
let shouldBeTrue = 0;
let shouldBeFalse = 0;
let toTrue = 0;
let toFalse = 0;
let unchanged = 0;
const changedMaterials: Array<{
id: string;
taskNo: string | null;
productModel: string;
before: boolean;
after: boolean;
assetTypes: string[];
}> = [];
for (const material of materials) {
const derivedIsFullSet = isTaskDetailFullSetFromAssets(material.assets);
const currentIsFullSet = Boolean(material.isFullSet);
if (derivedIsFullSet) {
shouldBeTrue += 1;
} else {
shouldBeFalse += 1;
}
if (derivedIsFullSet === currentIsFullSet) {
unchanged += 1;
continue;
}
if (derivedIsFullSet) {
toTrue += 1;
} else {
toFalse += 1;
}
changedMaterials.push({
id: material.id,
taskNo: material.taskNo,
productModel: material.productModel,
before: currentIsFullSet,
after: derivedIsFullSet,
assetTypes: material.assets.map((asset) => asset.assetType),
});
}
console.log(`总素材数: ${materials.length}`);
console.log(`应为整套: ${shouldBeTrue}`);
console.log(`应为非整套: ${shouldBeFalse}`);
console.log(`待改为 true: ${toTrue}`);
console.log(`待改为 false: ${toFalse}`);
console.log(`无需修改: ${unchanged}`);
if (changedMaterials.length > 0) {
console.log('\n待修改样例(最多展示前 20 条):');
changedMaterials.slice(0, 20).forEach((material, index) => {
console.log(
`${index + 1}. ${material.productModel} | taskNo=${material.taskNo || '无'} | ${material.before} -> ${material.after} | assets=${material.assetTypes.join(', ')}`,
);
});
} else {
console.log('\n没有需要修改的素材。');
}
if (!shouldApply) {
console.log('\n当前为 dry-run,未写入数据库。');
console.log('如需真正回填,请执行: npx tsx scripts/backfill-task-detail-full-set.ts --apply');
return;
}
if (changedMaterials.length === 0) {
console.log('\n没有可写入的变更,结束。');
return;
}
console.log('\n开始写入数据库...');
for (const material of changedMaterials) {
await prisma.material.update({
where: { id: material.id },
data: {
isFullSet: material.after,
},
});
}
console.log(`写入完成,共更新 ${changedMaterials.length} 条素材。`);
}
main()
.catch((error) => {
console.error('脚本执行失败:', error);
process.exitCode = 1;
})
.finally(async () => {
await prisma.$disconnect();
});