100 lines
2.7 KiB
TypeScript
100 lines
2.7 KiB
TypeScript
import { AdminVoteSessionRepository } from '../../domain/repositories/AdminVoteSessionRepository';
|
|
import { CastAdminVoteInput, CastAdminVoteOutput } from '../dtos/AdminVoteSessionDto';
|
|
|
|
/**
|
|
* Use Case: CastAdminVoteUseCase
|
|
*
|
|
* Casts a vote in an active admin vote session.
|
|
* Follows CQRS Light: command side operation.
|
|
*
|
|
* Per plans section 7.1.1
|
|
*/
|
|
export class CastAdminVoteUseCase {
|
|
constructor(
|
|
private readonly adminVoteSessionRepository: AdminVoteSessionRepository,
|
|
) {}
|
|
|
|
async execute(input: CastAdminVoteInput): Promise<CastAdminVoteOutput> {
|
|
try {
|
|
// Validate input
|
|
const errors = this.validateInput(input);
|
|
if (errors.length > 0) {
|
|
return {
|
|
success: false,
|
|
voteSessionId: input.voteSessionId,
|
|
voterId: input.voterId,
|
|
errors,
|
|
};
|
|
}
|
|
|
|
// Load the vote session
|
|
const session = await this.adminVoteSessionRepository.findById(input.voteSessionId);
|
|
if (!session) {
|
|
return {
|
|
success: false,
|
|
voteSessionId: input.voteSessionId,
|
|
voterId: input.voterId,
|
|
errors: ['Vote session not found'],
|
|
};
|
|
}
|
|
|
|
// Check if session is open
|
|
const voteTime = input.votedAt ? new Date(input.votedAt) : new Date();
|
|
if (!session.isVotingWindowOpen(voteTime)) {
|
|
return {
|
|
success: false,
|
|
voteSessionId: input.voteSessionId,
|
|
voterId: input.voterId,
|
|
errors: ['Vote session is not open for voting'],
|
|
};
|
|
}
|
|
|
|
// Cast the vote
|
|
session.castVote(input.voterId, input.positive, voteTime);
|
|
|
|
// Save updated session
|
|
await this.adminVoteSessionRepository.save(session);
|
|
|
|
return {
|
|
success: true,
|
|
voteSessionId: input.voteSessionId,
|
|
voterId: input.voterId,
|
|
};
|
|
|
|
} catch (error) {
|
|
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
|
|
return {
|
|
success: false,
|
|
voteSessionId: input.voteSessionId,
|
|
voterId: input.voterId,
|
|
errors: [`Failed to cast vote: ${errorMsg}`],
|
|
};
|
|
}
|
|
}
|
|
|
|
private validateInput(input: CastAdminVoteInput): string[] {
|
|
const errors: string[] = [];
|
|
|
|
if (!input.voteSessionId || input.voteSessionId.trim().length === 0) {
|
|
errors.push('voteSessionId is required');
|
|
}
|
|
|
|
if (!input.voterId || input.voterId.trim().length === 0) {
|
|
errors.push('voterId is required');
|
|
}
|
|
|
|
if (typeof input.positive !== 'boolean') {
|
|
errors.push('positive must be a boolean value');
|
|
}
|
|
|
|
if (input.votedAt) {
|
|
const votedAt = new Date(input.votedAt);
|
|
if (isNaN(votedAt.getTime())) {
|
|
errors.push('votedAt must be a valid date if provided');
|
|
}
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
}
|