product-review-admin.resolver.ts 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
  2. import {
  3. Allow,
  4. Ctx,
  5. EntityNotFoundError,
  6. ListQueryBuilder,
  7. Permission,
  8. Product,
  9. RequestContext,
  10. Transaction,
  11. TransactionalConnection,
  12. TranslatableSaver,
  13. translateDeep,
  14. } from '@vendure/core';
  15. import { ProductReviewTranslation } from '../entities/product-review-translation.entity';
  16. import { ProductReview } from '../entities/product-review.entity';
  17. import {
  18. MutationApproveProductReviewArgs,
  19. MutationRejectProductReviewArgs,
  20. MutationUpdateProductReviewArgs,
  21. QueryProductReviewArgs,
  22. QueryProductReviewsArgs,
  23. } from '../generated-admin-types';
  24. @Resolver()
  25. export class ProductReviewAdminResolver {
  26. constructor(
  27. private connection: TransactionalConnection,
  28. private listQueryBuilder: ListQueryBuilder,
  29. private translatableSaver: TranslatableSaver,
  30. ) {}
  31. @Query()
  32. @Allow(Permission.ReadCatalog)
  33. async productReviews(@Ctx() ctx: RequestContext, @Args() args: QueryProductReviewsArgs) {
  34. return this.listQueryBuilder
  35. .build(ProductReview, args.options || undefined, {
  36. relations: ['product'],
  37. ctx,
  38. })
  39. .getManyAndCount()
  40. .then(([items, totalItems]) => ({
  41. items,
  42. totalItems,
  43. }));
  44. }
  45. @Query()
  46. @Allow(Permission.ReadCatalog)
  47. async productReview(@Ctx() ctx: RequestContext, @Args() args: QueryProductReviewArgs) {
  48. const review = await this.connection.getRepository(ctx, ProductReview).findOne({
  49. where: { id: args.id },
  50. relations: {
  51. author: true,
  52. product: true,
  53. productVariant: true,
  54. },
  55. });
  56. if (!review) {
  57. throw new EntityNotFoundError(ProductReview.name, args.id);
  58. }
  59. return translateDeep(review, ctx.languageCode);
  60. }
  61. @Transaction()
  62. @Mutation()
  63. @Allow(Permission.UpdateCatalog)
  64. async updateProductReview(
  65. @Ctx() ctx: RequestContext,
  66. @Args() { input }: MutationUpdateProductReviewArgs,
  67. ) {
  68. const review = await this.connection.getEntityOrThrow(ctx, ProductReview, input.id);
  69. const originalResponse = review.response;
  70. return this.translatableSaver.update({
  71. ctx,
  72. input,
  73. entityType: ProductReview,
  74. translationType: ProductReviewTranslation,
  75. });
  76. }
  77. @Transaction()
  78. @Mutation()
  79. @Allow(Permission.UpdateCatalog)
  80. async approveProductReview(@Ctx() ctx: RequestContext, @Args() { id }: MutationApproveProductReviewArgs) {
  81. const review = await this.connection.getEntityOrThrow(ctx, ProductReview, id, {
  82. relations: ['product'],
  83. });
  84. if (review.state !== 'new') {
  85. return review;
  86. }
  87. const { product } = review;
  88. const newRating = this.calculateNewReviewAverage(review.rating, product);
  89. product.customFields.reviewCount++;
  90. product.customFields.reviewRating = newRating;
  91. await this.connection.getRepository(ctx, Product).save(product);
  92. review.state = 'approved';
  93. return this.connection.getRepository(ctx, ProductReview).save(review);
  94. }
  95. @Transaction()
  96. @Mutation()
  97. @Allow(Permission.UpdateCatalog)
  98. async rejectProductReview(@Ctx() ctx: RequestContext, @Args() { id }: MutationRejectProductReviewArgs) {
  99. const review = await this.connection.getEntityOrThrow(ctx, ProductReview, id);
  100. if (review.state !== 'new') {
  101. return review;
  102. }
  103. review.state = 'rejected';
  104. return this.connection.getRepository(ctx, ProductReview).save(review);
  105. }
  106. private calculateNewReviewAverage(rating: number, product: Product): number {
  107. const count = product.customFields.reviewCount;
  108. const currentRating = product.customFields.reviewRating || 0;
  109. const newRating = (currentRating * count + rating) / (count + 1);
  110. return newRating;
  111. }
  112. }