wip
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
/**
|
||||
* Infrastructure Adapter: InMemoryAchievementRepository
|
||||
*
|
||||
* In-memory implementation of IAchievementRepository
|
||||
*/
|
||||
|
||||
import {
|
||||
Achievement,
|
||||
AchievementCategory,
|
||||
DRIVER_ACHIEVEMENTS,
|
||||
STEWARD_ACHIEVEMENTS,
|
||||
ADMIN_ACHIEVEMENTS,
|
||||
COMMUNITY_ACHIEVEMENTS,
|
||||
} from '../../domain/entities/Achievement';
|
||||
import { UserAchievement } from '../../domain/entities/UserAchievement';
|
||||
import type { IAchievementRepository } from '../../domain/repositories/IAchievementRepository';
|
||||
|
||||
export class InMemoryAchievementRepository implements IAchievementRepository {
|
||||
private achievements: Map<string, Achievement> = new Map();
|
||||
private userAchievements: Map<string, UserAchievement> = new Map();
|
||||
|
||||
constructor() {
|
||||
// Seed with predefined achievements
|
||||
this.seedAchievements();
|
||||
}
|
||||
|
||||
private seedAchievements(): void {
|
||||
const allAchievements = [
|
||||
...DRIVER_ACHIEVEMENTS,
|
||||
...STEWARD_ACHIEVEMENTS,
|
||||
...ADMIN_ACHIEVEMENTS,
|
||||
...COMMUNITY_ACHIEVEMENTS,
|
||||
];
|
||||
|
||||
for (const props of allAchievements) {
|
||||
const achievement = Achievement.create(props);
|
||||
this.achievements.set(achievement.id, achievement);
|
||||
}
|
||||
}
|
||||
|
||||
// Achievement operations
|
||||
async findAchievementById(id: string): Promise<Achievement | null> {
|
||||
return this.achievements.get(id) ?? null;
|
||||
}
|
||||
|
||||
async findAllAchievements(): Promise<Achievement[]> {
|
||||
return Array.from(this.achievements.values());
|
||||
}
|
||||
|
||||
async findAchievementsByCategory(category: AchievementCategory): Promise<Achievement[]> {
|
||||
return Array.from(this.achievements.values())
|
||||
.filter(a => a.category === category);
|
||||
}
|
||||
|
||||
async createAchievement(achievement: Achievement): Promise<Achievement> {
|
||||
if (this.achievements.has(achievement.id)) {
|
||||
throw new Error('Achievement with this ID already exists');
|
||||
}
|
||||
this.achievements.set(achievement.id, achievement);
|
||||
return achievement;
|
||||
}
|
||||
|
||||
// UserAchievement operations
|
||||
async findUserAchievementById(id: string): Promise<UserAchievement | null> {
|
||||
return this.userAchievements.get(id) ?? null;
|
||||
}
|
||||
|
||||
async findUserAchievementsByUserId(userId: string): Promise<UserAchievement[]> {
|
||||
return Array.from(this.userAchievements.values())
|
||||
.filter(ua => ua.userId === userId);
|
||||
}
|
||||
|
||||
async findUserAchievementByUserAndAchievement(
|
||||
userId: string,
|
||||
achievementId: string
|
||||
): Promise<UserAchievement | null> {
|
||||
for (const ua of this.userAchievements.values()) {
|
||||
if (ua.userId === userId && ua.achievementId === achievementId) {
|
||||
return ua;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async hasUserEarnedAchievement(userId: string, achievementId: string): Promise<boolean> {
|
||||
const ua = await this.findUserAchievementByUserAndAchievement(userId, achievementId);
|
||||
return ua !== null && ua.isComplete();
|
||||
}
|
||||
|
||||
async createUserAchievement(userAchievement: UserAchievement): Promise<UserAchievement> {
|
||||
if (this.userAchievements.has(userAchievement.id)) {
|
||||
throw new Error('UserAchievement with this ID already exists');
|
||||
}
|
||||
this.userAchievements.set(userAchievement.id, userAchievement);
|
||||
return userAchievement;
|
||||
}
|
||||
|
||||
async updateUserAchievement(userAchievement: UserAchievement): Promise<UserAchievement> {
|
||||
if (!this.userAchievements.has(userAchievement.id)) {
|
||||
throw new Error('UserAchievement not found');
|
||||
}
|
||||
this.userAchievements.set(userAchievement.id, userAchievement);
|
||||
return userAchievement;
|
||||
}
|
||||
|
||||
// Stats
|
||||
async getAchievementLeaderboard(limit: number): Promise<{ userId: string; points: number; count: number }[]> {
|
||||
const userStats = new Map<string, { points: number; count: number }>();
|
||||
|
||||
for (const ua of this.userAchievements.values()) {
|
||||
if (!ua.isComplete()) continue;
|
||||
|
||||
const achievement = this.achievements.get(ua.achievementId);
|
||||
if (!achievement) continue;
|
||||
|
||||
const existing = userStats.get(ua.userId) ?? { points: 0, count: 0 };
|
||||
userStats.set(ua.userId, {
|
||||
points: existing.points + achievement.points,
|
||||
count: existing.count + 1,
|
||||
});
|
||||
}
|
||||
|
||||
return Array.from(userStats.entries())
|
||||
.map(([userId, stats]) => ({ userId, ...stats }))
|
||||
.sort((a, b) => b.points - a.points)
|
||||
.slice(0, limit);
|
||||
}
|
||||
|
||||
async getUserAchievementStats(userId: string): Promise<{
|
||||
total: number;
|
||||
points: number;
|
||||
byCategory: Record<AchievementCategory, number>
|
||||
}> {
|
||||
const userAchievements = await this.findUserAchievementsByUserId(userId);
|
||||
const completedAchievements = userAchievements.filter(ua => ua.isComplete());
|
||||
|
||||
const byCategory: Record<AchievementCategory, number> = {
|
||||
driver: 0,
|
||||
steward: 0,
|
||||
admin: 0,
|
||||
community: 0,
|
||||
};
|
||||
|
||||
let points = 0;
|
||||
|
||||
for (const ua of completedAchievements) {
|
||||
const achievement = this.achievements.get(ua.achievementId);
|
||||
if (achievement) {
|
||||
points += achievement.points;
|
||||
byCategory[achievement.category]++;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
total: completedAchievements.length,
|
||||
points,
|
||||
byCategory,
|
||||
};
|
||||
}
|
||||
|
||||
// Test helpers
|
||||
clearUserAchievements(): void {
|
||||
this.userAchievements.clear();
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.achievements.clear();
|
||||
this.userAchievements.clear();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user