This commit is contained in:
@@ -9,11 +9,6 @@ export class CreateReviewDto {
|
||||
@ApiProperty({ description: 'Food ID' })
|
||||
foodId: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
@ApiProperty({ description: 'Order ID' })
|
||||
orderId: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
|
||||
@@ -7,13 +7,12 @@ import { PositivePoint, NegativePoint } from '../enums/review-point.enum';
|
||||
import { ReviewStatus } from '../enums/review-status.enum';
|
||||
|
||||
@Entity({ tableName: 'reviews' })
|
||||
@Unique({ properties: ['order', 'food', 'user'] })
|
||||
@Unique({ properties: ['food', 'user'] })
|
||||
@Index({ properties: ['food', 'status'] })
|
||||
@Index({ properties: ['user'] })
|
||||
@Index({ properties: ['order'] })
|
||||
export class Review extends BaseEntity {
|
||||
@ManyToOne(() => Order)
|
||||
order: Order;
|
||||
@Property({ type: 'boolean' })
|
||||
isBuyer: boolean = false
|
||||
|
||||
@ManyToOne(() => Food)
|
||||
food: Food;
|
||||
|
||||
@@ -5,8 +5,6 @@ export class ReviewCreatedEvent {
|
||||
public readonly userId: string,
|
||||
public readonly foodId: string,
|
||||
public readonly foodName: string,
|
||||
public readonly orderId: string,
|
||||
public readonly orderNumber: string | number,
|
||||
public readonly rating: number,
|
||||
) {}
|
||||
}
|
||||
|
||||
@@ -46,13 +46,12 @@ export class ReviewListeners {
|
||||
restaurantId: event.restaurantId,
|
||||
message: {
|
||||
title: NotifTitleEnum.REVIEW_CREATED,
|
||||
content: `نظر جدید برای غذا "${event.foodName}" با امتیاز ${event.rating} از 5 برای سفارش شماره ${event.orderNumber} ثبت شد`,
|
||||
content: `نظر جدید برای غذا "${event.foodName}" با امتیاز ${event.rating} از 5 برای سفارش شماره ثبت شد`,
|
||||
sms: {
|
||||
templateId: this.reviewCreatedSmsTemplateId,
|
||||
parameters: {
|
||||
foodName: event.foodName,
|
||||
rating: event.rating.toString(),
|
||||
orderNumber: String(event.orderNumber),
|
||||
},
|
||||
},
|
||||
pushNotif: {
|
||||
|
||||
@@ -10,12 +10,12 @@ import { RequiredEntityData } from '@mikro-orm/core';
|
||||
import { Review } from '../entities/review.entity';
|
||||
import { FoodMessage, UserMessage, ReviewMessage } from 'src/common/enums/message.enum';
|
||||
import { Order } from '../../orders/entities/order.entity';
|
||||
import { OrderItem } from '../../orders/entities/order-item.entity';
|
||||
import { Restaurant } from 'src/modules/restaurants/entities/restaurant.entity';
|
||||
import { ReviewStatus } from '../enums/review-status.enum';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { ReviewCreatedEvent } from '../events/review.events';
|
||||
import { sanitizeText } from '../../utils/sanitize.util';
|
||||
import { OrderStatus } from 'src/modules/orders/interface/order.interface';
|
||||
|
||||
@Injectable()
|
||||
export class ReviewService {
|
||||
@@ -28,9 +28,9 @@ export class ReviewService {
|
||||
) { }
|
||||
|
||||
async create(userId: string, createReviewDto: CreateReviewDto): Promise<Review> {
|
||||
const { foodId, orderId, rating, comment, positivePoints, negativePoints } = createReviewDto;
|
||||
const { foodId, rating, comment, positivePoints, negativePoints } = createReviewDto;
|
||||
|
||||
const food = await this.foodRepository.findOne({ id: foodId });
|
||||
const food = await this.foodRepository.findOne({ id: foodId }, { populate: ['restaurant'] });
|
||||
if (!food) {
|
||||
throw new NotFoundException(FoodMessage.NOT_FOUND);
|
||||
}
|
||||
@@ -40,34 +40,14 @@ export class ReviewService {
|
||||
throw new NotFoundException(UserMessage.USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
// Find order and verify it belongs to the user, populate restaurant to get restaurantId
|
||||
const order = await this.em.findOne(Order, { id: orderId, user: { id: userId } }, { populate: ['restaurant'] });
|
||||
if (!order) {
|
||||
throw new NotFoundException('Order not found or does not belong to the current user');
|
||||
}
|
||||
|
||||
// Verify the food is in the order
|
||||
const orderItem = await this.em.findOne(OrderItem, {
|
||||
order: { id: orderId },
|
||||
food: { id: foodId },
|
||||
});
|
||||
if (!orderItem) {
|
||||
throw new BadRequestException('Food is not in the specified order');
|
||||
}
|
||||
|
||||
// Check if user already commented on this food for this order
|
||||
const existingReview = await this.reviewRepository.findOne({
|
||||
order: { id: orderId },
|
||||
food: { id: foodId },
|
||||
user: { id: userId },
|
||||
});
|
||||
|
||||
if (existingReview) {
|
||||
throw new BadRequestException(ReviewMessage.ALREADY_COMMENTED);
|
||||
}
|
||||
const isBuyer = await this.em.count(Order, {
|
||||
status: OrderStatus.COMPLETED,
|
||||
items: { food: { id: foodId } },
|
||||
user: { id: userId }
|
||||
}) > 0;
|
||||
|
||||
const data: RequiredEntityData<Review> = {
|
||||
order,
|
||||
isBuyer: true,
|
||||
food,
|
||||
user,
|
||||
rating,
|
||||
@@ -92,12 +72,10 @@ export class ReviewService {
|
||||
ReviewCreatedEvent.name,
|
||||
new ReviewCreatedEvent(
|
||||
review.id,
|
||||
order.restaurant.id,
|
||||
food.restaurant.id,
|
||||
userId,
|
||||
foodId,
|
||||
food.title || 'Unknown Food',
|
||||
orderId,
|
||||
order.orderNumber || '',
|
||||
food.title || '',
|
||||
rating,
|
||||
),
|
||||
);
|
||||
@@ -120,8 +98,8 @@ export class ReviewService {
|
||||
|
||||
async findById(id: string, restaurantId: string): Promise<Review> {
|
||||
const review = await this.reviewRepository.findOne(
|
||||
{ id, order: { restaurant: { id: restaurantId } } },
|
||||
{ populate: ['food', 'user', 'order'] },
|
||||
{ id, food: { restaurant: { id: restaurantId } } },
|
||||
{ populate: ['food', 'user'] },
|
||||
);
|
||||
if (!review) {
|
||||
throw new NotFoundException(ReviewMessage.NOT_FOUND);
|
||||
@@ -130,7 +108,7 @@ export class ReviewService {
|
||||
}
|
||||
|
||||
async update(id: string, userId: string, dto: UpdateReviewDto, isAdmin: boolean = false): Promise<Review> {
|
||||
const review = await this.reviewRepository.findOne({ id }, { populate: ['food', 'user', 'order'] });
|
||||
const review = await this.reviewRepository.findOne({ id }, { populate: ['food', 'user',] });
|
||||
if (!review) {
|
||||
throw new NotFoundException(ReviewMessage.NOT_FOUND);
|
||||
}
|
||||
@@ -165,7 +143,7 @@ export class ReviewService {
|
||||
}
|
||||
|
||||
async changeStatus(reviewId: string, status: ReviewStatus, restaurantId: string): Promise<Review> {
|
||||
const review = await this.reviewRepository.findOne({ id: reviewId, order: { restaurant: { id: restaurantId } } });
|
||||
const review = await this.reviewRepository.findOne({ id: reviewId, food: { restaurant: { id: restaurantId } } });
|
||||
if (!review) {
|
||||
throw new NotFoundException(ReviewMessage.NOT_FOUND);
|
||||
}
|
||||
@@ -175,7 +153,7 @@ export class ReviewService {
|
||||
}
|
||||
|
||||
async remove(id: string, userId: string, isAdmin: boolean = false) {
|
||||
const review = await this.reviewRepository.findOne({ id }, { populate: ['food', 'user', 'order'] });
|
||||
const review = await this.reviewRepository.findOne({ id }, { populate: ['food', 'user'] });
|
||||
if (!review) {
|
||||
throw new NotFoundException(ReviewMessage.NOT_FOUND);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user