Add synchronous price calculation method: Introduce calcRegionalSellPriceSync for improved performance in price calculations when worthPercent is known. Refactor getAllProductPricesInRegion to utilize this new method, enhancing efficiency by reducing database calls. Update BranchView to manage product prices cache with regionId for better data handling.

This commit is contained in:
Torsten Schulz (local)
2026-01-29 15:06:38 +01:00
parent 474e46837a
commit 032e336b65
3 changed files with 30 additions and 52 deletions

View File

@@ -98,6 +98,15 @@ function calcSellPrice(product, knowledgeFactor = 0) {
return min + (max - min) * (knowledgeFactor / 100); return min + (max - min) * (knowledgeFactor / 100);
} }
/** Synchrone Preisberechnung, wenn worthPercent bereits bekannt ist (kein DB-Zugriff). */
function calcRegionalSellPriceSync(product, knowledgeFactor, worthPercent) {
if (product.sellCost === null || product.sellCost === undefined) return null;
const basePrice = product.sellCost * (worthPercent / 100);
const min = basePrice * 0.6;
const max = basePrice;
return min + (max - min) * (knowledgeFactor / 100);
}
async function calcRegionalSellPrice(product, knowledgeFactor, regionId, worthPercent = null) { async function calcRegionalSellPrice(product, knowledgeFactor, regionId, worthPercent = null) {
// Wenn worthPercent nicht übergeben wurde, hole es aus der Datenbank // Wenn worthPercent nicht übergeben wurde, hole es aus der Datenbank
if (worthPercent === null) { if (worthPercent === null) {
@@ -4394,66 +4403,34 @@ class FalukantService extends BaseService {
} }
async getAllProductPricesInRegion(hashedUserId, regionId) { async getAllProductPricesInRegion(hashedUserId, regionId) {
const startTime = Date.now();
console.log(`[getAllProductPricesInRegion] Start für userId: ${hashedUserId}, regionId: ${regionId}`);
try { try {
const user = await this.getFalukantUserByHashedId(hashedUserId); const user = await this.getFalukantUserByHashedId(hashedUserId);
const userTime = Date.now();
console.log(`[getAllProductPricesInRegion] User geladen in ${userTime - startTime}ms`);
const character = await FalukantCharacter.findOne({ where: { userId: user.id } }); const character = await FalukantCharacter.findOne({ where: { userId: user.id } });
if (!character) { if (!character) {
throw new Error(`No FalukantCharacter found for user with id ${user.id}`); throw new Error(`No FalukantCharacter found for user with id ${user.id}`);
} }
const characterTime = Date.now(); const [products, knowledges, townWorths] = await Promise.all([
console.log(`[getAllProductPricesInRegion] Character geladen in ${characterTime - userTime}ms`); ProductType.findAll({ attributes: ['id', 'sellCost'] }),
Knowledge.findAll({
where: { characterId: character.id },
attributes: ['productId', 'knowledge']
}),
TownProductWorth.findAll({
where: { regionId: regionId },
attributes: ['productId', 'worthPercent']
})
]);
// Lade alle Produkte auf einmal
const products = await ProductType.findAll({
attributes: ['id', 'sellCost']
});
const productsTime = Date.now();
console.log(`[getAllProductPricesInRegion] ${products.length} Produkte geladen in ${productsTime - characterTime}ms`);
// Lade alle Knowledge-Werte für diesen Character auf einmal
const knowledges = await Knowledge.findAll({
where: { characterId: character.id },
attributes: ['productId', 'knowledge']
});
const knowledgeMap = new Map(knowledges.map(k => [k.productId, k.knowledge || 0])); const knowledgeMap = new Map(knowledges.map(k => [k.productId, k.knowledge || 0]));
const knowledgeTime = Date.now();
console.log(`[getAllProductPricesInRegion] ${knowledges.length} Knowledge-Werte geladen in ${knowledgeTime - productsTime}ms`);
// Lade alle TownProductWorth-Werte für diese Region auf einmal
const townWorths = await TownProductWorth.findAll({
where: { regionId: regionId },
attributes: ['productId', 'worthPercent']
});
const worthMap = new Map(townWorths.map(tw => [tw.productId, tw.worthPercent || 50])); const worthMap = new Map(townWorths.map(tw => [tw.productId, tw.worthPercent || 50]));
const worthTime = Date.now();
console.log(`[getAllProductPricesInRegion] ${townWorths.length} Worth-Werte geladen in ${worthTime - knowledgeTime}ms`);
// Berechne Preise für alle Produkte
const prices = {}; const prices = {};
for (const product of products) { for (const product of products) {
if (product.sellCost === null || product.sellCost === undefined) { const worthPercent = worthMap.get(product.id) ?? 50;
continue; // Überspringe Produkte ohne sellCost
}
const knowledgeFactor = knowledgeMap.get(product.id) || 0; const knowledgeFactor = knowledgeMap.get(product.id) || 0;
const worthPercent = worthMap.get(product.id) || 50; const price = calcRegionalSellPriceSync(product, knowledgeFactor, worthPercent);
if (price !== null) prices[product.id] = price;
// Verwende calcRegionalSellPrice mit bereits geladenem worthPercent
const price = await calcRegionalSellPrice(product, knowledgeFactor, regionId, worthPercent);
prices[product.id] = price;
} }
const calcTime = Date.now();
console.log(`[getAllProductPricesInRegion] Preise berechnet in ${calcTime - worthTime}ms`);
const totalTime = Date.now() - startTime;
console.log(`[getAllProductPricesInRegion] Gesamtzeit: ${totalTime}ms für ${Object.keys(prices).length} Produkte`);
return { prices }; return { prices };
} catch (error) { } catch (error) {
console.error(`[getAllProductPricesInRegion] Error for regionId=${regionId}:`, error); console.error(`[getAllProductPricesInRegion] Error for regionId=${regionId}:`, error);

View File

@@ -68,7 +68,6 @@ export default {
}, },
methods: { methods: {
toggleDropdown() { toggleDropdown() {
console.log("toggleDropdown", 'clicked');
if (this.disabled) return; if (this.disabled) return;
this.isOpen = !this.isOpen; this.isOpen = !this.isOpen;
}, },

View File

@@ -360,6 +360,7 @@ export default {
vehicles: [], vehicles: [],
activeTab: 'production', activeTab: 'production',
productPricesCache: {}, // Cache für regionale Preise: { productId: price } productPricesCache: {}, // Cache für regionale Preise: { productId: price }
productPricesCacheRegionId: null, // regionId, für die der Cache gültig ist
tabs: [ tabs: [
{ value: 'production', label: 'falukant.branch.tabs.production' }, { value: 'production', label: 'falukant.branch.tabs.production' },
{ value: 'inventory', label: 'falukant.branch.tabs.inventory' }, { value: 'inventory', label: 'falukant.branch.tabs.inventory' },
@@ -569,20 +570,20 @@ export default {
async loadProductPricesForCurrentBranch() { async loadProductPricesForCurrentBranch() {
if (!this.selectedBranch || !this.selectedBranch.regionId) { if (!this.selectedBranch || !this.selectedBranch.regionId) {
this.productPricesCache = {}; this.productPricesCache = {};
this.productPricesCacheRegionId = null;
return;
}
if (this.productPricesCacheRegionId === this.selectedBranch.regionId && Object.keys(this.productPricesCache).length > 0) {
return; return;
} }
// Lade alle Preise für die Region auf einmal (Bulk-Request)
try { try {
const startTime = performance.now();
const { data } = await apiClient.get('/api/falukant/products/prices-in-region', { const { data } = await apiClient.get('/api/falukant/products/prices-in-region', {
params: { params: {
regionId: this.selectedBranch.regionId regionId: this.selectedBranch.regionId
} }
}); });
const loadTime = performance.now() - startTime;
console.log(`[BranchView] Product prices loaded in ${loadTime.toFixed(2)}ms`);
this.productPricesCache = data.prices || {}; this.productPricesCache = data.prices || {};
this.productPricesCacheRegionId = this.selectedBranch.regionId;
} catch (error) { } catch (error) {
console.error(`Error loading product prices for region ${this.selectedBranch.regionId}:`, error); console.error(`Error loading product prices for region ${this.selectedBranch.regionId}:`, error);
// Fallback: Lade Preise einzeln (alte Methode) // Fallback: Lade Preise einzeln (alte Methode)
@@ -607,6 +608,7 @@ export default {
} }
} }
this.productPricesCache = prices; this.productPricesCache = prices;
this.productPricesCacheRegionId = this.selectedBranch?.regionId ?? null;
} }
}, },