Files
marketplace/отчеты/план-админ-модерация-монетизация.md
delta-lynx-89e8 dbbbbd26f4 Add rental system: listings, bookings, payments, payouts, reviews
Full rental marketplace with 6 categories (apartment, house, car, motorcycle, bicycle, ebike).
Booking workflow: create → confirm → pay → active → complete → payout.
Landlord dashboard, admin moderation, availability calendar, Stripe Connect payouts.
14 QA bugs found and fixed including validator schemas, API response types, HTTP methods.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 15:33:29 -08:00

275 lines
15 KiB
Markdown
Raw Permalink 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.
# Marketplace — Админ-панель, Модерация, Монетизация
## Контекст
MVP маркетплейса полностью рабочий и задеплоен (buyer/seller flow, чат, офферы, нотификации, избранное, настройки). Но отсутствует вся бизнес-логика уровня продакшена: нет ролей, нет админки, нет модерации, нет системы жалоб, нет монетизации. Сейчас любой листинг публикуется мгновенно без проверки, нет возможности заблокировать пользователя со стороны платформы, и нет инструментов для управления маркетплейсом.
**Сервер**: 173.212.212.157 | **Домен**: marketplace.173.212.212.157.sslip.io
---
## Фаза 1: Схема БД + RBAC (Роли и права)
### Prisma Schema — `server/prisma/schema.prisma`
**Новые enum'ы:**
- `UserRole`: USER, MODERATOR, ADMIN, SUPER_ADMIN
- `ReportReason`: SPAM, INAPPROPRIATE, SCAM, COUNTERFEIT, PROHIBITED_ITEM, HARASSMENT, OTHER
- `ReportStatus`: OPEN, REVIEWING, RESOLVED, DISMISSED
- `ReportTargetType`: LISTING, USER
- `SubscriptionTier`: BASIC, PRO, BUSINESS
- `SubscriptionStatus`: ACTIVE, CANCELLED, EXPIRED, PAST_DUE
- `PaymentType`: LISTING_FEE, COMMISSION, PROMOTION, SUBSCRIPTION
- `ModerationAction`: APPROVED, REJECTED, WARNING, BAN, UNBAN, LISTING_DELETED, LISTING_FEATURED
**Изменения существующих enum'ов:**
- `ListingStatus`: добавлен `PENDING_REVIEW` между DRAFT и ACTIVE
- `NotificationType`: добавлены `LISTING_APPROVED, LISTING_REJECTED, MODERATION_WARNING, ACCOUNT_BANNED, ACCOUNT_UNBANNED, REPORT_RESOLVED`
**Изменения существующих моделей:**
- `User`: + `role UserRole @default(USER)`, `isBanned Boolean @default(false)`, `banReason String?`, `bannedAt DateTime?`, `bannedBy String?`
- `Listing`: + `isFeatured Boolean @default(false)`, `rejectionReason String?`, `reviewedBy String?`, `reviewedAt DateTime?`
- `Payment`: + `type PaymentType @default(LISTING_FEE)`, `description String?`
**Новые модели (6 штук):**
| Модель | Назначение | Ключевые поля |
|---|---|---|
| `Report` | Жалобы пользователей | reporterId, targetType, targetId, reason, status, resolvedBy, resolution |
| `PlatformConfig` | Настройки платформы (1 строка) | listingFee, commissionPercent, autoApprove, maxImagesPerListing, maxListingsFreeTier, proPrice, businessPrice, promotionDayPrice, blockedKeywords[] |
| `Subscription` | Подписки продавцов | userId (unique), tier, status, stripeSubscriptionId, currentPeriodEnd |
| `PromotedListing` | Продвинутые объявления | listingId (unique), userId, startDate, endDate, amountPaid, isActive |
| `ModerationLog` | Аудит-лог действий модераторов | moderatorId, targetUserId?, targetListingId?, action, reason, details(Json) |
### Middleware — новые файлы
| Файл | Назначение |
|---|---|
| `server/src/middleware/requireRole.ts` | Фабрика middleware: `requireRole('ADMIN', 'SUPER_ADMIN')`. Проверяет роль из БД (не из JWT — изменения роли применяются мгновенно). Экспорт: `requireModerator`, `requireAdmin`, `requireSuperAdmin` |
| `server/src/middleware/checkBanned.ts` | Проверяет `isBanned` и возвращает 403 если забанен |
| `server/src/utils/moderation.ts` | Проверка текста на запрещённые слова из PlatformConfig. Кэш конфига на 60 сек |
### Изменения существующих файлов
| Файл | Изменения |
|---|---|
| `server/src/middleware/auth.ts` | Расширен `Request` типом `userRole?: string` |
| `server/src/routes/auth.ts` | Добавлен `role` в select; проверка `isBanned` при логине (403 с причиной) |
| `client/src/types/index.ts` | Добавлен `role: UserRole` в User, новые типы Report, PlatformConfig, Subscription и т.д. |
| `client/src/context/AuthContext.tsx` | Добавлены `isAdmin`, `isModerator`, `isSuperAdmin` (computed из `user.role`) |
### Seed — `server/prisma/seed.ts`
- Создана строка PlatformConfig с дефолтами
- alice → SUPER_ADMIN, bob → ADMIN, carol → MODERATOR
---
## Фаза 2: Админ-панель — Layout + Dashboard
### Новые компоненты
| Файл | Назначение |
|---|---|
| `client/src/components/layout/RequireRole.tsx` | Route guard: проверяет `user.role`, редирект на `/` если нет доступа |
| `client/src/components/layout/AdminLayout.tsx` | Sidebar с навигацией (Dashboard, Users, Listings, Reports, Moderation, Payments, Settings) + `<Outlet />` |
| `client/src/components/ui/StatCard.tsx` | Карточка метрики: иконка, число, подпись, тренд |
| `client/src/components/ui/DataTable.tsx` | Переиспользуемая таблица: колонки, пагинация, поиск, сортировка |
### Backend — `server/src/routes/admin/stats.ts`
| Endpoint | Доступ | Что делает |
|---|---|---|
| `GET /api/admin/stats` | MODERATOR+ | Общая статистика: юзеры, листинги, офферы, выручка, активные сегодня |
| `GET /api/admin/stats/revenue` | ADMIN+ | Выручка по дням/неделям/месяцам |
| `GET /api/admin/stats/users` | ADMIN+ | Рост пользователей по времени |
| `GET /api/admin/stats/listings` | MODERATOR+ | Активность листингов по времени |
### Frontend — `client/src/pages/admin/AdminDashboardPage.tsx`
- 5 StatCard'ов: юзеры, листинги, офферы, выручка, активные
- Quick Stats и Platform Health панели
### Router — `client/src/router.tsx`
```
/admin → RequireRole(['MODERATOR','ADMIN','SUPER_ADMIN']) → AdminLayout
/admin (index) → AdminDashboardPage
/admin/users → AdminUsersPage
/admin/users/:id → AdminUserDetailPage
/admin/listings → AdminListingsPage
/admin/reports → AdminReportsPage
/admin/moderation → AdminModerationPage
/admin/payments → AdminPaymentsPage
/admin/settings → AdminSettingsPage
```
### Header — `client/src/components/layout/Header.tsx`
- Добавлена ссылка "Admin" в навигацию (если `isModerator || isAdmin`)
---
## Фаза 3: Управление пользователями
### Backend — `server/src/routes/admin/users.ts`
| Endpoint | Доступ | Что делает |
|---|---|---|
| `GET /api/admin/users` | MODERATOR+ | Список юзеров с поиском, фильтром по роли/статусу, пагинацией |
| `GET /api/admin/users/:id` | MODERATOR+ | Полный профиль + кол-во листингов/офферов/жалоб + лог модерации |
| `PATCH /api/admin/users/:id/role` | SUPER_ADMIN | Сменить роль (нельзя себя понизить) |
| `POST /api/admin/users/:id/ban` | ADMIN+ | Забанить (body: `{ reason }`). Создаёт ModerationLog, отправляет нотификацию ACCOUNT_BANNED |
| `POST /api/admin/users/:id/unban` | ADMIN+ | Разбанить. ModerationLog + нотификация ACCOUNT_UNBANNED |
### Frontend
- `AdminUsersPage.tsx` — DataTable: аватар+имя, email, роль (Badge), статус, дата регистрации, действия
- `AdminUserDetailPage.tsx` — профиль + статистика + история модерации + кнопки бан/разбан/роль
---
## Фаза 4: Модерация объявлений
### Backend
**`server/src/routes/admin/listings.ts`:**
| Endpoint | Доступ | Что делает |
|---|---|---|
| `GET /api/admin/listings` | MODERATOR+ | Все листинги, фильтр по статусу/категории, поиск |
| `POST /api/admin/listings/:id/approve` | MODERATOR+ | Статус → ACTIVE, ModerationLog, нотификация LISTING_APPROVED |
| `POST /api/admin/listings/:id/reject` | MODERATOR+ | Статус → DELETED, сохранить rejectionReason, нотификация LISTING_REJECTED |
| `DELETE /api/admin/listings/:id` | ADMIN+ | Принудительное удаление, ModerationLog |
| `POST /api/admin/listings/:id/feature` | ADMIN+ | Переключить isFeatured |
**`server/src/routes/admin/moderation.ts`:**
| Endpoint | Доступ | Что делает |
|---|---|---|
| `GET /api/admin/moderation/queue` | MODERATOR+ | Листинги со статусом PENDING_REVIEW, пагинация |
| `GET /api/admin/moderation/logs` | ADMIN+ | Полная история действий модерации |
**Изменения `server/src/routes/listing.ts`:**
- `POST /:id/activate`: если `PlatformConfig.autoApprove === false` → статус `PENDING_REVIEW` вместо `ACTIVE`
- `POST /` (создание): проверка текста на `blockedKeywords` → если найдено, автоматически `PENDING_REVIEW`
- Добавлен `isFeatured` в listingSelect
### Frontend
- `AdminListingsPage.tsx` — DataTable с табами по статусу (ALL, PENDING_REVIEW, ACTIVE, SOLD, DELETED)
- `AdminModerationPage.tsx` — Карточки листингов на ревью: фото, заголовок, описание, продавец, кнопки Approve/Reject
---
## Фаза 5: Система жалоб (Reports)
### Backend
**`server/src/routes/report.ts`** (пользовательский):
| Endpoint | Доступ | Что делает |
|---|---|---|
| `POST /api/reports` | Авторизованный | Создать жалобу: `{ targetType, targetId, reason, description }` |
**`server/src/routes/admin/reports.ts`:**
| Endpoint | Доступ | Что делает |
|---|---|---|
| `GET /api/admin/reports` | MODERATOR+ | Список жалоб, фильтр по статусу/типу, пагинация |
| `GET /api/admin/reports/:id` | MODERATOR+ | Детали жалобы с информацией о цели |
| `PATCH /api/admin/reports/:id` | MODERATOR+ | Разрешить/отклонить: `{ status, resolution }` |
### Frontend
- `ReportModal.tsx` — Модалка с радиокнопками причин + описание
- `ProductDetailPage.tsx` — кнопка "Report" теперь открывает ReportModal (вместо `alert()`)
- `AdminReportsPage.tsx` — DataTable с табами по статусу
---
## Фаза 6: Настройки платформы + Админ платежей
### Backend
**`server/src/routes/admin/settings.ts`:**
| Endpoint | Доступ | Что делает |
|---|---|---|
| `GET /api/admin/settings` | ADMIN+ | Текущий PlatformConfig |
| `PATCH /api/admin/settings` | SUPER_ADMIN | Обновить любые поля PlatformConfig |
**`server/src/routes/admin/payments.ts`:**
| Endpoint | Доступ | Что делает |
|---|---|---|
| `GET /api/admin/payments` | ADMIN+ | Все платежи, фильтр по типу/статусу, пагинация |
| `GET /api/admin/payments/revenue` | ADMIN+ | Разбивка: listing fees, commissions, promotions, subscriptions |
**Изменения `server/src/routes/payment.ts`:**
- `POST /create-intent`: читает `listingFee` из PlatformConfig вместо хардкода `500`
- Записывает `type: 'LISTING_FEE'` в Payment
### Frontend
- `AdminSettingsPage.tsx` — Форма: listing fee, commission %, auto-approve toggle, max images, free tier limit, pro/business цены, promotion day price, blocked keywords
- `AdminPaymentsPage.tsx` — 4 карточки выручки по типам + таблица транзакций
---
## Фаза 7: Монетизация — Подписки + Продвижение
### Backend
**`server/src/routes/subscription.ts`:**
| Endpoint | Доступ | Что делает |
|---|---|---|
| `GET /api/subscriptions/current` | Авторизованный | Текущая подписка (или BASIC по умолчанию) |
| `POST /api/subscriptions/create` | Авторизованный | Создать подписку PRO/BUSINESS |
| `POST /api/subscriptions/cancel` | Авторизованный | Отменить подписку |
**`server/src/routes/promotion.ts`:**
| Endpoint | Доступ | Что делает |
|---|---|---|
| `POST /api/listings/:id/promote` | Авторизованный | Оплатить продвижение (body: `{ days }`) |
| `GET /api/listings/:id/promotion` | Авторизованный | Статус продвижения |
**Изменения `server/src/routes/offer.ts`:**
- При ACCEPTED: создаётся Payment с `type: 'COMMISSION'` и суммой = `commissionPercent` от amount
**Тарифные планы:**
- **Basic** (бесплатно): до 5 листингов/мес
- **Pro** ($9.99/мес): безлимит + аналитика продавца
- **Business** ($29.99/мес): bulk-операции + приоритетная поддержка
---
## Деплой
```bash
# 1. Миграция
cd server && npx prisma migrate dev --name admin_moderation_monetization
# 2. Обновить seed
npx tsx prisma/seed.ts
# 3. Билд и деплой
npm run build --workspace=client
scp -r client/dist/* root@173.212.212.157:/var/www/marketplace/
ssh root@173.212.212.157 'cd /var/www/marketplace-app && git pull && cd server && npm install && npx prisma db push && pm2 restart marketplace-api'
# 4. Seed на проде
ssh root@173.212.212.157 'cd /var/www/marketplace-app/server && npx tsx prisma/seed.ts'
```
---
## Верификация
1. Логин как alice (SUPER_ADMIN) → в хедере видна ссылка "Admin"
2. `/admin` → дашборд с метриками
3. `/admin/users` → таблица юзеров, можно забанить david
4. Логин как david → получить 403 "Account suspended"
5. `/admin/users` → разбанить david
6. `/admin/settings` → отключить autoApprove
7. Логин как david → создать листинг → статус PENDING_REVIEW
8. `/admin/moderation` → увидеть листинг в очереди → Approve
9. На странице товара нажать Report → модалка → отправить жалобу
10. `/admin/reports` → увидеть жалобу → Resolve
11. `/admin/payments` → все транзакции с типами
12. `/admin/settings` → изменить listing fee