Enhance NPC creation functionality and validation in Admin module
- Updated AdminController to validate the count parameter, ensuring it is between 1 and 500. - Refactored NPC creation logic in AdminService to create NPCs for each city-title combination, improving efficiency. - Enhanced frontend localization files to reflect changes in count descriptions and validation messages. - Updated CreateNPCView to provide user guidance on count input and display detailed creation results.
This commit is contained in:
@@ -389,13 +389,17 @@ class AdminController {
|
|||||||
try {
|
try {
|
||||||
const { userid: userId } = req.headers;
|
const { userid: userId } = req.headers;
|
||||||
const { regionIds, minAge, maxAge, minTitleId, maxTitleId, count } = req.body;
|
const { regionIds, minAge, maxAge, minTitleId, maxTitleId, count } = req.body;
|
||||||
|
const countValue = parseInt(count) || 1;
|
||||||
|
if (countValue < 1 || countValue > 500) {
|
||||||
|
return res.status(400).json({ error: 'Count must be between 1 and 500' });
|
||||||
|
}
|
||||||
const result = await AdminService.createNPCs(userId, {
|
const result = await AdminService.createNPCs(userId, {
|
||||||
regionIds: regionIds && regionIds.length > 0 ? regionIds : null,
|
regionIds: regionIds && regionIds.length > 0 ? regionIds : null,
|
||||||
minAge: parseInt(minAge) || 0,
|
minAge: parseInt(minAge) || 0,
|
||||||
maxAge: parseInt(maxAge) || 100,
|
maxAge: parseInt(maxAge) || 100,
|
||||||
minTitleId: parseInt(minTitleId) || 1,
|
minTitleId: parseInt(minTitleId) || 1,
|
||||||
maxTitleId: parseInt(maxTitleId) || 19,
|
maxTitleId: parseInt(maxTitleId) || 19,
|
||||||
count: parseInt(count) || 1
|
count: countValue
|
||||||
});
|
});
|
||||||
res.status(200).json(result);
|
res.status(200).json(result);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -1156,75 +1156,78 @@ class AdminService {
|
|||||||
|
|
||||||
const genders = ['male', 'female'];
|
const genders = ['male', 'female'];
|
||||||
const createdNPCs = [];
|
const createdNPCs = [];
|
||||||
|
const totalNPCs = targetRegions.length * titles.length * count;
|
||||||
|
|
||||||
// Erstelle NPCs in einer Transaktion
|
// Erstelle NPCs in einer Transaktion
|
||||||
|
// Für jede Stadt-Titel-Kombination wird die angegebene Anzahl erstellt
|
||||||
await sequelize.transaction(async (t) => {
|
await sequelize.transaction(async (t) => {
|
||||||
for (let i = 0; i < count; i++) {
|
for (const region of targetRegions) {
|
||||||
// Zufällige Region
|
for (const title of titles) {
|
||||||
const region = targetRegions[Math.floor(Math.random() * targetRegions.length)];
|
// Erstelle 'count' NPCs für diese Stadt-Titel-Kombination
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
// Zufälliges Geschlecht
|
||||||
|
const gender = genders[Math.floor(Math.random() * genders.length)];
|
||||||
|
|
||||||
// Zufälliges Geschlecht
|
// Zufälliger Vorname
|
||||||
const gender = genders[Math.floor(Math.random() * genders.length)];
|
const firstName = await FalukantPredefineFirstname.findAll({
|
||||||
|
where: { gender },
|
||||||
|
order: sequelize.fn('RANDOM'),
|
||||||
|
limit: 1,
|
||||||
|
transaction: t
|
||||||
|
});
|
||||||
|
if (firstName.length === 0) {
|
||||||
|
throw new Error(`No first names found for gender: ${gender}`);
|
||||||
|
}
|
||||||
|
const fnObj = firstName[0];
|
||||||
|
|
||||||
// Zufälliger Vorname
|
// Zufälliger Nachname
|
||||||
const firstName = await FalukantPredefineFirstname.findAll({
|
const lastName = await FalukantPredefineLastname.findAll({
|
||||||
where: { gender },
|
order: sequelize.fn('RANDOM'),
|
||||||
order: sequelize.fn('RANDOM'),
|
limit: 1,
|
||||||
limit: 1,
|
transaction: t
|
||||||
transaction: t
|
});
|
||||||
});
|
if (lastName.length === 0) {
|
||||||
if (firstName.length === 0) {
|
throw new Error('No last names found');
|
||||||
throw new Error(`No first names found for gender: ${gender}`);
|
}
|
||||||
|
const lnObj = lastName[0];
|
||||||
|
|
||||||
|
// Zufälliges Alter (in Jahren, wird in Tage umgerechnet)
|
||||||
|
const randomAge = Math.floor(Math.random() * (maxAge - minAge + 1)) + minAge;
|
||||||
|
const birthdate = new Date();
|
||||||
|
birthdate.setDate(birthdate.getDate() - randomAge); // 5 Tage = 5 Jahre alt
|
||||||
|
|
||||||
|
// Erstelle den NPC-Charakter (ohne userId = NPC)
|
||||||
|
const npc = await FalukantCharacter.create({
|
||||||
|
userId: null, // Wichtig: null = NPC
|
||||||
|
regionId: region.id,
|
||||||
|
firstName: fnObj.id,
|
||||||
|
lastName: lnObj.id,
|
||||||
|
gender: gender,
|
||||||
|
birthdate: birthdate,
|
||||||
|
titleOfNobility: title.id,
|
||||||
|
health: 100,
|
||||||
|
moodId: 1
|
||||||
|
}, { transaction: t });
|
||||||
|
|
||||||
|
createdNPCs.push({
|
||||||
|
id: npc.id,
|
||||||
|
firstName: fnObj.name,
|
||||||
|
lastName: lnObj.name,
|
||||||
|
gender: gender,
|
||||||
|
age: randomAge,
|
||||||
|
region: region.name,
|
||||||
|
title: title.labelTr
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const fnObj = firstName[0];
|
|
||||||
|
|
||||||
// Zufälliger Nachname
|
|
||||||
const lastName = await FalukantPredefineLastname.findAll({
|
|
||||||
order: sequelize.fn('RANDOM'),
|
|
||||||
limit: 1,
|
|
||||||
transaction: t
|
|
||||||
});
|
|
||||||
if (lastName.length === 0) {
|
|
||||||
throw new Error('No last names found');
|
|
||||||
}
|
|
||||||
const lnObj = lastName[0];
|
|
||||||
|
|
||||||
// Zufälliges Alter (in Jahren, wird in Tage umgerechnet)
|
|
||||||
const randomAge = Math.floor(Math.random() * (maxAge - minAge + 1)) + minAge;
|
|
||||||
const birthdate = new Date();
|
|
||||||
birthdate.setDate(birthdate.getDate() - randomAge); // 5 Tage = 5 Jahre alt
|
|
||||||
|
|
||||||
// Zufälliger Title
|
|
||||||
const title = titles[Math.floor(Math.random() * titles.length)];
|
|
||||||
|
|
||||||
// Erstelle den NPC-Charakter (ohne userId = NPC)
|
|
||||||
const npc = await FalukantCharacter.create({
|
|
||||||
userId: null, // Wichtig: null = NPC
|
|
||||||
regionId: region.id,
|
|
||||||
firstName: fnObj.id,
|
|
||||||
lastName: lnObj.id,
|
|
||||||
gender: gender,
|
|
||||||
birthdate: birthdate,
|
|
||||||
titleOfNobility: title.id,
|
|
||||||
health: 100,
|
|
||||||
moodId: 1
|
|
||||||
}, { transaction: t });
|
|
||||||
|
|
||||||
createdNPCs.push({
|
|
||||||
id: npc.id,
|
|
||||||
firstName: fnObj.name,
|
|
||||||
lastName: lnObj.name,
|
|
||||||
gender: gender,
|
|
||||||
age: randomAge,
|
|
||||||
region: region.name,
|
|
||||||
title: title.labelTr
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
count: createdNPCs.length,
|
count: createdNPCs.length,
|
||||||
|
countPerCombination: count,
|
||||||
|
totalCombinations: targetRegions.length * titles.length,
|
||||||
npcs: createdNPCs
|
npcs: createdNPCs
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,18 +122,20 @@
|
|||||||
"to": "bis",
|
"to": "bis",
|
||||||
"years": "Jahre",
|
"years": "Jahre",
|
||||||
"titleRange": "Titel-Bereich",
|
"titleRange": "Titel-Bereich",
|
||||||
"count": "Anzahl",
|
"count": "Anzahl pro Stadt-Titel-Kombination",
|
||||||
|
"countHelp": "Diese Anzahl wird für jede Kombination aus gewählter Stadt und Titel erstellt.",
|
||||||
"create": "NPCs erstellen",
|
"create": "NPCs erstellen",
|
||||||
"creating": "Erstelle...",
|
"creating": "Erstelle...",
|
||||||
"result": "Ergebnis",
|
"result": "Ergebnis",
|
||||||
"createdCount": "{count} NPCs wurden erstellt.",
|
"createdCount": "{count} NPCs wurden erstellt.",
|
||||||
|
"combinationInfo": "{perCombination} NPCs pro Kombination × {combinations} Kombinationen = {count} NPCs insgesamt",
|
||||||
"age": "Alter",
|
"age": "Alter",
|
||||||
"errorLoadingRegions": "Fehler beim Laden der Städte.",
|
"errorLoadingRegions": "Fehler beim Laden der Städte.",
|
||||||
"errorLoadingTitles": "Fehler beim Laden der Titel.",
|
"errorLoadingTitles": "Fehler beim Laden der Titel.",
|
||||||
"errorCreating": "Fehler beim Erstellen der NPCs.",
|
"errorCreating": "Fehler beim Erstellen der NPCs.",
|
||||||
"invalidAgeRange": "Ungültiger Altersbereich.",
|
"invalidAgeRange": "Ungültiger Altersbereich.",
|
||||||
"invalidTitleRange": "Ungültiger Titel-Bereich.",
|
"invalidTitleRange": "Ungültiger Titel-Bereich.",
|
||||||
"invalidCount": "Ungültige Anzahl (1-100)."
|
"invalidCount": "Ungültige Anzahl (1-500)."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"chatrooms": {
|
"chatrooms": {
|
||||||
|
|||||||
@@ -149,18 +149,20 @@
|
|||||||
"to": "to",
|
"to": "to",
|
||||||
"years": "years",
|
"years": "years",
|
||||||
"titleRange": "Title Range",
|
"titleRange": "Title Range",
|
||||||
"count": "Count",
|
"count": "Count per City-Title Combination",
|
||||||
|
"countHelp": "This count will be created for each combination of selected city and title.",
|
||||||
"create": "Create NPCs",
|
"create": "Create NPCs",
|
||||||
"creating": "Creating...",
|
"creating": "Creating...",
|
||||||
"result": "Result",
|
"result": "Result",
|
||||||
"createdCount": "{count} NPCs have been created.",
|
"createdCount": "{count} NPCs have been created.",
|
||||||
|
"combinationInfo": "{perCombination} NPCs per combination × {combinations} combinations = {count} NPCs total",
|
||||||
"age": "Age",
|
"age": "Age",
|
||||||
"errorLoadingRegions": "Error loading cities.",
|
"errorLoadingRegions": "Error loading cities.",
|
||||||
"errorLoadingTitles": "Error loading titles.",
|
"errorLoadingTitles": "Error loading titles.",
|
||||||
"errorCreating": "Error creating NPCs.",
|
"errorCreating": "Error creating NPCs.",
|
||||||
"invalidAgeRange": "Invalid age range.",
|
"invalidAgeRange": "Invalid age range.",
|
||||||
"invalidTitleRange": "Invalid title range.",
|
"invalidTitleRange": "Invalid title range.",
|
||||||
"invalidCount": "Invalid count (1-100)."
|
"invalidCount": "Invalid count (1-500)."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"chatrooms": {
|
"chatrooms": {
|
||||||
|
|||||||
@@ -47,7 +47,8 @@
|
|||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>{{ $t('admin.falukant.createNPC.count') }}:</label>
|
<label>{{ $t('admin.falukant.createNPC.count') }}:</label>
|
||||||
<input type="number" v-model.number="count" min="1" max="100" class="form-input" />
|
<input type="number" v-model.number="count" min="1" max="500" class="form-input" />
|
||||||
|
<div class="help-text">{{ $t('admin.falukant.createNPC.countHelp') }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
@@ -61,6 +62,13 @@
|
|||||||
<div v-if="result" class="result-section">
|
<div v-if="result" class="result-section">
|
||||||
<h2>{{ $t('admin.falukant.createNPC.result') }}</h2>
|
<h2>{{ $t('admin.falukant.createNPC.result') }}</h2>
|
||||||
<p>{{ $t('admin.falukant.createNPC.createdCount', { count: result.count }) }}</p>
|
<p>{{ $t('admin.falukant.createNPC.createdCount', { count: result.count }) }}</p>
|
||||||
|
<p v-if="result.totalCombinations" class="info-text">
|
||||||
|
{{ $t('admin.falukant.createNPC.combinationInfo', {
|
||||||
|
perCombination: result.countPerCombination,
|
||||||
|
combinations: result.totalCombinations,
|
||||||
|
total: result.count
|
||||||
|
}) }}
|
||||||
|
</p>
|
||||||
<div v-if="result.npcs && result.npcs.length > 0" class="npcs-list">
|
<div v-if="result.npcs && result.npcs.length > 0" class="npcs-list">
|
||||||
<div v-for="npc in result.npcs" :key="npc.id" class="npc-item">
|
<div v-for="npc in result.npcs" :key="npc.id" class="npc-item">
|
||||||
{{ $t(`falukant.titles.${npc.gender}.${npc.title}`) }} {{ npc.firstName }} {{ npc.lastName }}
|
{{ $t(`falukant.titles.${npc.gender}.${npc.title}`) }} {{ npc.firstName }} {{ npc.lastName }}
|
||||||
@@ -142,7 +150,7 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.count < 1 || this.count > 100) {
|
if (this.count < 1 || this.count > 500) {
|
||||||
this.error = this.$t('admin.falukant.createNPC.invalidCount');
|
this.error = this.$t('admin.falukant.createNPC.invalidCount');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -286,4 +294,17 @@ export default {
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.help-text {
|
||||||
|
font-size: 0.9em;
|
||||||
|
color: #666;
|
||||||
|
margin-top: 5px;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-text {
|
||||||
|
font-size: 0.9em;
|
||||||
|
color: #155724;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user