Refactor proposal generation in FalukantService to improve character selection logic
- Removed the tracking of used character IDs and streamlined the exclusion of characters already proposed or currently active as directors. - Enhanced logging for SQL queries and fallback mechanisms to ensure better visibility during character selection. - Implemented a more efficient approach to gather and process character knowledge for proposal creation, ensuring accurate average calculations. - Improved error handling to provide clearer feedback when no eligible characters are found.
This commit is contained in:
@@ -2462,8 +2462,6 @@ class FalukantService extends BaseService {
|
||||
try {
|
||||
const threeWeeksAgo = new Date(Date.now() - 21 * 24 * 60 * 60 * 1000);
|
||||
const proposalCount = Math.floor(Math.random() * 3) + 3;
|
||||
let createdProposals = 0;
|
||||
const usedCharacterIds = new Set(); // Verhindere doppelte Proposals
|
||||
|
||||
// Hole bereits existierende Proposals, um diese Charaktere auszuschließen
|
||||
const existingProposals = await DirectorProposal.findAll({
|
||||
@@ -2471,18 +2469,34 @@ class FalukantService extends BaseService {
|
||||
attributes: ['directorCharacterId'],
|
||||
raw: true
|
||||
});
|
||||
existingProposals.forEach(p => usedCharacterIds.add(p.directorCharacterId));
|
||||
const proposalCharacterIds = existingProposals.map(p => p.directorCharacterId);
|
||||
|
||||
// Hole alle Charaktere, die bereits als Direktor arbeiten (egal für welchen User)
|
||||
const existingDirectors = await Director.findAll({
|
||||
attributes: ['directorCharacterId'],
|
||||
raw: true
|
||||
});
|
||||
const directorCharacterIds = existingDirectors.map(d => d.directorCharacterId);
|
||||
|
||||
// Kombiniere beide Listen
|
||||
const excludedCharacterIds = [...new Set([...proposalCharacterIds, ...directorCharacterIds])];
|
||||
|
||||
console.log(`[generateProposals] Excluding ${excludedCharacterIds.length} characters (${proposalCharacterIds.length} proposals + ${directorCharacterIds.length} active directors)`);
|
||||
console.log(`[generateProposals] Region ID: ${regionId}, Proposal count needed: ${proposalCount}`);
|
||||
|
||||
for (let i = 0; i < proposalCount; i++) {
|
||||
// Versuche zuerst Charaktere, die mindestens 3 Wochen alt sind
|
||||
let whereClause = {
|
||||
regionId,
|
||||
userId: null, // Nur NPCs
|
||||
id: { [Op.notIn]: Array.from(usedCharacterIds) }, // Keine bereits verwendeten
|
||||
createdAt: { [Op.lt]: threeWeeksAgo },
|
||||
};
|
||||
|
||||
let directorCharacter = await FalukantCharacter.findOne({
|
||||
if (excludedCharacterIds.length > 0) {
|
||||
whereClause.id = { [Op.notIn]: excludedCharacterIds };
|
||||
}
|
||||
whereClause.createdAt = { [Op.lt]: threeWeeksAgo };
|
||||
|
||||
// Erstelle Query-Objekt für Logging
|
||||
const queryOptions = {
|
||||
where: whereClause,
|
||||
include: [
|
||||
{
|
||||
@@ -2492,18 +2506,38 @@ class FalukantService extends BaseService {
|
||||
},
|
||||
],
|
||||
order: sequelize.literal('RANDOM()'),
|
||||
});
|
||||
limit: proposalCount,
|
||||
};
|
||||
|
||||
// Fallback: Wenn keine älteren Charaktere gefunden werden, verwende auch neuere
|
||||
if (!directorCharacter) {
|
||||
console.log(`[generateProposals] No characters older than 3 weeks found in region ${regionId}, trying all NPCs...`);
|
||||
whereClause = {
|
||||
// Logge die SQL-Query
|
||||
try {
|
||||
const query = FalukantCharacter.findAll(queryOptions);
|
||||
const sqlQuery = query.toSQL ? query.toSQL() : query;
|
||||
console.log(`[generateProposals] SQL Query (older than 3 weeks):`, JSON.stringify(sqlQuery, null, 2));
|
||||
} catch (e) {
|
||||
// Fallback: Logge die Query-Optionen direkt
|
||||
console.log(`[generateProposals] Query Options (older than 3 weeks):`, JSON.stringify(queryOptions, null, 2));
|
||||
}
|
||||
console.log(`[generateProposals] WHERE clause:`, JSON.stringify(whereClause, null, 2));
|
||||
console.log(`[generateProposals] Excluded character IDs:`, excludedCharacterIds);
|
||||
|
||||
let directorCharacters = await FalukantCharacter.findAll(queryOptions);
|
||||
|
||||
// Fallback: Wenn nicht genug ältere Charaktere gefunden werden, verwende auch neuere
|
||||
if (directorCharacters.length < proposalCount) {
|
||||
console.log(`[generateProposals] Only found ${directorCharacters.length} characters older than 3 weeks, trying all NPCs...`);
|
||||
|
||||
const fallbackWhereClause = {
|
||||
regionId,
|
||||
userId: null, // Nur NPCs
|
||||
id: { [Op.notIn]: Array.from(usedCharacterIds) },
|
||||
};
|
||||
directorCharacter = await FalukantCharacter.findOne({
|
||||
where: whereClause,
|
||||
|
||||
if (excludedCharacterIds.length > 0) {
|
||||
fallbackWhereClause.id = { [Op.notIn]: excludedCharacterIds };
|
||||
}
|
||||
|
||||
const fallbackQueryOptions = {
|
||||
where: fallbackWhereClause,
|
||||
include: [
|
||||
{
|
||||
model: TitleOfNobility,
|
||||
@@ -2512,35 +2546,79 @@ class FalukantService extends BaseService {
|
||||
},
|
||||
],
|
||||
order: sequelize.literal('RANDOM()'),
|
||||
limit: proposalCount,
|
||||
};
|
||||
|
||||
// Logge die Fallback-SQL-Query
|
||||
try {
|
||||
const fallbackQuery = FalukantCharacter.findAll(fallbackQueryOptions);
|
||||
const fallbackSqlQuery = fallbackQuery.toSQL ? fallbackQuery.toSQL() : fallbackQuery;
|
||||
console.log(`[generateProposals] SQL Query (all NPCs):`, JSON.stringify(fallbackSqlQuery, null, 2));
|
||||
} catch (e) {
|
||||
console.log(`[generateProposals] Fallback Query Options:`, JSON.stringify(fallbackQueryOptions, null, 2));
|
||||
}
|
||||
console.log(`[generateProposals] Fallback WHERE clause:`, JSON.stringify(fallbackWhereClause, null, 2));
|
||||
|
||||
const fallbackCharacters = await FalukantCharacter.findAll(fallbackQueryOptions);
|
||||
|
||||
// Kombiniere beide Listen und entferne Duplikate
|
||||
const allCharacterIds = new Set(directorCharacters.map(c => c.id));
|
||||
fallbackCharacters.forEach(c => {
|
||||
if (!allCharacterIds.has(c.id)) {
|
||||
directorCharacters.push(c);
|
||||
allCharacterIds.add(c.id);
|
||||
}
|
||||
});
|
||||
|
||||
// Limitiere auf proposalCount
|
||||
directorCharacters = directorCharacters.slice(0, proposalCount);
|
||||
}
|
||||
|
||||
if (!directorCharacter) {
|
||||
console.warn(`[generateProposals] No more available NPCs in region ${regionId} (${usedCharacterIds.size} already used)`);
|
||||
// Wenn keine Charaktere gefunden werden, breche die Schleife ab
|
||||
// aber wirf keinen Fehler, wenn bereits einige Proposals erstellt wurden
|
||||
if (createdProposals === 0) {
|
||||
if (directorCharacters.length === 0) {
|
||||
console.error(`[generateProposals] No NPCs found in region ${regionId} at all`);
|
||||
throw new Error('No directors available for the region');
|
||||
}
|
||||
break; // Stoppe die Schleife, aber wirf keinen Fehler
|
||||
}
|
||||
|
||||
// Füge diesen Charakter zu den verwendeten hinzu
|
||||
usedCharacterIds.add(directorCharacter.id);
|
||||
console.log(`[generateProposals] Found ${directorCharacters.length} available NPCs`);
|
||||
|
||||
// Batch-Berechnung der Knowledge-Werte
|
||||
const characterIds = directorCharacters.map(c => c.id);
|
||||
const allKnowledges = await Knowledge.findAll({
|
||||
where: { characterId: { [Op.in]: characterIds } },
|
||||
attributes: ['characterId', 'knowledge'],
|
||||
raw: true
|
||||
});
|
||||
|
||||
// Gruppiere Knowledge nach characterId und berechne Durchschnitt
|
||||
const knowledgeMap = new Map();
|
||||
characterIds.forEach(id => knowledgeMap.set(id, []));
|
||||
allKnowledges.forEach(k => {
|
||||
const list = knowledgeMap.get(k.characterId) || [];
|
||||
list.push(k.knowledge);
|
||||
knowledgeMap.set(k.characterId, list);
|
||||
});
|
||||
|
||||
// Erstelle alle Proposals in einem Batch
|
||||
const proposalsToCreate = directorCharacters.map(character => {
|
||||
const knowledges = knowledgeMap.get(character.id) || [];
|
||||
const avgKnowledge = knowledges.length > 0
|
||||
? knowledges.reduce((sum, k) => sum + k, 0) / knowledges.length
|
||||
: 0;
|
||||
|
||||
const avgKnowledge = await this.calculateAverageKnowledge(directorCharacter.id);
|
||||
const proposedIncome = Math.round(
|
||||
directorCharacter.nobleTitle.level * Math.pow(1.231, avgKnowledge / 1.5)
|
||||
character.nobleTitle.level * Math.pow(1.231, avgKnowledge / 1.5)
|
||||
);
|
||||
await DirectorProposal.create({
|
||||
directorCharacterId: directorCharacter.id,
|
||||
|
||||
return {
|
||||
directorCharacterId: character.id,
|
||||
employerUserId: falukantUserId,
|
||||
proposedIncome,
|
||||
};
|
||||
});
|
||||
createdProposals++;
|
||||
}
|
||||
|
||||
console.log(`[generateProposals] Created ${createdProposals} director proposals for region ${regionId}`);
|
||||
await DirectorProposal.bulkCreate(proposalsToCreate);
|
||||
|
||||
console.log(`[generateProposals] Created ${proposalsToCreate.length} director proposals for region ${regionId}`);
|
||||
} catch (error) {
|
||||
console.error('[generateProposals] Error:', error.message, error.stack);
|
||||
throw new Error(error.message);
|
||||
|
||||
Reference in New Issue
Block a user