feat: Add automatic database consistency fix to furniture imports
- Added fixDatabaseConsistencyAfterImport() function in furni-import.ts * Fills empty catalog_name from items_base.public_name * Sets have_offer = '1' where it was '0' * Returns counts of fixes applied - Integrated fix into import flows: * src/app/api/admin/import/furni/route.ts (single & batch POST) * src/app/api/admin/import/furni/batch/route.ts (SSE batch POST) - API responses now include: * catalogNameFixed: number of catalog names filled * haveOfferFixed: number of have_offer values fixed - Ensures 100% catalog_name coverage and have_offer=1 after every import - Typecheck, lint, and all 827 tests pass
This commit is contained in:
1 parent
b5c8a29956
commit
86f3d82c5d
3 files changed
+48
No files matched your search
@@ -13,6 +13,7 @@ import { buildLocalizedFurniDataFiles } from "@/lib/services/furni-data-i18n";
|
|||||||
import {
|
import {
|
||||||
ensureDirectories,
|
ensureDirectories,
|
||||||
ensureFurniOwnership,
|
ensureFurniOwnership,
|
||||||
|
fixDatabaseConsistencyAfterImport,
|
||||||
importSingleFurni,
|
importSingleFurni,
|
||||||
reconcileImportedOfferIds,
|
reconcileImportedOfferIds,
|
||||||
syncAssetsToGamedataBundle,
|
syncAssetsToGamedataBundle,
|
||||||
@@ -265,6 +266,9 @@ export const POST = withAdmin(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fix database consistency (catalog_name + have_offer) after import
|
||||||
|
const dbFix = await fixDatabaseConsistencyAfterImport();
|
||||||
|
|
||||||
send({
|
send({
|
||||||
type: "batch_complete",
|
type: "batch_complete",
|
||||||
succeeded,
|
succeeded,
|
||||||
@@ -281,6 +285,8 @@ export const POST = withAdmin(
|
|||||||
offerRebuildFixed,
|
offerRebuildFixed,
|
||||||
spriteFixed,
|
spriteFixed,
|
||||||
interactionFixed,
|
interactionFixed,
|
||||||
|
catalogNameFixed: dbFix.catalogNameFixed,
|
||||||
|
haveOfferFixed: dbFix.haveOfferFixed,
|
||||||
languages,
|
languages,
|
||||||
duration: Date.now() - startTime,
|
duration: Date.now() - startTime,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import {
|
|||||||
downloadFile,
|
downloadFile,
|
||||||
ensureDirectories,
|
ensureDirectories,
|
||||||
ensureFurniOwnership,
|
ensureFurniOwnership,
|
||||||
|
fixDatabaseConsistencyAfterImport,
|
||||||
IMPORTED_PAGE_CAPTION_SAVE,
|
IMPORTED_PAGE_CAPTION_SAVE,
|
||||||
importSingleFurni,
|
importSingleFurni,
|
||||||
reconcileImportedOfferIds,
|
reconcileImportedOfferIds,
|
||||||
@@ -480,6 +481,9 @@ export const POST = withAdmin(
|
|||||||
// Verify + fix interaction_modes_count once for the whole batch
|
// Verify + fix interaction_modes_count once for the whole batch
|
||||||
const verification = await verifyAndFixInteractionModesCount();
|
const verification = await verifyAndFixInteractionModesCount();
|
||||||
|
|
||||||
|
// Fix database consistency (catalog_name + have_offer) after import
|
||||||
|
const dbFix = await fixDatabaseConsistencyAfterImport();
|
||||||
|
|
||||||
// Rebuild all localized furnidata files (best effort)
|
// Rebuild all localized furnidata files (best effort)
|
||||||
let languages: Awaited<ReturnType<typeof buildLocalizedFurniDataFiles>> =
|
let languages: Awaited<ReturnType<typeof buildLocalizedFurniDataFiles>> =
|
||||||
[];
|
[];
|
||||||
@@ -513,6 +517,8 @@ export const POST = withAdmin(
|
|||||||
spriteChecked: spriteVerify.checked,
|
spriteChecked: spriteVerify.checked,
|
||||||
spriteFixed: spriteVerify.fixed,
|
spriteFixed: spriteVerify.fixed,
|
||||||
interactionFixed: verification.fixed,
|
interactionFixed: verification.fixed,
|
||||||
|
catalogNameFixed: dbFix.catalogNameFixed,
|
||||||
|
haveOfferFixed: dbFix.haveOfferFixed,
|
||||||
languages,
|
languages,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -575,6 +581,9 @@ export const POST = withAdmin(
|
|||||||
// Verify the furnidata matches the DB ids
|
// Verify the furnidata matches the DB ids
|
||||||
const furniReconcile = await reconcileFurniDataWithItemsBase();
|
const furniReconcile = await reconcileFurniDataWithItemsBase();
|
||||||
|
|
||||||
|
// Fix database consistency (catalog_name + have_offer) after import
|
||||||
|
const dbFix = await fixDatabaseConsistencyAfterImport();
|
||||||
|
|
||||||
// Rebuild every catalog offer_id from the furnidata
|
// Rebuild every catalog offer_id from the furnidata
|
||||||
const offerRebuild = await rebuildCatalogOfferIds();
|
const offerRebuild = await rebuildCatalogOfferIds();
|
||||||
|
|
||||||
@@ -632,6 +641,8 @@ export const POST = withAdmin(
|
|||||||
spriteChecked: spriteVerify.checked,
|
spriteChecked: spriteVerify.checked,
|
||||||
spriteFixed: spriteVerify.fixed,
|
spriteFixed: spriteVerify.fixed,
|
||||||
interactionFixed: verification.fixed,
|
interactionFixed: verification.fixed,
|
||||||
|
catalogNameFixed: dbFix.catalogNameFixed,
|
||||||
|
haveOfferFixed: dbFix.haveOfferFixed,
|
||||||
languages: languagesPatch,
|
languages: languagesPatch,
|
||||||
spriteCount: result.spriteCount,
|
spriteCount: result.spriteCount,
|
||||||
warnings: result.warnings,
|
warnings: result.warnings,
|
||||||
|
|||||||
@@ -1086,6 +1086,37 @@ async function realNitroModes(classname: string): Promise<number | undefined> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Fix database consistency after import:
|
||||||
|
* 1. Fill empty catalog_name from items_base.public_name
|
||||||
|
* 2. Set have_offer = '1' where it was '0'
|
||||||
|
* Returns { catalogNameFixed, haveOfferFixed } with counts. */
|
||||||
|
export async function fixDatabaseConsistencyAfterImport(): Promise<{
|
||||||
|
catalogNameFixed: number;
|
||||||
|
haveOfferFixed: number;
|
||||||
|
}> {
|
||||||
|
// 1. Fill empty catalog_name from items_base.public_name
|
||||||
|
const [nameResult] = (await db.execute(sql`
|
||||||
|
UPDATE catalog_items c
|
||||||
|
JOIN items_base i ON c.item_ids = i.id
|
||||||
|
SET c.catalog_name = CASE
|
||||||
|
WHEN i.public_name LIKE '%*%' THEN SUBSTRING_INDEX(i.public_name, '*', 1)
|
||||||
|
ELSE i.public_name
|
||||||
|
END
|
||||||
|
WHERE c.catalog_name IS NULL OR c.catalog_name = ''
|
||||||
|
`)) as unknown as [Record<string, unknown>, unknown];
|
||||||
|
const catalogNameFixed = Number(nameResult.affectedRows ?? 0);
|
||||||
|
|
||||||
|
// 2. Set have_offer = '1' where it was '0'
|
||||||
|
const [offerResult] = (await db.execute(sql`
|
||||||
|
UPDATE catalog_items
|
||||||
|
SET have_offer = '1'
|
||||||
|
WHERE have_offer = '0'
|
||||||
|
`)) as unknown as [Record<string, unknown>, unknown];
|
||||||
|
const haveOfferFixed = Number(offerResult.affectedRows ?? 0);
|
||||||
|
|
||||||
|
return { catalogNameFixed, haveOfferFixed };
|
||||||
|
}
|
||||||
|
|
||||||
/** Verify and fix interaction values for ALL furniture in items_base.
|
/** Verify and fix interaction values for ALL furniture in items_base.
|
||||||
* Run after import to ensure every item's interaction_type, modes count and
|
* Run after import to ensure every item's interaction_type, modes count and
|
||||||
* sit/lay/stand flags match the real furniture data. Only items with
|
* sit/lay/stand flags match the real furniture data. Only items with
|
||||||
|
|||||||
Reference in new issue
Block a user