fix time out
This commit is contained in:
parent
520b42f2c7
commit
c5e600900c
1 changed files with 178 additions and 21 deletions
|
|
@ -8197,25 +8197,13 @@ export class OrganizationController extends Controller {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2.5 Sync positions table for all affected posMasters
|
// 2.5 Sync positions table for all affected posMasters (BATCH operation for performance)
|
||||||
const positionSyncStats: { deleted: number; updated: number; inserted: number } = {
|
const positionSyncStats = await this.syncAllPositionsBatch(
|
||||||
deleted: 0,
|
|
||||||
updated: 0,
|
|
||||||
inserted: 0,
|
|
||||||
};
|
|
||||||
for (const [draftPosMasterId, [currentPosMasterId, nextHolderId]] of posMasterMapping) {
|
|
||||||
const stats = await this.syncPositionsForPosMaster(
|
|
||||||
queryRunner,
|
queryRunner,
|
||||||
draftPosMasterId,
|
posMasterMapping,
|
||||||
currentPosMasterId,
|
|
||||||
drafRevisionId,
|
drafRevisionId,
|
||||||
currentRevisionId,
|
currentRevisionId,
|
||||||
nextHolderId,
|
|
||||||
);
|
);
|
||||||
positionSyncStats.deleted += stats.deleted;
|
|
||||||
positionSyncStats.updated += stats.updated;
|
|
||||||
positionSyncStats.inserted += stats.inserted;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build comprehensive summary
|
// Build comprehensive summary
|
||||||
const summary = {
|
const summary = {
|
||||||
|
|
@ -8512,13 +8500,15 @@ export class OrganizationController extends Controller {
|
||||||
/**
|
/**
|
||||||
* Helper function: Sync positions for a PosMaster
|
* Helper function: Sync positions for a PosMaster
|
||||||
* Handles DELETE/UPDATE/INSERT for positions associated with a posMaster
|
* Handles DELETE/UPDATE/INSERT for positions associated with a posMaster
|
||||||
|
*
|
||||||
|
* @deprecated Kept as fallback - use syncAllPositionsBatch for better performance
|
||||||
*/
|
*/
|
||||||
private async syncPositionsForPosMaster(
|
private async syncPositionsForPosMaster(
|
||||||
queryRunner: any,
|
queryRunner: any,
|
||||||
draftPosMasterId: string,
|
draftPosMasterId: string,
|
||||||
currentPosMasterId: string,
|
currentPosMasterId: string,
|
||||||
draftRevisionId: string,
|
_draftRevisionId: string,
|
||||||
currentRevisionId: string,
|
_currentRevisionId: string,
|
||||||
nextHolderId: string | null | undefined,
|
nextHolderId: string | null | undefined,
|
||||||
): Promise<{ deleted: number; updated: number; inserted: number }> {
|
): Promise<{ deleted: number; updated: number; inserted: number }> {
|
||||||
// Fetch draft and current positions for this posMaster
|
// Fetch draft and current positions for this posMaster
|
||||||
|
|
@ -8607,4 +8597,171 @@ export class OrganizationController extends Controller {
|
||||||
|
|
||||||
return { deleted: toDelete.length, updated: updatedCount, inserted: insertedCount };
|
return { deleted: toDelete.length, updated: updatedCount, inserted: insertedCount };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Batch version: Sync positions for ALL posMasters in a single operation
|
||||||
|
* This significantly reduces database round trips for large organizations
|
||||||
|
*/
|
||||||
|
private async syncAllPositionsBatch(
|
||||||
|
queryRunner: any,
|
||||||
|
posMasterMapping: Map<string, [string, string | null | undefined]>,
|
||||||
|
_draftRevisionId: string,
|
||||||
|
_currentRevisionId: string,
|
||||||
|
): Promise<{ deleted: number; updated: number; inserted: number }> {
|
||||||
|
// Extract draft and current posMaster IDs
|
||||||
|
const draftPosMasterIds = Array.from(posMasterMapping.keys());
|
||||||
|
const currentPosMasterIds = Array.from(posMasterMapping.values()).map(([currentId]) => currentId);
|
||||||
|
|
||||||
|
if (draftPosMasterIds.length === 0) {
|
||||||
|
return { deleted: 0, updated: 0, inserted: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch ALL positions for ALL posMasters in just 2 queries
|
||||||
|
const [allDraftPositions, allCurrentPositions] = await Promise.all([
|
||||||
|
queryRunner.manager.find(Position, {
|
||||||
|
where: {
|
||||||
|
posMasterId: In(draftPosMasterIds),
|
||||||
|
},
|
||||||
|
order: { orderNo: "ASC" },
|
||||||
|
}),
|
||||||
|
queryRunner.manager.find(Position, {
|
||||||
|
where: {
|
||||||
|
posMasterId: In(currentPosMasterIds),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Group positions by posMasterId for processing
|
||||||
|
const draftPositionsByMaster = new Map<string, any[]>();
|
||||||
|
for (const pos of allDraftPositions) {
|
||||||
|
if (!draftPositionsByMaster.has(pos.posMasterId)) {
|
||||||
|
draftPositionsByMaster.set(pos.posMasterId, []);
|
||||||
|
}
|
||||||
|
draftPositionsByMaster.get(pos.posMasterId)!.push(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentPositionsByMaster = new Map<string, any[]>();
|
||||||
|
for (const pos of allCurrentPositions) {
|
||||||
|
if (!currentPositionsByMaster.has(pos.posMasterId)) {
|
||||||
|
currentPositionsByMaster.set(pos.posMasterId, []);
|
||||||
|
}
|
||||||
|
currentPositionsByMaster.get(pos.posMasterId)!.push(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect all operations
|
||||||
|
const allToDelete: string[] = [];
|
||||||
|
const allToUpdate: Array<{ id: string; data: any }> = [];
|
||||||
|
const allToInsert: Array<any> = [];
|
||||||
|
const profileUpdates: Map<string, any> = new Map();
|
||||||
|
|
||||||
|
// Process each posMaster mapping
|
||||||
|
for (const [draftPosMasterId, [currentPosMasterId, nextHolderId]] of posMasterMapping) {
|
||||||
|
const draftPositions = draftPositionsByMaster.get(draftPosMasterId) || [];
|
||||||
|
const currentPositions = currentPositionsByMaster.get(currentPosMasterId) || [];
|
||||||
|
|
||||||
|
// If no draft positions, mark all current positions for deletion
|
||||||
|
if (draftPositions.length === 0) {
|
||||||
|
allToDelete.push(...currentPositions.map((p: any) => p.id));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build map for tracking
|
||||||
|
const currentByOrderNo = new Map(currentPositions.map((p: any) => [p.orderNo, p]));
|
||||||
|
const draftOrderNos = new Set(draftPositions.map((p: any) => p.orderNo));
|
||||||
|
|
||||||
|
// Mark for deletion: current positions not in draft (by orderNo)
|
||||||
|
for (const currentPos of currentPositions) {
|
||||||
|
if (!draftOrderNos.has(currentPos.orderNo)) {
|
||||||
|
allToDelete.push(currentPos.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process UPDATE and INSERT
|
||||||
|
for (const draftPos of draftPositions) {
|
||||||
|
const current = currentByOrderNo.get(draftPos.orderNo);
|
||||||
|
|
||||||
|
if (current) {
|
||||||
|
// UPDATE existing position - collect for batch update
|
||||||
|
allToUpdate.push({
|
||||||
|
id: current.id,
|
||||||
|
data: {
|
||||||
|
positionName: draftPos.positionName,
|
||||||
|
positionField: draftPos.positionField,
|
||||||
|
posTypeId: draftPos.posTypeId,
|
||||||
|
posLevelId: draftPos.posLevelId,
|
||||||
|
posExecutiveId: draftPos.posExecutiveId,
|
||||||
|
positionExecutiveField: draftPos.positionExecutiveField,
|
||||||
|
positionArea: draftPos.positionArea,
|
||||||
|
isSpecial: draftPos.isSpecial,
|
||||||
|
orderNo: draftPos.orderNo,
|
||||||
|
positionIsSelected: draftPos.positionIsSelected,
|
||||||
|
lastUpdateFullName: draftPos.lastUpdateFullName,
|
||||||
|
lastUpdatedAt: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// INSERT new position - collect for batch insert
|
||||||
|
allToInsert.push({
|
||||||
|
...draftPos,
|
||||||
|
id: undefined,
|
||||||
|
posMasterId: currentPosMasterId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect profile update for the selected position
|
||||||
|
if (nextHolderId != null && draftPos.positionIsSelected) {
|
||||||
|
profileUpdates.set(nextHolderId, {
|
||||||
|
position: draftPos.positionName,
|
||||||
|
posTypeId: draftPos.posTypeId,
|
||||||
|
posLevelId: draftPos.posLevelId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute bulk operations
|
||||||
|
let deletedCount = 0;
|
||||||
|
let updatedCount = 0;
|
||||||
|
let insertedCount = 0;
|
||||||
|
|
||||||
|
// Bulk DELETE
|
||||||
|
if (allToDelete.length > 0) {
|
||||||
|
await queryRunner.manager.delete(Position, allToDelete);
|
||||||
|
deletedCount = allToDelete.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bulk UPDATE (batch by 500 to avoid query size limits)
|
||||||
|
if (allToUpdate.length > 0) {
|
||||||
|
const batchSize = 500;
|
||||||
|
for (let i = 0; i < allToUpdate.length; i += batchSize) {
|
||||||
|
const batch = allToUpdate.slice(i, i + batchSize);
|
||||||
|
await Promise.all(
|
||||||
|
batch.map(({ id, data }) => queryRunner.manager.update(Position, id, data))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
updatedCount = allToUpdate.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bulk INSERT
|
||||||
|
if (allToInsert.length > 0) {
|
||||||
|
const batchSize = 500;
|
||||||
|
for (let i = 0; i < allToInsert.length; i += batchSize) {
|
||||||
|
const batch = allToInsert.slice(i, i + batchSize);
|
||||||
|
await queryRunner.manager.save(Position, batch);
|
||||||
|
}
|
||||||
|
insertedCount = allToInsert.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bulk UPDATE profiles
|
||||||
|
if (profileUpdates.size > 0) {
|
||||||
|
const profileUpdateEntries = Array.from(profileUpdates.entries());
|
||||||
|
await Promise.all(
|
||||||
|
profileUpdateEntries.map(([profileId, data]) =>
|
||||||
|
queryRunner.manager.update(Profile, profileId, data)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { deleted: deletedCount, updated: updatedCount, inserted: insertedCount };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue