310 lines
12 KiB
TypeScript
310 lines
12 KiB
TypeScript
import { Injectable, Inject } from '@nestjs/common';
|
|
import { CreateTeamInputDTO } from './dtos/CreateTeamInputDTO';
|
|
import { UpdateTeamInputDTO } from './dtos/UpdateTeamInputDTO';
|
|
import { GetAllTeamsOutputDTO } from './dtos/GetAllTeamsOutputDTO';
|
|
import { GetTeamDetailsOutputDTO } from './dtos/GetTeamDetailsOutputDTO';
|
|
import { GetTeamMembersOutputDTO } from './dtos/GetTeamMembersOutputDTO';
|
|
import { GetTeamJoinRequestsOutputDTO } from './dtos/GetTeamJoinRequestsOutputDTO';
|
|
import { CreateTeamOutputDTO } from './dtos/CreateTeamOutputDTO';
|
|
import { UpdateTeamOutputDTO } from './dtos/UpdateTeamOutputDTO';
|
|
import { GetDriverTeamOutputDTO } from './dtos/GetDriverTeamOutputDTO';
|
|
import { GetTeamMembershipOutputDTO } from './dtos/GetTeamMembershipOutputDTO';
|
|
|
|
// Core imports
|
|
import type { Logger } from '@core/shared/application/Logger';
|
|
import type { ITeamRepository } from '@core/racing/domain/repositories/ITeamRepository';
|
|
import type { ITeamMembershipRepository } from '@core/racing/domain/repositories/ITeamMembershipRepository';
|
|
import type { IDriverRepository } from '@core/racing/domain/repositories/IDriverRepository';
|
|
|
|
// Use cases
|
|
import { GetAllTeamsUseCase } from '@core/racing/application/use-cases/GetAllTeamsUseCase';
|
|
import { GetTeamDetailsUseCase } from '@core/racing/application/use-cases/GetTeamDetailsUseCase';
|
|
import { GetTeamMembersUseCase } from '@core/racing/application/use-cases/GetTeamMembersUseCase';
|
|
import { GetTeamJoinRequestsUseCase } from '@core/racing/application/use-cases/GetTeamJoinRequestsUseCase';
|
|
import { CreateTeamUseCase, CreateTeamInput } from '@core/racing/application/use-cases/CreateTeamUseCase';
|
|
import { UpdateTeamUseCase, UpdateTeamInput } from '@core/racing/application/use-cases/UpdateTeamUseCase';
|
|
import { GetDriverTeamUseCase } from '@core/racing/application/use-cases/GetDriverTeamUseCase';
|
|
import { GetTeamMembershipUseCase } from '@core/racing/application/use-cases/GetTeamMembershipUseCase';
|
|
|
|
// Tokens
|
|
import { TEAM_REPOSITORY_TOKEN, TEAM_MEMBERSHIP_REPOSITORY_TOKEN, DRIVER_REPOSITORY_TOKEN, LOGGER_TOKEN, TEAM_STATS_REPOSITORY_TOKEN } from './TeamTokens';
|
|
import type { ITeamStatsRepository } from '@core/racing/domain/repositories/ITeamStatsRepository';
|
|
|
|
@Injectable()
|
|
export class TeamService {
|
|
constructor(
|
|
@Inject(TEAM_REPOSITORY_TOKEN) private readonly teamRepository: ITeamRepository,
|
|
@Inject(TEAM_MEMBERSHIP_REPOSITORY_TOKEN) private readonly membershipRepository: ITeamMembershipRepository,
|
|
@Inject(DRIVER_REPOSITORY_TOKEN) private readonly driverRepository: IDriverRepository,
|
|
@Inject(LOGGER_TOKEN) private readonly logger: Logger,
|
|
@Inject(TEAM_STATS_REPOSITORY_TOKEN) private readonly teamStatsRepository: ITeamStatsRepository,
|
|
) {}
|
|
|
|
async getAll(): Promise<GetAllTeamsOutputDTO> {
|
|
this.logger.debug('[TeamService] Fetching all teams.');
|
|
|
|
const useCase = new GetAllTeamsUseCase(
|
|
this.teamRepository,
|
|
this.membershipRepository,
|
|
this.teamStatsRepository,
|
|
this.logger
|
|
);
|
|
const result = await useCase.execute({});
|
|
if (result.isErr()) {
|
|
this.logger.error('Error fetching all teams', new Error(result.error?.details?.message || 'Unknown error'));
|
|
return { teams: [], totalCount: 0 };
|
|
}
|
|
|
|
const value = result.value;
|
|
if (!value) {
|
|
return { teams: [], totalCount: 0 };
|
|
}
|
|
|
|
return {
|
|
teams: value.teams.map(t => ({
|
|
id: t.team.id,
|
|
name: t.team.name.toString(),
|
|
tag: t.team.tag.toString(),
|
|
description: t.description,
|
|
memberCount: t.memberCount,
|
|
leagues: t.leagues,
|
|
totalWins: t.totalWins,
|
|
totalRaces: t.totalRaces,
|
|
performanceLevel: t.performanceLevel,
|
|
specialization: t.specialization,
|
|
region: t.region,
|
|
languages: t.languages,
|
|
rating: t.rating,
|
|
logoUrl: t.logoUrl,
|
|
isRecruiting: t.isRecruiting,
|
|
})),
|
|
totalCount: value.totalCount,
|
|
};
|
|
}
|
|
|
|
async getDetails(teamId: string, userId?: string): Promise<GetTeamDetailsOutputDTO | null> {
|
|
this.logger.debug(`[TeamService] Fetching team details for teamId: ${teamId}, userId: ${userId}`);
|
|
|
|
const useCase = new GetTeamDetailsUseCase(this.teamRepository, this.membershipRepository);
|
|
const result = await useCase.execute({ teamId, driverId: userId || '' });
|
|
if (result.isErr()) {
|
|
this.logger.error(`Error fetching team details for teamId: ${teamId}: ${result.error?.details?.message || 'Unknown error'}`);
|
|
return null;
|
|
}
|
|
|
|
const value = result.value;
|
|
if (!value) {
|
|
return null;
|
|
}
|
|
|
|
// Convert to DTO
|
|
return {
|
|
team: {
|
|
id: value.team.id,
|
|
name: value.team.name.toString(),
|
|
tag: value.team.tag.toString(),
|
|
description: value.team.description.toString(),
|
|
ownerId: value.team.ownerId.toString(),
|
|
leagues: value.team.leagues.map(l => l.toString()),
|
|
isRecruiting: value.team.isRecruiting,
|
|
createdAt: value.team.createdAt?.toDate()?.toISOString?.() || new Date().toISOString(),
|
|
category: undefined,
|
|
},
|
|
membership: value.membership ? {
|
|
role: value.membership.role === 'driver' ? 'member' : (value.membership.role as 'owner' | 'manager' | 'member'),
|
|
joinedAt: value.membership.joinedAt.toISOString(),
|
|
isActive: value.membership.status === 'active',
|
|
} : null,
|
|
canManage: value.canManage,
|
|
};
|
|
}
|
|
|
|
async getMembers(teamId: string): Promise<GetTeamMembersOutputDTO> {
|
|
this.logger.debug(`[TeamService] Fetching team members for teamId: ${teamId}`);
|
|
|
|
const useCase = new GetTeamMembersUseCase(this.membershipRepository, this.driverRepository, this.teamRepository, this.logger);
|
|
const result = await useCase.execute({ teamId });
|
|
if (result.isErr()) {
|
|
this.logger.error(`Error fetching team members for teamId: ${teamId}: ${result.error?.details?.message || 'Unknown error'}`);
|
|
return {
|
|
members: [],
|
|
totalCount: 0,
|
|
ownerCount: 0,
|
|
managerCount: 0,
|
|
memberCount: 0,
|
|
};
|
|
}
|
|
|
|
const value = result.value;
|
|
if (!value) {
|
|
return {
|
|
members: [],
|
|
totalCount: 0,
|
|
ownerCount: 0,
|
|
managerCount: 0,
|
|
memberCount: 0,
|
|
};
|
|
}
|
|
|
|
return {
|
|
members: value.members.map(m => ({
|
|
driverId: m.driver?.id || '',
|
|
driverName: m.driver?.name?.toString() || '',
|
|
role: m.membership.role === 'driver' ? 'member' : (m.membership.role as 'owner' | 'manager' | 'member'),
|
|
joinedAt: m.membership.joinedAt.toISOString(),
|
|
isActive: m.membership.status === 'active',
|
|
avatarUrl: '', // Would need MediaResolver here
|
|
})),
|
|
totalCount: value.members.length,
|
|
ownerCount: value.members.filter(m => m.membership.role === 'owner').length,
|
|
managerCount: value.members.filter(m => m.membership.role === 'manager').length,
|
|
memberCount: value.members.filter(m => m.membership.role === 'driver').length,
|
|
};
|
|
}
|
|
|
|
async getJoinRequests(teamId: string): Promise<GetTeamJoinRequestsOutputDTO> {
|
|
this.logger.debug(`[TeamService] Fetching team join requests for teamId: ${teamId}`);
|
|
|
|
const useCase = new GetTeamJoinRequestsUseCase(this.membershipRepository, this.driverRepository, this.teamRepository);
|
|
const result = await useCase.execute({ teamId });
|
|
if (result.isErr()) {
|
|
this.logger.error(`Error fetching team join requests for teamId: ${teamId}`, new Error(result.error?.details?.message || 'Unknown error'));
|
|
return {
|
|
requests: [],
|
|
pendingCount: 0,
|
|
totalCount: 0,
|
|
};
|
|
}
|
|
|
|
const value = result.value;
|
|
if (!value) {
|
|
return {
|
|
requests: [],
|
|
pendingCount: 0,
|
|
totalCount: 0,
|
|
};
|
|
}
|
|
|
|
return {
|
|
requests: value.joinRequests.map(r => ({
|
|
requestId: r.id,
|
|
driverId: r.driverId,
|
|
driverName: r.driver.name.toString(),
|
|
teamId: r.teamId,
|
|
status: 'pending',
|
|
requestedAt: r.requestedAt.toISOString(),
|
|
avatarUrl: '', // Would need MediaResolver here
|
|
})),
|
|
pendingCount: value.joinRequests.length,
|
|
totalCount: value.joinRequests.length,
|
|
};
|
|
}
|
|
|
|
async create(input: CreateTeamInputDTO, userId?: string): Promise<CreateTeamOutputDTO> {
|
|
this.logger.debug('[TeamService] Creating team', { input, userId });
|
|
|
|
const command: CreateTeamInput = {
|
|
name: input.name,
|
|
tag: input.tag,
|
|
description: input.description ?? '',
|
|
ownerId: userId || '',
|
|
leagues: [],
|
|
};
|
|
|
|
const useCase = new CreateTeamUseCase(this.teamRepository, this.membershipRepository, this.logger);
|
|
const result = await useCase.execute(command);
|
|
if (result.isErr()) {
|
|
this.logger.error(`Error creating team: ${result.error?.details?.message || 'Unknown error'}`);
|
|
return { id: '', success: false };
|
|
}
|
|
|
|
const value = result.value;
|
|
if (!value) {
|
|
return { id: '', success: false };
|
|
}
|
|
|
|
return { id: value.team.id, success: true };
|
|
}
|
|
|
|
async update(teamId: string, input: UpdateTeamInputDTO, userId?: string): Promise<UpdateTeamOutputDTO> {
|
|
this.logger.debug(`[TeamService] Updating team ${teamId}`, { input, userId });
|
|
|
|
const command: UpdateTeamInput = {
|
|
teamId,
|
|
updates: {
|
|
...(input.name !== undefined && { name: input.name }),
|
|
...(input.tag !== undefined && { tag: input.tag }),
|
|
...(input.description !== undefined && { description: input.description }),
|
|
},
|
|
updatedBy: userId || '',
|
|
};
|
|
|
|
const useCase = new UpdateTeamUseCase(this.teamRepository, this.membershipRepository);
|
|
const result = await useCase.execute(command);
|
|
if (result.isErr()) {
|
|
this.logger.error(`Error updating team ${teamId}: ${result.error?.details?.message || 'Unknown error'}`);
|
|
return { success: false };
|
|
}
|
|
|
|
return { success: true };
|
|
}
|
|
|
|
async getDriverTeam(driverId: string): Promise<GetDriverTeamOutputDTO | null> {
|
|
this.logger.debug(`[TeamService] Fetching team for driverId: ${driverId}`);
|
|
|
|
const useCase = new GetDriverTeamUseCase(this.teamRepository, this.membershipRepository, this.logger);
|
|
const result = await useCase.execute({ driverId });
|
|
if (result.isErr()) {
|
|
this.logger.error(`Error fetching team for driverId: ${driverId}: ${result.error?.details?.message || 'Unknown error'}`);
|
|
return null;
|
|
}
|
|
|
|
const value = result.value;
|
|
if (!value || !value.team) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
team: {
|
|
id: value.team.id,
|
|
name: value.team.name.toString(),
|
|
tag: value.team.tag.toString(),
|
|
description: value.team.description.toString(),
|
|
ownerId: value.team.ownerId.toString(),
|
|
leagues: value.team.leagues.map(l => l.toString()),
|
|
isRecruiting: value.team.isRecruiting,
|
|
createdAt: value.team.createdAt?.toDate?.()?.toISOString?.() || new Date().toISOString(),
|
|
category: undefined,
|
|
},
|
|
membership: {
|
|
role: value.membership.role === 'driver' ? 'member' : (value.membership.role as 'owner' | 'manager' | 'member'),
|
|
joinedAt: value.membership.joinedAt.toISOString(),
|
|
isActive: value.membership.status === 'active',
|
|
},
|
|
isOwner: value.membership.role === 'owner',
|
|
canManage: value.membership.role === 'owner' || value.membership.role === 'manager',
|
|
};
|
|
}
|
|
|
|
async getMembership(teamId: string, driverId: string): Promise<GetTeamMembershipOutputDTO | null> {
|
|
this.logger.debug(`[TeamService] Fetching team membership for teamId: ${teamId}, driverId: ${driverId}`);
|
|
|
|
const useCase = new GetTeamMembershipUseCase(this.membershipRepository, this.logger);
|
|
const result = await useCase.execute({ teamId, driverId });
|
|
if (result.isErr()) {
|
|
this.logger.error(`Error fetching team membership for teamId: ${teamId}, driverId: ${driverId}: ${result.error?.details?.message || 'Unknown error'}`);
|
|
return null;
|
|
}
|
|
|
|
const value = result.value;
|
|
if (!value) {
|
|
return null;
|
|
}
|
|
|
|
return value.membership ? {
|
|
role: value.membership.role,
|
|
joinedAt: value.membership.joinedAt,
|
|
isActive: value.membership.isActive,
|
|
} : null;
|
|
}
|
|
} |