Files
karibeo_api/src/modules/analytics/analytics.service.ts
2025-10-10 21:47:56 -04:00

127 lines
3.8 KiB
TypeScript
Executable File

import { Injectable, NotFoundException, ConflictException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Review } from '../../entities/review.entity';
import { CreateReviewDto } from './dto/create-review.dto';
@Injectable()
export class AnalyticsService {
constructor(
@InjectRepository(Review)
private readonly reviewRepository: Repository<Review>,
) {}
async createReview(createReviewDto: CreateReviewDto): Promise<Review> {
// Check if user already reviewed this item
const existingReview = await this.reviewRepository.findOne({
where: {
userId: createReviewDto.userId,
reviewableType: createReviewDto.reviewableType,
reviewableId: createReviewDto.reviewableId,
},
});
if (existingReview) {
throw new ConflictException('You have already reviewed this item');
}
const review = this.reviewRepository.create(createReviewDto);
return this.reviewRepository.save(review);
}
async findReviewsForItem(
reviewableType: string,
reviewableId: string,
page: number = 1,
limit: number = 10,
): Promise<{
reviews: Review[];
total: number;
averageRating: number;
ratingDistribution: Record<number, number>;
}> {
const [reviews, total] = await this.reviewRepository.findAndCount({
where: { reviewableType, reviewableId },
relations: ['user'],
skip: (page - 1) * limit,
take: limit,
order: { createdAt: 'DESC' },
});
// Calculate average rating
const averageResult = await this.reviewRepository
.createQueryBuilder('review')
.select('AVG(review.rating)', 'average')
.where('review.reviewableType = :type AND review.reviewableId = :id', {
type: reviewableType,
id: reviewableId,
})
.getRawOne();
const averageRating = parseFloat(averageResult.average) || 0;
// Calculate rating distribution
const distributionResult = await this.reviewRepository
.createQueryBuilder('review')
.select('review.rating', 'rating')
.addSelect('COUNT(*)', 'count')
.where('review.reviewableType = :type AND review.reviewableId = :id', {
type: reviewableType,
id: reviewableId,
})
.groupBy('review.rating')
.getRawMany();
const ratingDistribution: Record<number, number> = {};
for (let i = 1; i <= 5; i++) {
ratingDistribution[i] = 0;
}
distributionResult.forEach(item => {
ratingDistribution[item.rating] = parseInt(item.count);
});
return { reviews, total, averageRating, ratingDistribution };
}
async getAnalyticsOverview(): Promise<{
totalReviews: number;
averageRating: number;
reviewsByType: Array<{ type: string; count: number; avgRating: number }>;
recentReviews: Review[];
}> {
const totalReviews = await this.reviewRepository.count();
const averageResult = await this.reviewRepository
.createQueryBuilder('review')
.select('AVG(review.rating)', 'average')
.getRawOne();
const averageRating = parseFloat(averageResult.average) || 0;
const reviewsByType = await this.reviewRepository
.createQueryBuilder('review')
.select('review.reviewableType', 'type')
.addSelect('COUNT(*)', 'count')
.addSelect('AVG(review.rating)', 'avgRating')
.groupBy('review.reviewableType')
.getRawMany();
const recentReviews = await this.reviewRepository.find({
relations: ['user'],
order: { createdAt: 'DESC' },
take: 10,
});
return {
totalReviews,
averageRating,
reviewsByType: reviewsByType.map(item => ({
type: item.type,
count: parseInt(item.count),
avgRating: parseFloat(item.avgRating),
})),
recentReviews,
};
}
}