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:
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user