Initial commit
This commit is contained in:
126
src/modules/analytics/analytics.service.ts
Executable file
126
src/modules/analytics/analytics.service.ts
Executable file
@@ -0,0 +1,126 @@
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user