Files
yourpart3/backend/services/adminService.js
Torsten Schulz (local) 8f4327efb5 Änderung: Erweiterung der Benutzerstatistiken im Admin-Bereich
Änderungen:
- Neue Methode `getUserStatistics` im `AdminController` hinzugefügt, um Benutzerstatistiken abzurufen.
- Implementierung der Logik zur Berechnung der Gesamtanzahl aktiver Benutzer, Geschlechterverteilung und Altersverteilung im `AdminService`.
- Neue Route `/users/statistics` im `adminRouter` definiert, um auf die Benutzerstatistiken zuzugreifen.
- Anpassungen der Navigationsstruktur und Übersetzungen für Benutzerstatistiken in den Sprachdateien aktualisiert.

Diese Anpassungen verbessern die Analyse der Benutzerbasis und erweitern die Funktionalität des Admin-Bereichs.
2025-09-12 16:34:56 +02:00

966 lines
34 KiB
JavaScript

import RoomType from '../models/chat/room_type.js';
import ChatRight from '../models/chat/rights.js';
import UserRight from "../models/community/user_right.js";
import UserRightType from "../models/type/user_right.js";
import InterestType from "../models/type/interest.js";
import InterestTranslationType from "../models/type/interest_translation.js";
import User from "../models/community/user.js";
import UserParamValue from "../models/type/user_param_value.js";
import UserParamType from "../models/type/user_param.js";
import ContactMessage from "../models/service/contactmessage.js";
import ContactService from "./ContactService.js";
import { sendAnswerEmail } from './emailService.js';
import { Op } from 'sequelize';
import FalukantUser from "../models/falukant/data/user.js";
import FalukantCharacter from "../models/falukant/data/character.js";
import FalukantPredefineFirstname from "../models/falukant/predefine/firstname.js";
import FalukantPredefineLastname from "../models/falukant/predefine/lastname.js";
import Branch from "../models/falukant/data/branch.js";
import FalukantStock from "../models/falukant/data/stock.js";
import FalukantStockType from "../models/falukant/type/stock.js";
import RegionData from "../models/falukant/data/region.js";
import BranchType from "../models/falukant/type/branch.js";
import Room from '../models/chat/room.js';
import UserParam from '../models/community/user_param.js';
class AdminService {
async hasUserAccess(userId, section) {
const userRights = await UserRight.findAll({
include: [{
model: UserRightType,
as: 'rightType',
where: {
title: [section, 'mainadmin'],
}
},
{
model: User,
as: 'user_with_rights',
where: {
hashedId: userId,
}
}
]
});
return userRights.length > 0;
}
async getOpenInterests(userId) {
if (!this.hasUserAccess(userId, 'interests')) {
throw new Error('noaccess');
}
const openInterests = await InterestType.findAll({
where: {
allowed: false
},
include: {
model: InterestTranslationType,
as: 'interest_translations',
}
})
return openInterests;
}
async changeInterest(userId, interestId, active, adultOnly) {
if (!this.hasUserAccess(userId, 'interests')) {
throw new Error('noaccess');
}
const interest = await InterestType.findOne({
where: {
id: interestId
}
});
if (interest) {
interest.allowed = active;
interest.adultOnly = adultOnly;
await interest.save();
}
}
async deleteInterest(userId, interestId) {
if (!this.hasUserAccess(userId, 'interests')) {
throw new Error('noaccess');
}
const interest = await InterestType.findOne({
where: {
id: interestId
}
});
if (interest) {
await interest.destroy();
}
}
async changeTranslation(userId, interestId, translations) {
if (!this.hasUserAccess(userId, 'interests')) {
throw new Error('noaccess');
}
const interest = await InterestType.findOne({
id: interestId
});
if (!interest) {
throw new Error('notexisting');
}
for (const languageId of Object.keys(translations)) {
const languageObject = await UserParamValue.findOne(
{
where: {
id: languageId
}
}
);
if (!languageObject) {
throw new Error('wronglanguage');
}
const translation = await InterestTranslationType.findOne(
{
where: {
interestsId: interestId,
language: languageObject.id
}
}
);
if (translation) {
translation.translation = translations[languageId];
translation.save();
} else {
await InterestTranslationType.create({
interestsId: interestId,
language: languageObject.id,
translation: translations[languageId]
});
}
}
}
async getOpenContacts(userId) {
if (!this.hasUserAccess(userId, 'contacts')) {
throw new Error('noaccess');
}
const openContacts = await ContactMessage.findAll({
where: {
isFinished: false,
}
})
return openContacts;
}
async answerContact(contactId, answer) {
const contact = await ContactService.getContactById(contactId);
await ContactService.saveAnswer(contact, answer);
await sendAnswerEmail(contact.email, answer, contact.language || 'en');
}
async getFalukantUser(userId, userName, characterName) {
if (!(await this.hasUserAccess(userId, 'falukantusers'))) {
throw new Error('noaccess');
}
let users;
if (userName) {
users = await User.findAll({
where: {
username: {
[Op.like]: '%' + userName + '%'
}
},
include: [{
model: FalukantUser,
as: 'falukantData',
required: true,
include: [{
model: FalukantCharacter,
as: 'character',
required: true,
include: [{
model: FalukantPredefineFirstname,
as: 'definedFirstName',
required: true
}, {
model: FalukantPredefineLastname,
as: 'definedLastName',
required: true
}]
}]
}]
});
} else if (characterName) {
const [firstname, lastname] = characterName.split(' ');
users = await User.findAll({
include: [{
model: FalukantUser,
as: 'falukantData',
required: true,
include: [{
model: FalukantCharacter,
as: 'character',
required: true,
include: [{
model: FalukantPredefineFirstname,
as: 'definedFirstName',
required: true,
where: {
name: firstname
}
}, {
model: FalukantPredefineLastname,
as: 'definedLastName',
required: true,
where: {
name: lastname
}
}]
}]
}]
});
} else {
throw new Error('no search parameter');
}
return users.map(user => {
return {
id: user.hashedId,
username: user.username,
falukantUser: user.falukantData
}
});
}
async getFalukantUserById(userId, hashedId) {
if (!(await this.hasUserAccess(userId, 'falukantusers'))) {
throw new Error('noaccess');
}
const user = await User.findOne({
where: {
hashedId: hashedId
},
attributes: ['hashedId', 'username'],
include: [{
model: FalukantUser,
as: 'falukantData',
required: true,
attributes: ['money', 'certificate', 'id'],
include: [{
model: FalukantCharacter,
as: 'character',
attributes: ['birthdate', 'health', 'title_of_nobility'],
include: [{
model: FalukantPredefineFirstname,
as: 'definedFirstName',
}, {
model: FalukantPredefineLastname,
as: 'definedLastName',
}]
}]
}]
});
return user;
}
async getFalukantUserBranches(userId, falukantUserId) {
if (!(await this.hasUserAccess(userId, 'falukantusers'))) {
throw new Error('noaccess');
}
try {
// Zuerst die Branches laden
const branches = await Branch.findAll({
where: {
falukantUserId: falukantUserId
}
});
// Dann für jede Branch die zusätzlichen Daten laden
const branchesWithData = await Promise.all(branches.map(async (branch) => {
const region = await RegionData.findByPk(branch.regionId);
const branchType = await BranchType.findByPk(branch.branchTypeId);
const stocks = await FalukantStock.findAll({
where: { branchId: branch.id },
include: [{
model: FalukantStockType,
as: 'stockType',
attributes: ['labelTr']
}]
});
return {
...branch.toJSON(),
region: region ? { name: region.name } : null,
branchType: branchType ? { labelTr: branchType.labelTr } : null,
stocks: stocks
};
}));
return branchesWithData;
} catch (error) {
console.error('Error in getFalukantUserBranches:', error);
throw error;
}
}
async updateFalukantStock(userId, stockId, quantity) {
if (!(await this.hasUserAccess(userId, 'falukantusers'))) {
throw new Error('noaccess');
}
const stock = await FalukantStock.findByPk(stockId);
if (!stock) {
throw new Error('Stock not found');
}
stock.quantity = quantity;
await stock.save();
return stock;
}
async addFalukantStock(userId, branchId, stockTypeId, quantity) {
if (!(await this.hasUserAccess(userId, 'falukantusers'))) {
throw new Error('noaccess');
}
// Prüfe ob Branch existiert
const branch = await Branch.findByPk(branchId);
if (!branch) {
throw new Error('Branch not found');
}
// Prüfe ob StockType existiert
const stockType = await FalukantStockType.findByPk(stockTypeId);
if (!stockType) {
throw new Error('Stock type not found');
}
// Prüfe ob bereits ein Stock dieses Typs für diesen Branch existiert
const existingStock = await FalukantStock.findOne({
where: {
branchId: branchId,
stockTypeId: stockTypeId
}
});
if (existingStock) {
throw new Error('Stock of this type already exists for this branch');
}
// Erstelle neuen Stock
const newStock = await FalukantStock.create({
branchId: branchId,
stockTypeId: stockTypeId,
quantity: quantity
});
// Lade den neuen Stock mit allen Beziehungen
const stockWithData = await FalukantStock.findByPk(newStock.id, {
include: [{ model: FalukantStockType, as: 'stockType', attributes: ['labelTr'] }]
});
return stockWithData;
}
async getFalukantStockTypes(userId) {
if (!(await this.hasUserAccess(userId, 'falukantusers'))) {
throw new Error('noaccess');
}
const stockTypes = await FalukantStockType.findAll({
attributes: ['id', 'labelTr']
});
return stockTypes;
}
async changeFalukantUser(userId, falukantUserId, falukantData) {
if (!(await this.hasUserAccess(userId, 'falukantusers'))) {
throw new Error('noaccess');
}
const falukantUser = await FalukantUser.findOne({
where: {
id: falukantUserId
}
});
if (!falukantUser) {
throw new Error('notfound');
}
const character = await FalukantCharacter.findOne({
where: {
userId: falukantUserId
}
});
if (!character) {
throw new Error('notfound');
}
if (Object.keys(falukantData).indexOf('age') >= 0) {
const birthDate = (new Date()) - (falukantData.age * 24 * 3600000);
await character.update({
birthdate: birthDate
});
}
if (Object.keys(falukantData).indexOf('money') >= 0) {
await falukantUser.update({
money: falukantData.money
});
}
if (Object.keys(falukantData).indexOf('title_of_nobility') >= 0) {
await character.update({
titleOfNobility: falukantData.title_of_nobility
});
}
await falukantUser.save();
await character.save();
}
// --- User Administration ---
async searchUsers(requestingHashedUserId, query) {
if (!(await this.hasUserAccess(requestingHashedUserId, 'useradministration'))) {
throw new Error('noaccess');
}
if (!query || query.trim().length === 0) return [];
const users = await User.findAll({
where: {
[Op.or]: [
{ username: { [Op.iLike]: `%${query}%` } },
// email is encrypted, can't search directly reliably; skip email search
]
},
attributes: ['id', 'hashedId', 'username', 'active', 'registrationDate']
});
return users.map(u => ({ id: u.hashedId, username: u.username, active: u.active, registrationDate: u.registrationDate }));
}
async getUserByHashedId(requestingHashedUserId, targetHashedId) {
if (!(await this.hasUserAccess(requestingHashedUserId, 'useradministration'))) {
throw new Error('noaccess');
}
const user = await User.findOne({
where: { hashedId: targetHashedId },
attributes: ['id', 'hashedId', 'username', 'active', 'registrationDate']
});
if (!user) throw new Error('notfound');
return { id: user.hashedId, username: user.username, active: user.active, registrationDate: user.registrationDate };
}
async updateUser(requestingHashedUserId, targetHashedId, data) {
if (!(await this.hasUserAccess(requestingHashedUserId, 'useradministration'))) {
throw new Error('noaccess');
}
const user = await User.findOne({ where: { hashedId: targetHashedId } });
if (!user) throw new Error('notfound');
const updates = {};
if (typeof data.username === 'string' && data.username.trim().length > 0) {
updates.username = data.username.trim();
}
if (typeof data.active === 'boolean') {
updates.active = data.active;
}
if (Object.keys(updates).length === 0) return { id: user.hashedId, username: user.username, active: user.active };
await user.update(updates);
return { id: user.hashedId, username: user.username, active: user.active };
}
// --- User Rights Administration ---
async listUserRightTypes(requestingHashedUserId) {
if (!(await this.hasUserAccess(requestingHashedUserId, 'rights'))) {
throw new Error('noaccess');
}
const types = await UserRightType.findAll({ attributes: ['id', 'title'] });
// map to tr keys if needed; keep title as key used elsewhere
return types.map(t => ({ id: t.id, title: t.title }));
}
async listUserRightsForUser(requestingHashedUserId, targetHashedId) {
if (!(await this.hasUserAccess(requestingHashedUserId, 'rights'))) {
throw new Error('noaccess');
}
const user = await User.findOne({ where: { hashedId: targetHashedId }, attributes: ['id', 'hashedId', 'username'] });
if (!user) throw new Error('notfound');
const rights = await UserRight.findAll({
where: { userId: user.id },
include: [{ model: UserRightType, as: 'rightType' }]
});
return rights.map(r => ({ rightTypeId: r.rightTypeId, title: r.rightType?.title }));
}
async addUserRight(requestingHashedUserId, targetHashedId, rightTypeId) {
if (!(await this.hasUserAccess(requestingHashedUserId, 'rights'))) {
throw new Error('noaccess');
}
const user = await User.findOne({ where: { hashedId: targetHashedId } });
if (!user) throw new Error('notfound');
const type = await UserRightType.findByPk(rightTypeId);
if (!type) throw new Error('wrongtype');
const existing = await UserRight.findOne({ where: { userId: user.id, rightTypeId } });
if (existing) return existing; // idempotent
const created = await UserRight.create({ userId: user.id, rightTypeId });
return created;
}
async removeUserRight(requestingHashedUserId, targetHashedId, rightTypeId) {
if (!(await this.hasUserAccess(requestingHashedUserId, 'rights'))) {
throw new Error('noaccess');
}
const user = await User.findOne({ where: { hashedId: targetHashedId } });
if (!user) throw new Error('notfound');
await UserRight.destroy({ where: { userId: user.id, rightTypeId } });
return true;
}
// --- Chat Room Admin ---
async getRoomTypes(userId) {
if (!(await this.hasUserAccess(userId, 'chatrooms'))) {
throw new Error('noaccess');
}
return await RoomType.findAll();
}
async getGenderRestrictions(userId) {
if (!(await this.hasUserAccess(userId, 'chatrooms'))) {
throw new Error('noaccess');
}
// Find the UserParamType for gender restriction (e.g. description = 'gender')
const genderType = await UserParamType.findOne({ where: { description: 'gender' } });
if (!genderType) return [];
return await UserParamValue.findAll({ where: { userParamTypeId: genderType.id } });
}
async getUserRights(userId) {
if (!(await this.hasUserAccess(userId, 'chatrooms'))) {
throw new Error('noaccess');
}
return await ChatRight.findAll();
}
async getRooms(userId) {
if (!(await this.hasUserAccess(userId, 'chatrooms'))) {
throw new Error('noaccess');
}
// Only return necessary fields to the frontend
return await Room.findAll({
attributes: [
'id',
'title',
'roomTypeId',
'isPublic',
'genderRestrictionId',
'minAge',
'maxAge',
'friendsOfOwnerOnly',
'requiredUserRightId',
'password' // only if needed for editing, otherwise remove
],
include: [
{ model: RoomType, as: 'roomType' },
{
model: UserParamValue,
as: 'genderRestriction',
required: false // Wichtig: required: false, damit auch Räume ohne genderRestriction geladen werden
},
]
});
}
async updateRoom(userId, id, data) {
if (!(await this.hasUserAccess(userId, 'chatrooms'))) {
throw new Error('noaccess');
}
const room = await Room.findByPk(id);
if (!room) throw new Error('Room not found');
await room.update(data);
return room;
}
async createRoom(userId, data) {
if (!(await this.hasUserAccess(userId, 'chatrooms'))) {
throw new Error('noaccess');
}
return await Room.create(data);
}
async deleteRoom(userId, id) {
if (!(await this.hasUserAccess(userId, 'chatrooms'))) {
throw new Error('noaccess');
}
return await Room.destroy({ where: { id } });
}
// --- Match3 Admin Methods ---
async getMatch3Campaigns(userId) {
if (!(await this.hasUserAccess(userId, 'match3'))) {
throw new Error('noaccess');
}
const Match3Campaign = (await import('../models/match3/campaign.js')).default;
return await Match3Campaign.findAll({
include: [{
model: (await import('../models/match3/level.js')).default,
as: 'levels',
include: [{
model: (await import('../models/match3/objective.js')).default,
as: 'objectives',
required: false
}],
required: false
}]
});
}
async getMatch3Campaign(userId, id) {
if (!(await this.hasUserAccess(userId, 'match3'))) {
throw new Error('noaccess');
}
const Match3Campaign = (await import('../models/match3/campaign.js')).default;
return await Match3Campaign.findByPk(id, {
include: [{
model: (await import('../models/match3/level.js')).default,
as: 'levels',
include: [{
model: (await import('../models/match3/objective.js')).default,
as: 'objectives',
required: false
}],
required: false
}]
});
}
async createMatch3Campaign(userId, data) {
if (!(await this.hasUserAccess(userId, 'match3'))) {
throw new Error('noaccess');
}
const Match3Campaign = (await import('../models/match3/campaign.js')).default;
return await Match3Campaign.create(data);
}
async updateMatch3Campaign(userId, id, data) {
if (!(await this.hasUserAccess(userId, 'match3'))) {
throw new Error('noaccess');
}
const Match3Campaign = (await import('../models/match3/campaign.js')).default;
const campaign = await Match3Campaign.findByPk(id);
if (!campaign) throw new Error('Campaign not found');
await campaign.update(data);
return campaign;
}
async deleteMatch3Campaign(userId, id) {
if (!(await this.hasUserAccess(userId, 'match3'))) {
throw new Error('noaccess');
}
const Match3Campaign = (await import('../models/match3/campaign.js')).default;
return await Match3Campaign.destroy({ where: { id } });
}
async getMatch3Levels(userId) {
if (!(await this.hasUserAccess(userId, 'match3'))) {
throw new Error('noaccess');
}
const Match3Level = (await import('../models/match3/level.js')).default;
return await Match3Level.findAll({
include: [
{
model: (await import('../models/match3/campaign.js')).default,
as: 'campaign',
required: false
},
{
model: (await import('../models/match3/objective.js')).default,
as: 'objectives',
required: false
}
],
order: [['order', 'ASC']]
});
}
async getMatch3Level(userId, id) {
if (!(await this.hasUserAccess(userId, 'match3'))) {
throw new Error('noaccess');
}
const Match3Level = (await import('../models/match3/level.js')).default;
return await Match3Level.findByPk(id, {
include: [
{
model: (await import('../models/match3/campaign.js')).default,
as: 'campaign',
required: false
},
{
model: (await import('../models/match3/objective.js')).default,
as: 'objectives',
required: false
}
]
});
}
async createMatch3Level(userId, data) {
if (!(await this.hasUserAccess(userId, 'match3'))) {
throw new Error('noaccess');
}
const Match3Level = (await import('../models/match3/level.js')).default;
// Wenn keine campaignId gesetzt ist, setze eine Standard-Campaign-ID
if (!data.campaignId) {
// Versuche eine Standard-Campaign zu finden oder erstelle eine
const Match3Campaign = (await import('../models/match3/campaign.js')).default;
let defaultCampaign = await Match3Campaign.findOne({ where: { isActive: true } });
if (!defaultCampaign) {
// Erstelle eine Standard-Campaign falls keine existiert
defaultCampaign = await Match3Campaign.create({
name: 'Standard Campaign',
description: 'Standard Campaign für Match3 Levels',
isActive: true,
order: 1
});
}
data.campaignId = defaultCampaign.id;
}
// Validiere, dass campaignId gesetzt ist
if (!data.campaignId) {
throw new Error('CampaignId ist erforderlich');
}
return await Match3Level.create(data);
}
async updateMatch3Level(userId, id, data) {
if (!(await this.hasUserAccess(userId, 'match3'))) {
throw new Error('noaccess');
}
const Match3Level = (await import('../models/match3/level.js')).default;
const level = await Match3Level.findByPk(id);
if (!level) throw new Error('Level not found');
await level.update(data);
return level;
}
async deleteMatch3Level(userId, id) {
if (!(await this.hasUserAccess(userId, 'match3'))) {
throw new Error('noaccess');
}
const Match3Level = (await import('../models/match3/level.js')).default;
return await Match3Level.destroy({ where: { id } });
}
// Match3 Objectives
async getMatch3Objectives(userId) {
if (!(await this.hasUserAccess(userId, 'match3'))) {
throw new Error('noaccess');
}
const Match3Objective = (await import('../models/match3/objective.js')).default;
return await Match3Objective.findAll({
include: [{
model: (await import('../models/match3/level.js')).default,
as: 'level',
required: false
}],
order: [['order', 'ASC']]
});
}
async getMatch3Objective(userId, id) {
if (!(await this.hasUserAccess(userId, 'match3'))) {
throw new Error('noaccess');
}
const Match3Objective = (await import('../models/match3/objective.js')).default;
return await Match3Objective.findByPk(id, {
include: [{
model: (await import('../models/match3/level.js')).default,
as: 'level',
required: false
}]
});
}
async createMatch3Objective(userId, data) {
if (!(await this.hasUserAccess(userId, 'match3'))) {
throw new Error('noaccess');
}
const Match3Objective = (await import('../models/match3/objective.js')).default;
// Validiere, dass levelId gesetzt ist
if (!data.levelId) {
throw new Error('LevelId ist erforderlich');
}
// Validiere, dass target eine ganze Zahl ist
if (data.target && !Number.isInteger(Number(data.target))) {
throw new Error('Target muss eine ganze Zahl sein');
}
return await Match3Objective.create(data);
}
async updateMatch3Objective(userId, id, data) {
if (!(await this.hasUserAccess(userId, 'match3'))) {
throw new Error('noaccess');
}
const Match3Objective = (await import('../models/match3/objective.js')).default;
const objective = await Match3Objective.findByPk(id);
if (!objective) throw new Error('Objective not found');
// Validiere, dass target eine ganze Zahl ist
if (data.target && !Number.isInteger(Number(data.target))) {
throw new Error('Target muss eine ganze Zahl sein');
}
await objective.update(data);
return objective;
}
async deleteMatch3Objective(userId, id) {
if (!(await this.hasUserAccess(userId, 'match3'))) {
throw new Error('noaccess');
}
const Match3Objective = (await import('../models/match3/objective.js')).default;
return await Match3Objective.destroy({ where: { id } });
}
async getMatch3TileTypes(userId) {
if (!(await this.hasUserAccess(userId, 'match3'))) {
throw new Error('noaccess');
}
const Match3TileType = (await import('../models/match3/tileType.js')).default;
return await Match3TileType.findAll({
order: [['name', 'ASC']]
});
}
async createMatch3TileType(userId, data) {
if (!(await this.hasUserAccess(userId, 'match3'))) {
throw new Error('noaccess');
}
const Match3TileType = (await import('../models/match3/tileType.js')).default;
return await Match3TileType.create(data);
}
async updateMatch3TileType(userId, id, data) {
if (!(await this.hasUserAccess(userId, 'match3'))) {
throw new Error('noaccess');
}
const Match3TileType = (await import('../models/match3/tileType.js')).default;
const tileType = await Match3TileType.findByPk(id);
if (!tileType) throw new Error('Tile type not found');
await tileType.update(data);
return tileType;
}
async deleteMatch3TileType(userId, id) {
if (!(await this.hasUserAccess(userId, 'match3'))) {
throw new Error('noaccess');
}
const Match3TileType = (await import('../models/match3/tileType.js')).default;
return await Match3TileType.destroy({ where: { id } });
}
async getUserStatistics(userId) {
if (!(await this.hasUserAccess(userId, 'mainadmin'))) {
throw new Error('noaccess');
}
// Gesamtanzahl angemeldeter Benutzer
const totalUsers = await User.count({
where: { active: true }
});
// Geschlechterverteilung - ohne raw: true um Entschlüsselung zu ermöglichen
const genderStats = await UserParam.findAll({
include: [{
model: UserParamType,
as: 'paramType',
where: { description: 'gender' }
}]
});
const genderDistribution = {};
for (const stat of genderStats) {
const genderId = stat.value; // Dies ist die ID des Geschlechts
if (genderId) {
const genderValue = await UserParamValue.findOne({
where: { id: genderId }
});
if (genderValue) {
const gender = genderValue.value; // z.B. 'male', 'female'
genderDistribution[gender] = (genderDistribution[gender] || 0) + 1;
}
}
}
// Altersverteilung basierend auf Geburtsdatum - ohne raw: true um Entschlüsselung zu ermöglichen
const birthdateStats = await UserParam.findAll({
include: [{
model: UserParamType,
as: 'paramType',
where: { description: 'birthdate' }
}]
});
const ageGroups = {
'unter 12': 0,
'12-14': 0,
'14-16': 0,
'16-18': 0,
'18-21': 0,
'21-25': 0,
'25-30': 0,
'30-40': 0,
'40-50': 0,
'50-60': 0,
'über 60': 0
};
const now = new Date();
for (const stat of birthdateStats) {
try {
const birthdate = new Date(stat.value);
if (isNaN(birthdate.getTime())) continue;
const age = now.getFullYear() - birthdate.getFullYear();
const monthDiff = now.getMonth() - birthdate.getMonth();
let actualAge = age;
if (monthDiff < 0 || (monthDiff === 0 && now.getDate() < birthdate.getDate())) {
actualAge--;
}
if (actualAge < 12) {
ageGroups['unter 12']++;
} else if (actualAge >= 12 && actualAge < 14) {
ageGroups['12-14']++;
} else if (actualAge >= 14 && actualAge < 16) {
ageGroups['14-16']++;
} else if (actualAge >= 16 && actualAge < 18) {
ageGroups['16-18']++;
} else if (actualAge >= 18 && actualAge < 21) {
ageGroups['18-21']++;
} else if (actualAge >= 21 && actualAge < 25) {
ageGroups['21-25']++;
} else if (actualAge >= 25 && actualAge < 30) {
ageGroups['25-30']++;
} else if (actualAge >= 30 && actualAge < 40) {
ageGroups['30-40']++;
} else if (actualAge >= 40 && actualAge < 50) {
ageGroups['40-50']++;
} else if (actualAge >= 50 && actualAge < 60) {
ageGroups['50-60']++;
} else {
ageGroups['über 60']++;
}
} catch (error) {
console.error('Fehler beim Verarbeiten des Geburtsdatums:', error);
}
}
return {
totalUsers,
genderDistribution,
ageGroups
};
}
}
export default new AdminService();