From 032e336b6568e3c4f14c9a3b5375dd6d0f8f7a43 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Thu, 29 Jan 2026 15:06:38 +0100 Subject: [PATCH] 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. --- backend/services/falukantService.js | 69 +++++++------------ .../src/components/form/FormattedDropdown.vue | 1 - frontend/src/views/falukant/BranchView.vue | 12 ++-- 3 files changed, 30 insertions(+), 52 deletions(-) diff --git a/backend/services/falukantService.js b/backend/services/falukantService.js index a88a60c..6316c9a 100644 --- a/backend/services/falukantService.js +++ b/backend/services/falukantService.js @@ -98,6 +98,15 @@ function calcSellPrice(product, knowledgeFactor = 0) { 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) { // Wenn worthPercent nicht übergeben wurde, hole es aus der Datenbank if (worthPercent === null) { @@ -4394,66 +4403,34 @@ class FalukantService extends BaseService { } async getAllProductPricesInRegion(hashedUserId, regionId) { - const startTime = Date.now(); - console.log(`[getAllProductPricesInRegion] Start für userId: ${hashedUserId}, regionId: ${regionId}`); - try { 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 } }); if (!character) { throw new Error(`No FalukantCharacter found for user with id ${user.id}`); } - const characterTime = Date.now(); - console.log(`[getAllProductPricesInRegion] Character geladen in ${characterTime - userTime}ms`); + const [products, knowledges, townWorths] = await Promise.all([ + 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 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 worthTime = Date.now(); - console.log(`[getAllProductPricesInRegion] ${townWorths.length} Worth-Werte geladen in ${worthTime - knowledgeTime}ms`); - // Berechne Preise für alle Produkte const prices = {}; for (const product of products) { - if (product.sellCost === null || product.sellCost === undefined) { - continue; // Überspringe Produkte ohne sellCost - } - + const worthPercent = worthMap.get(product.id) ?? 50; const knowledgeFactor = knowledgeMap.get(product.id) || 0; - const worthPercent = worthMap.get(product.id) || 50; - - // Verwende calcRegionalSellPrice mit bereits geladenem worthPercent - const price = await calcRegionalSellPrice(product, knowledgeFactor, regionId, worthPercent); - prices[product.id] = price; + const price = calcRegionalSellPriceSync(product, knowledgeFactor, worthPercent); + if (price !== null) 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 }; } catch (error) { console.error(`[getAllProductPricesInRegion] Error for regionId=${regionId}:`, error); diff --git a/frontend/src/components/form/FormattedDropdown.vue b/frontend/src/components/form/FormattedDropdown.vue index 4dfe9f3..7e85ce5 100644 --- a/frontend/src/components/form/FormattedDropdown.vue +++ b/frontend/src/components/form/FormattedDropdown.vue @@ -68,7 +68,6 @@ export default { }, methods: { toggleDropdown() { - console.log("toggleDropdown", 'clicked'); if (this.disabled) return; this.isOpen = !this.isOpen; }, diff --git a/frontend/src/views/falukant/BranchView.vue b/frontend/src/views/falukant/BranchView.vue index a124bd5..aaf91b4 100644 --- a/frontend/src/views/falukant/BranchView.vue +++ b/frontend/src/views/falukant/BranchView.vue @@ -360,6 +360,7 @@ export default { vehicles: [], activeTab: 'production', productPricesCache: {}, // Cache für regionale Preise: { productId: price } + productPricesCacheRegionId: null, // regionId, für die der Cache gültig ist tabs: [ { value: 'production', label: 'falukant.branch.tabs.production' }, { value: 'inventory', label: 'falukant.branch.tabs.inventory' }, @@ -569,20 +570,20 @@ export default { async loadProductPricesForCurrentBranch() { if (!this.selectedBranch || !this.selectedBranch.regionId) { this.productPricesCache = {}; + this.productPricesCacheRegionId = null; + return; + } + if (this.productPricesCacheRegionId === this.selectedBranch.regionId && Object.keys(this.productPricesCache).length > 0) { return; } - - // Lade alle Preise für die Region auf einmal (Bulk-Request) try { - const startTime = performance.now(); const { data } = await apiClient.get('/api/falukant/products/prices-in-region', { params: { 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.productPricesCacheRegionId = this.selectedBranch.regionId; } catch (error) { console.error(`Error loading product prices for region ${this.selectedBranch.regionId}:`, error); // Fallback: Lade Preise einzeln (alte Methode) @@ -607,6 +608,7 @@ export default { } } this.productPricesCache = prices; + this.productPricesCacheRegionId = this.selectedBranch?.regionId ?? null; } },