From f7a977df33f7834f2e89306210c7eaa5a511a294 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Thu, 29 Jan 2026 16:04:43 +0100 Subject: [PATCH] Enhance FalukantService with character caching and optimized city retrieval: Introduce caching for cities with branch types to reduce database queries, and streamline character retrieval logic. Update product and knowledge fetching to improve performance and maintainability. --- backend/services/falukantService.js | 71 ++++++++++++++--------------- 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/backend/services/falukantService.js b/backend/services/falukantService.js index 5765b93..cd42fd3 100644 --- a/backend/services/falukantService.js +++ b/backend/services/falukantService.js @@ -4541,36 +4541,47 @@ class FalukantService extends BaseService { const priceByProduct = new Map(items.map(i => [i.productId, i.currentPrice])); const user = await this.getFalukantUserByHashedId(hashedUserId); - const character = await FalukantCharacter.findOne({ where: { userId: user.id } }); + const character = user.character || await FalukantCharacter.findOne({ where: { userId: user.id }, attributes: ['id'] }); if (!character) { throw new Error(`No FalukantCharacter found for user with id ${user.id}`); } + const characterId = character.id; - const [products, knowledges, cities, townWorths] = await Promise.all([ + let citiesWithBranchType = FalukantService._citiesBatchCache?.get(user.id); + const now = Date.now(); + if (citiesWithBranchType && citiesWithBranchType.expires > now) { + citiesWithBranchType = citiesWithBranchType.data; + } else { + const cityRows = await sequelize.query( + `SELECT r.id, r.name, + MAX(CASE WHEN bt.label_tr IN ('store','fullstack') THEN 2 WHEN bt.label_tr = 'production' THEN 1 ELSE 0 END) AS branch_type_sort +FROM falukant_data.region r +INNER JOIN falukant_type.region rt ON r.region_type_id = rt.id AND rt.label_tr = 'city' +LEFT JOIN falukant_data.branch b ON b.region_id = r.id AND b.falukant_user_id = :userId +LEFT JOIN falukant_type.branch bt ON b.branch_type_id = bt.id +GROUP BY r.id, r.name +ORDER BY r.id`, + { replacements: { userId: user.id }, type: sequelize.QueryTypes.SELECT } + ); + const branchTypeByCityId = new Map(); + const cities = []; + for (const row of cityRows) { + cities.push({ id: row.id, name: row.name }); + const sort = row.branch_type_sort ?? 0; + branchTypeByCityId.set(row.id, sort === 2 ? 'store' : sort === 1 ? 'production' : null); + } + citiesWithBranchType = { cities, branchTypeByCityId }; + if (!FalukantService._citiesBatchCache) FalukantService._citiesBatchCache = new Map(); + FalukantService._citiesBatchCache.set(user.id, { data: citiesWithBranchType, expires: now + 60000 }); + } + const { cities, branchTypeByCityId } = citiesWithBranchType; + + const [products, knowledges, townWorths] = await Promise.all([ ProductType.findAll({ where: { id: { [Op.in]: productIds } }, attributes: ['id', 'sellCost'] }), Knowledge.findAll({ - where: { characterId: character.id, productId: { [Op.in]: productIds } }, + where: { characterId: characterId, productId: { [Op.in]: productIds } }, attributes: ['productId', 'knowledge'] }), - 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: { [Op.in]: productIds } }, attributes: ['productId', 'regionId', 'worthPercent'] @@ -4578,12 +4589,7 @@ class FalukantService extends BaseService { ]); const knowledgeByProduct = new Map(knowledges.map(k => [k.productId, k.knowledge || 0])); - const worthByProductRegion = new Map(); - for (const tw of townWorths) { - const key = `${tw.productId}-${tw.regionId}`; - worthByProductRegion.set(key, tw.worthPercent); - } - const productById = new Map(products.map(p => [p.id, p])); + const worthByProductRegion = new Map(townWorths.map(tw => [`${tw.productId}-${tw.regionId}`, tw.worthPercent])); const PRICE_TOLERANCE = 0.01; const out = {}; @@ -4604,18 +4610,11 @@ class FalukantService extends BaseService { const priceInCity = calcRegionalSellPriceSync(product, knowledgeFactor, worthPercent); if (priceInCity == null) continue; if (priceInCity <= currentRegionalPrice - PRICE_TOLERANCE) continue; - - let branchType = null; - if (city.branches && city.branches.length > 0) { - const branchTypes = city.branches.map(b => b.branchType?.labelTr).filter(Boolean); - if (branchTypes.includes('store') || branchTypes.includes('fullstack')) branchType = 'store'; - else if (branchTypes.includes('production')) branchType = 'production'; - } results.push({ regionId: city.id, regionName: city.name, price: priceInCity, - branchType + branchType: branchTypeByCityId.get(city.id) ?? null }); } results.sort((a, b) => b.price - a.price);