/** * Periodischer Job: reputation_periodic für politische Amtsinhaber. * Aufruf: systemd-Timer oder FALUKANT_POLITICAL_REPUTATION_JOB=1 (siehe server.js). */ import PoliticalOffice from '../models/falukant/data/political_office.js'; import PoliticalOfficeType from '../models/falukant/type/political_office_type.js'; import PoliticalOfficeBenefit from '../models/falukant/predefine/political_office_benefit.js'; import PoliticalOfficeBenefitType from '../models/falukant/type/political_office_benefit_type.js'; import PoliticalBenefitLastTick from '../models/falukant/data/political_benefit_last_tick.js'; import FalukantCharacter from '../models/falukant/data/character.js'; import FalukantUser from '../models/falukant/data/user.js'; import User from '../models/community/user.js'; import { sequelize } from '../utils/sequelize.js'; import { notifyUser } from '../utils/socket.js'; export async function runPoliticalReputationTicks() { const offices = await PoliticalOffice.findAll({ include: [{ model: PoliticalOfficeType, as: 'type', attributes: ['id', 'name'] }] }); const toNotify = new Set(); let ticks = 0; for (const po of offices) { const characterId = po.characterId; const benefitRows = await PoliticalOfficeBenefit.findAll({ where: { officeTypeId: po.officeTypeId }, include: [ { model: PoliticalOfficeBenefitType, as: 'benefitDefinition', attributes: ['tr'], required: true, where: { tr: 'reputation_periodic' } } ] }); for (const br of benefitRows) { const v = br.value && typeof br.value === 'object' ? br.value : {}; const intervalDays = Math.max(1, Number(v.intervalDays ?? v.everyDays ?? 7)); const gain = Math.max(1, Number(v.gain ?? 1)); const [tickRow, created] = await PoliticalBenefitLastTick.findOrCreate({ where: { characterId, politicalOfficeBenefitId: br.id }, defaults: { characterId, politicalOfficeBenefitId: br.id, lastTickAt: new Date(po.createdAt), ticksCount: 0 } }); const baseMs = new Date(tickRow.lastTickAt).getTime(); const daysSince = Math.floor((Date.now() - baseMs) / 86400000); if (daysSince < intervalDays) continue; await sequelize.transaction(async (t) => { const ch = await FalukantCharacter.findByPk(characterId, { transaction: t }); if (!ch) return; const nextRep = Math.min(100, (ch.reputation ?? 0) + gain); await ch.update({ reputation: nextRep }, { transaction: t }); await PoliticalBenefitLastTick.update( { lastTickAt: new Date(), ticksCount: (tickRow.ticksCount || 0) + 1 }, { where: { id: tickRow.id }, transaction: t } ); }); ticks += 1; toNotify.add(characterId); console.info( `[PoliticalBenefits] reputation_tick characterId=${characterId} benefitId=${br.id} gain=${gain}` ); } } for (const characterId of toNotify) { const ch = await FalukantCharacter.findByPk(characterId, { attributes: ['userId'] }); if (!ch?.userId) continue; const fu = await FalukantUser.findOne({ where: { id: ch.userId }, include: [{ model: User, as: 'user', attributes: ['hashedId'] }] }); const hid = fu?.user?.hashedId; if (hid) notifyUser(hid, 'falukantUpdateStatus', {}); } return { processedOffices: offices.length, ticksApplied: ticks }; }