Add executeReputationAction method in FalukantService: Implemented functionality to handle reputation actions for users, including daily limits, cooldowns, and cost checks. This update enhances user interaction with the reputation system and ensures proper transaction management.

This commit is contained in:
Torsten Schulz (local)
2026-02-05 23:57:28 +01:00
parent 10649d9fbf
commit 23b0c45518
3 changed files with 98 additions and 0 deletions

View File

@@ -126,6 +126,8 @@ class FalukantController {
this.getTitlesOfNobility = this._wrapWithUser((userId) => this.service.getTitlesOfNobility(userId));
this.getReputationActions = this._wrapWithUser((userId) => this.service.getReputationActions(userId));
this.executeReputationAction = this._wrapWithUser((userId, req) =>
this.service.executeReputationAction(userId, req.body?.actionTypeId), { successStatus: 201 });
this.getHouseTypes = this._wrapWithUser((userId) => this.service.getHouseTypes(userId));
this.getMoodAffect = this._wrapWithUser((userId) => this.service.getMoodAffect(userId));
this.getCharacterAffect = this._wrapWithUser((userId) => this.service.getCharacterAffect(userId));

View File

@@ -48,6 +48,7 @@ router.post('/family/gift', falukantController.sendGift);
router.get('/family', falukantController.getFamily);
router.get('/nobility/titels', falukantController.getTitlesOfNobility);
router.get('/reputation/actions', falukantController.getReputationActions);
router.post('/reputation/actions', falukantController.executeReputationAction);
router.get('/houses/types', falukantController.getHouseTypes);
router.get('/houses/buyable', falukantController.getBuyableHouses);
router.get('/houses', falukantController.getUserHouse);

View File

@@ -3285,6 +3285,101 @@ class FalukantService extends BaseService {
};
}
async executeReputationAction(hashedUserId, actionTypeId) {
const user = await getFalukantUserOrFail(hashedUserId);
const character = await FalukantCharacter.findOne({
where: { userId: user.id },
attributes: ['id', 'reputation']
});
if (!character) {
throw new Error('Character not found');
}
const actionType = await ReputationActionType.findByPk(actionTypeId);
if (!actionType) {
throw new Error('Action type not found');
}
const todayStart = new Date();
todayStart.setHours(0, 0, 0, 0);
const todayEnd = new Date();
todayEnd.setHours(23, 59, 59, 999);
const dailyCap = 10;
const todayActions = await ReputationActionLog.count({
where: {
falukantUserId: user.id,
actionTimestamp: { [Op.between]: [todayStart, todayEnd] }
}
});
if (todayActions >= dailyCap) {
throw new Error('Daily limit reached');
}
const lastAction = await ReputationActionLog.findOne({
where: { falukantUserId: user.id },
order: [['actionTimestamp', 'DESC']],
attributes: ['actionTimestamp']
});
const cooldownMinutes = 60;
if (lastAction?.actionTimestamp) {
const cooldownSec = cooldownMinutes * 60;
const secondsSinceLast = Math.floor((Date.now() - new Date(lastAction.actionTimestamp)) / 1000);
if (secondsSinceLast < cooldownSec) {
throw new Error('Cooldown active');
}
}
const reloadedUser = await FalukantUser.findByPk(user.id, { attributes: ['id', 'money'] });
if (reloadedUser.money < actionType.cost) {
throw new Error('notenoughmoney');
}
const windowStart = new Date();
windowStart.setDate(windowStart.getDate() - actionType.decayWindowDays);
const timesUsedBefore = await ReputationActionLog.count({
where: {
falukantUserId: user.id,
actionTypeId: actionType.id,
actionTimestamp: { [Op.gte]: windowStart }
}
});
let gain = actionType.baseGain;
for (let i = 0; i < timesUsedBefore; i++) {
gain = Math.max(actionType.minGain, Math.floor(gain * actionType.decayFactor));
}
const transaction = await sequelize.transaction();
try {
await updateFalukantUserMoney(
user.id,
-actionType.cost,
'Reputation action: ' + actionType.tr,
user.id,
transaction
);
await ReputationActionLog.create(
{
falukantUserId: user.id,
actionTypeId: actionType.id,
cost: actionType.cost,
baseGain: actionType.baseGain,
gain,
timesUsedBefore
},
{ transaction }
);
const newReputation = Math.min(100, Math.max(0, (character.reputation ?? 0) + gain));
await character.update({ reputation: newReputation }, { transaction });
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
return { gain, cost: actionType.cost };
}
async getHouseTypes() {
// return House
}