From c5ab17ad99a89cd597c7b62e4563f14b14e3d443 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Thu, 29 Jan 2026 15:20:38 +0100 Subject: [PATCH] Refactor FalukantService and SaleSection components: Optimize product and knowledge retrieval by using Promise.all for concurrent database queries, improving performance. Additionally, reorganize the speedLabel method for better readability and maintainability, ensuring accurate localization handling for transport speed values. --- backend/services/falukantService.js | 102 ++++++++---------- .../src/components/falukant/SaleSection.vue | 27 +++-- 2 files changed, 55 insertions(+), 74 deletions(-) diff --git a/backend/services/falukantService.js b/backend/services/falukantService.js index 6316c9a..326265c 100644 --- a/backend/services/falukantService.js +++ b/backend/services/falukantService.js @@ -4445,79 +4445,61 @@ class FalukantService extends BaseService { throw new Error(`No FalukantCharacter found for user with id ${user.id}`); } - // Produkt abrufen - const product = await ProductType.findOne({ where: { id: productId } }); + const [product, knowledge, cities, townWorths] = await Promise.all([ + ProductType.findOne({ where: { id: productId } }), + Knowledge.findOne({ + where: { characterId: character.id, productId: productId } + }), + RegionData.findAll({ + attributes: ['id', 'name'], + include: [ + { + model: RegionType, + as: 'regionType', + where: { labelTr: 'city' }, + attributes: ['labelTr'] + }, + { + model: Branch, + as: 'branches', + where: { falukantUserId: user.id }, + include: [ + { + model: BranchType, + as: 'branchType', + attributes: ['labelTr'] + } + ], + attributes: ['branchTypeId'], + required: false + } + ] + }), + TownProductWorth.findAll({ + where: { productId: productId }, + attributes: ['regionId', 'worthPercent'] + }) + ]); + if (!product) { throw new Error(`Product not found with id ${productId}`); } - - // Knowledge für dieses Produkt abrufen - const knowledge = await Knowledge.findOne({ - where: { characterId: character.id, productId: productId } - }); const knowledgeFactor = knowledge?.knowledge || 0; - - // Alle Städte abrufen - const cities = await RegionData.findAll({ - attributes: ['id', 'name'], - include: [ - { - model: RegionType, - as: 'regionType', - where: { labelTr: 'city' }, - attributes: ['labelTr'] - }, - { - model: Branch, - as: 'branches', - where: { falukantUserId: user.id }, - include: [ - { - model: BranchType, - as: 'branchType', - attributes: ['labelTr'] - } - ], - attributes: ['branchTypeId'], - required: false - } - ] - }); - - // TownProductWorth für alle Städte und dieses Produkt einmalig abrufen - // (vermeidet N+1 Query Problem) - const townWorths = await TownProductWorth.findAll({ - where: { productId: productId }, - attributes: ['regionId', 'worthPercent'] - }); const worthMap = new Map(townWorths.map(tw => [tw.regionId, tw.worthPercent])); - // Berechne den regionalen Preis für die aktuelle Region (falls angegeben) - // WICHTIG: Ignoriere den übergebenen currentPrice, da er möglicherweise nicht - // den regionalen Faktor berücksichtigt. Berechne stattdessen immer den korrekten - // regionalen Preis basierend auf currentRegionId. - let currentRegionalPrice = currentPrice; // Fallback auf übergebenen Preis + let currentRegionalPrice = currentPrice; if (currentRegionId) { const currentWorthPercent = worthMap.get(currentRegionId) || 50; - // Verwende calcRegionalSellPrice mit bereits geladenem worthPercent (keine DB-Query) - currentRegionalPrice = await calcRegionalSellPrice(product, knowledgeFactor, currentRegionId, currentWorthPercent); + currentRegionalPrice = calcRegionalSellPriceSync(product, knowledgeFactor, currentWorthPercent) ?? currentPrice; } - // Für jede Stadt den Preis berechnen und Branch-Typ bestimmen const results = []; + const PRICE_TOLERANCE = 0.01; for (const city of cities) { - // Aktuelle Stadt ausschließen - if (currentRegionId && city.id === currentRegionId) { - continue; - } - + if (currentRegionId && city.id === currentRegionId) continue; const worthPercent = worthMap.get(city.id) || 50; - // Verwende calcRegionalSellPrice mit bereits geladenem worthPercent (keine DB-Query) - const priceInCity = await calcRegionalSellPrice(product, knowledgeFactor, city.id, worthPercent); - - // Nur Städte zurückgeben, wo der Preis höher ist - // Kleine Toleranz (0.01) für Rundungsfehler bei Gleitkommaberechnungen - const PRICE_TOLERANCE = 0.01; + const priceInCity = calcRegionalSellPriceSync(product, knowledgeFactor, worthPercent); + if (priceInCity == null) continue; if (priceInCity > currentRegionalPrice - PRICE_TOLERANCE) { // Branch-Typ bestimmen let branchType = null; // null = kein Branch diff --git a/frontend/src/components/falukant/SaleSection.vue b/frontend/src/components/falukant/SaleSection.vue index 8555379..9376ea8 100644 --- a/frontend/src/components/falukant/SaleSection.vue +++ b/frontend/src/components/falukant/SaleSection.vue @@ -252,20 +252,6 @@ }); }, }, - methods: { - speedLabel(value) { - if (value == null) return this.$t('falukant.branch.transport.speed.unknown') || '—'; - if (typeof value === 'object') { - const k = value.tr ?? value.id ?? 'unknown'; - const tKey = `falukant.branch.transport.speed.${k}`; - const t = this.$t(tKey); - return (t && t !== tKey) ? t : String(k); - } - const key = String(value); - const tKey = `falukant.branch.transport.speed.${key}`; - const translated = this.$t(tKey); - return (!translated || translated === tKey) ? key : translated; - }, async mounted() { await this.loadInventory(); await this.loadTransports(); @@ -281,6 +267,19 @@ } }, methods: { + speedLabel(value) { + if (value == null) return this.$t('falukant.branch.transport.speed.unknown') || '—'; + if (typeof value === 'object') { + const k = value.tr ?? value.id ?? 'unknown'; + const tKey = `falukant.branch.transport.speed.${k}`; + const t = this.$t(tKey); + return (t && t !== tKey) ? t : String(k); + } + const key = String(value); + const tKey = `falukant.branch.transport.speed.${key}`; + const translated = this.$t(tKey); + return (!translated || translated === tKey) ? key : translated; + }, async loadInventory() { try { const response = await apiClient.get(`/api/falukant/inventory/${this.branchId}`);