Files
yourpart3/backend/jobs/politicalBenefitsTick.js
Torsten Schulz (local) 56be4b76c0
All checks were successful
Deploy to production / deploy (push) Successful in 3m3s
feat(political-benefits): implement political powers and benefits system
- Added new political powers and benefits functionalities, including reputation ticks, tax jurisdiction management, and appointment capabilities.
- Introduced a new job for periodic reputation updates and created necessary database tables for tracking political benefits.
- Enhanced the FalukantController and services to support new endpoints for managing political powers and appointments.
- Updated localization files to reflect new features and improve user experience across multiple languages.
- Modified the UI to display new political powers and benefits, ensuring accurate representation in the PoliticsView.
2026-04-02 16:00:29 +02:00

95 lines
3.9 KiB
JavaScript

/**
* 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 };
}