backend phase 14 & frontend phase 7
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
# Handoff — after backend-phase-14 (Reviews, ratings & patient care records)
|
||||
|
||||
**The trust loop and the continuity-of-care loop are live.** A customer leaves **one moderated review per
|
||||
completed booking**; an admin/moderator publishes/hides/rejects it; the nurse's public rating is recomputed
|
||||
**from source on every transition** (so hiding a 1-star lowers the count and re-derives the average — no
|
||||
inflated-after-hide drift); low ratings auto-raise an internal `support_alert`; and nurses author **encrypted,
|
||||
patient-scoped** clinical notes readable only under a strict clinical-access rule.
|
||||
|
||||
## What the frontend (f13-b14) can now build
|
||||
- **Leave a review** — `POST bookings/{bookingId}/review` `{ rating 1–5, body?, tagCodes? }` (customer who owns a
|
||||
completed booking). Returns `{ id, moderationStatus: "pending_moderation", lowRatingAlertRaised }`. The review is
|
||||
**not public** until an admin publishes it — build the "submitted, awaiting moderation" state.
|
||||
- **Public nurse reviews** — `GET nurses/{nurseProfileId}/reviews?page=&pageSize=` → `{ aggregate: { averageRating,
|
||||
publishedCount }, reviews: PagedResult<{ id, rating, body, tagCodes[], createdAt }> }`. **Published only.**
|
||||
- **Public tag rollup** — `GET nurses/{nurseProfileId}/review_tags` → `{ publishedReviewCount, tags: [{ code,
|
||||
labelFa, labelEn, count, percentage }] }` ("% punctual"). The seeded vocab (`punctual/professional/clean/kind/
|
||||
communicative`) is always returned.
|
||||
- **Tag your own review** — `POST reviews/{reviewId}/tags` `{ tagCodes }` (author or moderator; replaces the set).
|
||||
- **Admin moderation console** — `GET admin/reviews/moderation_queue?status=&page=&pageSize=` (default
|
||||
`pending_moderation`; each row carries the linked `lowRatingAlertId` for triage) and
|
||||
`PATCH reviews/{reviewId}/status` `{ action: publish|hide|reject|unpublish, reason? }` (hide/reject need a reason).
|
||||
The PATCH returns the recomputed `{ averageRating, totalReviews }`.
|
||||
- **Patient care records** — `POST patients/{patientId}/care_records` `{ bookingId?, body }` (nurse with a
|
||||
confirmed booking) and `GET patients/{patientId}/care_records?page=&pageSize=` (owning customer / nurse with a
|
||||
confirmed booking / admin) → decrypted `{ id, patientId, bookingId, nurseProfileId, nurseName, body, recordedAt }`
|
||||
newest first. The history is **patient-scoped** — a new nurse taking over reads the whole history.
|
||||
|
||||
## Contracts
|
||||
- **`dev/contracts/domains/reviews-records.md`** — all 8 endpoints, the `moderationStatus`/action enums, tag
|
||||
codes, DTO shapes, and the care-record **access matrix**.
|
||||
- **`dev/contracts/openapi/swagger.v1.json`** refreshed — the 8 review/care-record paths are in the snapshot.
|
||||
|
||||
## What is mocked (and how it becomes real)
|
||||
- **`IReviewModerationService`** (new) — AI review pre-screen. `MockReviewModerationService` is a keyword filter:
|
||||
clean text → a human-review **Flag** by default (so the publish gate holds — reviews land `pending_moderation`);
|
||||
a banned-word substring → **Reject** (auto-hidden). Config `Seams:ReviewModeration:{AutoApproveClean,BannedWords}`.
|
||||
Make it real → a text classifier / LLM endpoint (see reports/mocks-registry.md). `ModerateReviewCommand` keeps
|
||||
decision authority + the human override, so the real impl never touches the handler.
|
||||
|
||||
## Load-bearing rules (don't regress)
|
||||
- **Recompute from source, not delta** — every publish/hide/reject/unpublish re-derives `average_rating`/
|
||||
`total_reviews` over currently-published reviews (exclude-the-changed-review then fold in its new status), in the
|
||||
same transaction, then refreshes the search index.
|
||||
- **Publish gate** — `pending_moderation`/`hidden`/`rejected` are never in a public read and never counted.
|
||||
- **1:1 per completed booking** — `UNIQUE(booking_id)` + handler pre-check; cross-tenant is a 404.
|
||||
- **Low rating (≤ config `min_rating_for_support_alert`, default 2)** raises an internal `low_rating` alert —
|
||||
internal-only, never in a user response (only its id shows on the admin queue).
|
||||
- **Care records are patient-scoped, encrypted at rest, strict access** — a nurse without a confirmed booking for
|
||||
the patient is denied read + write.
|
||||
|
||||
## Deferred (flagged, not built)
|
||||
- Two-way (nurse-reviews-customer) double-blind reviews with timed reveal.
|
||||
- First-class `incidents` entity + ML fraud scoring (manual suspension + `support_alerts` cover it now).
|
||||
- The ticket system, partner centers, and the admin **support-alert worklist console** → **b15** (this phase only
|
||||
*raises* alerts).
|
||||
- `SuspendNurse` / `ResolveSupportAlert` / `FlagConcern` admin actions → b15.
|
||||
Reference in New Issue
Block a user