Files
nick-doc/04 - Flows/Rating Flow.md
2026-05-23 20:35:34 +03:30

121 lines
6.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: Rating Flow
tags: [flow, rating, review, moderation]
related_models: ["[[Review]]", "[[ShopSettings]]", "[[PurchaseRequest]]"]
related_apis: ["POST /api/marketplace/reviews", "GET /api/marketplace/reviews/:subjectType/:subjectId"]
---
# Rating Flow
After an order is `completed`, the buyer rates the seller and (optionally) leaves a review; the seller can rate the buyer in the reciprocal flow. Reviews are scoped by `subjectType` (`seller` | `template`) and constrained by the seller's `ShopSettings`.
## Actors
- **Buyer** (typical reviewer).
- **Seller** (subject; can also be a reviewer in the reciprocal direction).
- **System** — enforces uniqueness and moderation rules.
- **Backend** — `backend/src/services/marketplace/reviewRoutes.ts`.
- **MongoDB** — `reviews` collection (`backend/src/models/Review.ts`).
## Preconditions
- The associated `PurchaseRequest` is `completed` (or `finalized`).
- The reviewer is the buyer of that request (for `isVerifiedBuyer` to be `true`).
- The subject's `ShopSettings.allowSellerReviews` / `allowTemplateReviews` is not `false` (`reviewRoutes.ts:15-31`).
## Step-by-step narrative
1. From the request detail or seller profile, the buyer clicks "Leave review". The form captures `rating` (15) and `comment` (≤ 1000 chars).
2. Frontend POSTs `POST /api/marketplace/reviews` with `{ subjectType: 'seller' | 'template', subjectId, rating, comment, purchaseRequestId? }`.
3. Backend route handler:
- Validates payload.
- Calls `isReviewsAllowed(subjectType, subjectId)` — checks the seller's `ShopSettings` (for sellers, look up the seller directly; for templates, look up the template's owning seller).
- Sets `isVerifiedBuyer = true` if the user owns a `completed` purchase request from that seller.
- Defaults `status: 'published'` (no moderation queue today).
4. Inserts a `Review` document. Unique index `{ subjectType, subjectId, reviewerId }` prevents a user from reviewing the same subject twice (`Review.ts:34`).
5. Aggregated stats are recomputed on read via `computeStats` (`reviewRoutes.ts:33-62`) — count, average, per-star histogram. No denormalised counter on `User` today; everything is computed at read-time.
## Visibility
- Public — anyone hitting `GET /api/marketplace/reviews/seller/:sellerId` sees `status: 'published'` reviews paginated by 10.
- If `ShopSettings.allowSellerReviews === false` (or `allowTemplateReviews === false`), reads return `403 Reviews are disabled by seller`.
- The seller can flag a review for moderation (planned — current statuses include `pending` and `rejected`, but no UI to flip them today; admin can update via direct DB).
## Sequence diagram
```mermaid
sequenceDiagram
autonumber
actor B as Buyer
participant FE as Frontend
participant BE as Backend
participant DB as MongoDB
B->>FE: Open seller profile / request detail
B->>FE: Click "Leave review", choose stars + comment
FE->>BE: POST /api/marketplace/reviews
BE->>DB: ShopSettings.findOne({sellerId}) → allowSellerReviews?
alt allowed
BE->>DB: PurchaseRequest.exists({buyer, seller, status:"completed"})?
BE->>DB: Review.create({status:"published", isVerifiedBuyer})
BE-->>FE: 201 { review }
else disabled
BE-->>FE: 403 Reviews disabled
end
Note over FE: Public visitor:
FE->>BE: GET /api/marketplace/reviews/seller/{id}
BE->>DB: Review.find / computeStats aggregate
BE-->>FE: { items, pagination, stats:{count, avg, histogram} }
```
## API calls
| Method | Endpoint | Purpose |
|---|---|---|
| `POST` | `/api/marketplace/reviews` | Submit review |
| `GET` | `/api/marketplace/reviews/:subjectType/:subjectId` | List reviews + stats |
| `PATCH` | `/api/marketplace/reviews/:id` | Edit own review (within edit window) |
| `DELETE` | `/api/marketplace/reviews/:id` | Delete own review |
## Database writes
- **`reviews`** — insert on submission; one document per `(subjectType, subjectId, reviewerId)`.
- **`shopsettings`** — read-only here; the seller controls `allowSellerReviews` / `allowTemplateReviews` in their shop settings.
## Socket events emitted
- None today. A `new-review` event broadcast to `user-{sellerId}` would be a useful enhancement so sellers see reviews appear live.
## Side effects
- Recompute aggregate on every list call — fine for small volumes; consider caching `stats` per subject when the review count grows.
- Order rating field also stamps `metadata.rating` on the purchase request when the marketplace endpoint accepts ratings inline (see `routes.ts` references in `backend/src/services/marketplace/routes.ts`).
## Error / edge cases
- **Duplicate review** → MongoDB `E11000` from the unique index; surface as `409 Already reviewed`.
- **Subject disabled reviews** → `403`.
- **Reviewer not a verified buyer** → review is still allowed but `isVerifiedBuyer = false`. Display this in the UI.
- **Rating out of 15** → Mongoose schema validator rejects.
- **Comment > 1000 chars** → schema-level rejection.
- **Seller toggles `allowSellerReviews=false` after reviews exist** → existing reviews remain stored but become unreadable via the public GET (`reviewRoutes.ts:81-83`).
- **Spam / abuse** → no automatic moderation; admin can flip `status` to `rejected` to hide.
> [!tip] Verified-buyer badge
> The `isVerifiedBuyer` flag is the most credible signal for prospective buyers. Always render a "Verified buyer" pill next to reviews where this is `true`.
## Linked flows
- [[Purchase Request Flow]] — precursor that makes the buyer "verified".
- [[Seller Offer Flow]] — display average rating on offer cards.
- [[Dispute Flow]] — a resolved dispute could trigger a review prompt; today they are independent.
## Source files
- Backend: `backend/src/services/marketplace/reviewRoutes.ts`
- Backend: `backend/src/models/Review.ts`
- Backend: `backend/src/services/marketplace/shopSettingsController.ts` (allow flags)
- Backend: `backend/src/services/marketplace/routes.ts` (inline rating on order completion)
- Frontend: review components under `frontend/src/sections/account/` and seller profile views