Перевести ядро на единый контракт источника
Карточка, очередь и отсев работают с SourceItem (SourceRef + SourceContent); Telegram-поля убраны из домена, персистентности, конвейера и wire, остались только в адаптере приёма. Tenant-миграции пересозданы с нуля. Фронт переведён на generic source/content с просмотрщиком вложений.
This commit is contained in:
+8
-7
@@ -232,16 +232,13 @@
|
||||
"local": false, // создана локально, без внешнего источника
|
||||
"title": "Разработка интернет-магазина", // string ≤140
|
||||
"summary": "Компания: …\nЗадача: …",// блок «О заявке»
|
||||
"source": { "kind": "telegram", "displayName": "Канал заказов", "originRef": "123456789", "receivedAt": 1726000000000 },
|
||||
"sourceMsg": "Ищу разработчика…", // исходное сообщение
|
||||
"sourceDialogId": "123456789",
|
||||
"sourceMsgId": 4242,
|
||||
"source": { "kind": "telegram", "externalId": "4242", "displayName": "Канал заказов", "originRef": "123456789", "author": "…", "receivedAt": "2026-09-11T10:00:00+00:00", "extra": {"hue": "#8b8ff8"} },
|
||||
"content": { "text": "Ищу разработчика…", "html": null, "author": "…", "subject": null, "data": [], "links": [], "contacts": [] },
|
||||
"stack": ["vue", "dotnet"],
|
||||
"budget": {"from": 100000, "to": 200000, "cur": "RUB"},
|
||||
"converted": {"from": 100000, "to": 200000, "cur": "RUB"},
|
||||
"contact": "@client",
|
||||
"contacts": [{"type": "tg", "value": "@client"}],
|
||||
"channel": {"name": "Канал заказов", "handle": "@orders", "hue": "#8b8ff8"}, // прежний ch
|
||||
"matchHits": [{"label": "Стек", "term": "vue", "word": null}],
|
||||
"comments": [{"id": "cm_…", "by": "Вы", "text": "Позвонил", "time": "5 мин"}],
|
||||
"links": [{"id": "pl_…", "name": "Бриф", "url": "https://example.com"}],
|
||||
@@ -257,11 +254,15 @@
|
||||
}
|
||||
```
|
||||
|
||||
Ключевые поля: `id/containerId/(col)` — принадлежность; `source/sourceMsg/sourceDialogId/sourceMsgId` —
|
||||
происхождение; `stack/budget/converted/contact/contacts/channel/matchHits` — данные заявки; `comments/
|
||||
Ключевые поля: `id/containerId/(col)` — принадлежность; `source` (`SourceRef`: вид, внешний id, подпись,
|
||||
ссылка на оригинал, цвет в `extra.hue`) и `content` (`SourceContent`: текст, разметка, ссылки, контакты,
|
||||
вложения `data`) — происхождение; `stack/budget/converted/contact/contacts/matchHits` — данные заявки; `comments/
|
||||
links/files/history/tzText/reminder` — модули; `isNew/prevCol/isVacancy/isVacancyKnown` — маркеры. Ровно
|
||||
один из полей `history[].type`/`history[].stage` задан.
|
||||
|
||||
Строки очереди и отсева «Обработки» отдают тот же generic `source`/`content` (раньше — `ch`); решение
|
||||
отсева — `decidedBy`/`decidedByLabel` (stop|ml|ai|stale|dup).
|
||||
|
||||
**counts** (`GET /api/cards/counts`): `{new: int, "<containerId>": {count: int, new: int}, learning: int, ml: int, ai: int}` — плоская форма (совместима с прежним `/api/leads/counts`).
|
||||
|
||||
### 4.2 Контейнер (container) — `GET /api/containers`, `POST/PATCH` тела
|
||||
|
||||
@@ -1,7 +1,19 @@
|
||||
# Дейл (Deal) — Статус разработки и прогресс
|
||||
|
||||
> Обновляется в конце каждого захода. Проект НЕ git — это главный борд состояния.
|
||||
> Дата последнего обновления: 2026-09-10. **Все этапы 0–12 выполнены (100%)** — см. roadmap
|
||||
> Обновляется в конце каждого захода. Проект в git (ветка `main`, коммиты на русском) — борд состояния + git-история.
|
||||
> Дата последнего обновления: 2026-09-11.
|
||||
>
|
||||
> **2026-09-11 — единый контракт источника (generic source).** Ядро (домен Cards, Storage-сервис,
|
||||
> персистентность, конвейер, wire, фронт) переведено с Telegram-полей карточки на generic-тип
|
||||
> `SourceItem` (`SourceRef` + `SourceContent`, вложения — `DataRef` → общий Storage). Дашборды/канбан/конвейер
|
||||
> больше не знают о Telegram; Telegram-специфика — только в тонком адаптере приёма. Tenant-миграции
|
||||
> пересозданы с нуля (init). Ядро: build 4 sln 0/0, `Deal.Tests.Unit` **1277/1277 PASS**, фронт `build` +
|
||||
> `lint:i18n` зелёные. Детали — `docs/superpowers/specs/2026-09-11-source-contract-design.md`.
|
||||
> Осталось (в backlog): `GET /api/cards/{id}/source` + `ISourceContentProvider`, выгрузка вложений
|
||||
> telegram-адаптером в Storage, `TelegramSourceContentProvider`, перенос оставшейся Telegram-специфики
|
||||
> (`TelegramStore`, `Dialogs`/`TgMessages`, Discovery) в telegram-сервис.
|
||||
|
||||
**Все этапы 0–12 выполнены (100%)** — см. roadmap
|
||||
> `docs/superpowers/plans/2026-09-05-deal-roadmap.md`, план этапа 10
|
||||
> `docs/superpowers/plans/2026-09-10-deal-stage10-operator-analytics.md` и ledgers в `.superpowers/sdd/`.
|
||||
> Live-приёмки на Docker Desktop выполнены: dev-smoke 14/14, SaaS-контур 15/15, prod-контур+mTLS+
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Дизайн: единый контракт источника + общий Storage-сервис данных
|
||||
|
||||
Дата: 2026-09-11. Статус: на согласовании (контракт не плодит типы вложений; файлы — в общем Storage).
|
||||
Дата: 2026-09-11. Статус: реализовано в ядре (домен, Storage-сервис, персистентность, конвейер, wire, фронт); адаптер/провайдер telegram-сервиса и Storage-выгрузка — следующие шаги. Контракт не плодит типы вложений; файлы — в общем Storage.
|
||||
|
||||
## 1. Принцип
|
||||
|
||||
@@ -127,3 +127,45 @@ API ядра: `GET /api/cards/{id}/source` → generic контент.
|
||||
6. Frontend: generic источник + универсальный просмотрщик.
|
||||
7. Telegram: адаптер + провайдер исходника (только в telegram-сервисе) + выгрузка в Storage.
|
||||
8. Комментарии: убрать упоминания Telegram из ядра и задачи/этапы — везде.
|
||||
|
||||
## 9. Реализация: зафиксированные сигнатуры
|
||||
|
||||
### Ядро: домен
|
||||
|
||||
- `SourceRefs` (Deal.Modules.Cards/Application/Sources): `Empty`, `DefaultHue = "#666"`,
|
||||
`HueKey = "hue"`, расширения `DedupeKey()` (вид|оригинал|внешний id), `ResolveHue()`.
|
||||
- `CardSnapshot`: вместо `ChannelName/ChannelHandle/ChannelHue/SourceMsg/SourceDialogId/SourceMsgId` —
|
||||
`SourceRef Source` + `SourceContent Content`; `ReceivedAt` остаётся.
|
||||
- `CardDto`: вместо `Channel`/`SourceMsg`/`SourceDialogId`/`SourceMsgId`/прежнего `Source` —
|
||||
`SourceRef Source` + `SourceContent Content`; `ReceivedAtMs` остаётся. `CardChannelDto`/`CardSourceDto` удалены.
|
||||
- `ICardStore.GetCardBySourceAsync(SourceRef source, CancellationToken ct)`.
|
||||
|
||||
### Ядро: конвейер
|
||||
|
||||
- `QueuedMessage { required SourceItem Item; bool Force; }`.
|
||||
- `QueueItemDto { string Id; SourceRef Source; SourceContent Content; string Text; string Status;
|
||||
long MsgAtMs; long QueuedAtMs; bool Force; }` (JsonIgnore на Force).
|
||||
- `RejectRecord { SourceRef Source; SourceContent Content; string Text; long MsgAtMs;
|
||||
string DecidedBy; string Stage; string Reason; string Kw; string? DeterministicId; }`
|
||||
(`DeterministicId = r_{Kind}_{OriginRef}_{ExternalId}`).
|
||||
- `RejectedItemDto`: `Source`/`Content`, `DecidedBy`/`DecidedByLabel` (решение), остальное как было.
|
||||
- `IPipelineStore.ExistsDuplicateAsync(SourceRef source, CancellationToken ct)`.
|
||||
- `PipelineChannelDto` удалён.
|
||||
|
||||
### Схема БД (схема тенанта)
|
||||
|
||||
- Cards: удалить `ChannelName/ChannelHandle/ChannelHue/SourceMsg/SourceDialogId/SourceMsgId`;
|
||||
добавить `SourceKind`, `SourceExternalId`, `SourceOriginRef` (text, для запросов), `SourceJson` (text),
|
||||
`ContentJson` (text), `SourceText` (text). FTS: Title+Summary+SourceText+Contact.
|
||||
- QueueItems: удалить `DialogId/ChannelName/ChannelHandle/ChannelHue/MsgId`; добавить
|
||||
`SourceKey` (text, уникальный ключ дедупа), `SourceJson`, `ContentJson`. `Text/MsgAt/Status/Force/CreatedAt/UpdatedAt` остаются.
|
||||
- RejectedItems: удалить `DialogId/MsgId/ChannelName/ChannelHandle/ChannelHue`; добавить
|
||||
`SourceKey`, `SourceJson`, `ContentJson`. FTS — по `Text`.
|
||||
- Миграции tenant: старые удалить, сгенерировать новый init с нуля (данных нет).
|
||||
|
||||
### Маппинг источника
|
||||
|
||||
- Адаптер Telegram (в ядре — тонкий край приёма): `Kind="telegram"`, `ExternalId=MsgId`,
|
||||
`OriginRef=DialogId`, `DisplayName=ChannelName`, `Extra["hue"]=ChannelHue` (иначе дефолт),
|
||||
`ReceivedAt=msgAt`, `Content.Text=Text`, `Content.Author=ChannelName`.
|
||||
- Дашборды/карточки/конвейер работают только с `SourceRef`/`SourceContent`; Telegram-поля не проходят дальше адаптера.
|
||||
|
||||
@@ -816,6 +816,14 @@ Contact) + GIN-индекс; tsvector-колонки авто-актуальны
|
||||
(квалификация, ≤6, primary), поля канала `ch`, `sourceMsg`/`sourceDialogId`/`sourceMsgId`; колонка —
|
||||
inbox либо доска по `BoardAccepts`+правилам; после успешной классификации карточка получает
|
||||
`isVacancy`/`isVacancyKnown`; создание карточки связывает хэш в `DedupEntries` с её id.
|
||||
|
||||
> **Актуально с 2026-09-11: единый контракт источника.** Карточка, очередь и отсев работают с generic-типом
|
||||
> `SourceItem` (`SourceRef` + `SourceContent`). В таблицах `Cards`/`QueueItems`/`RejectedItems` вместо
|
||||
> Telegram-колонок (`ChannelName`/`SourceMsg`/`SourceMsgId`…) хранятся `SourceKind`/`SourceExternalId`/
|
||||
> `SourceOriginRef`/`SourceJson`/`ContentJson` (карточка — ещё `SourceText` и FTS по нему; очередь/отсев —
|
||||
> `SourceKey` для дедупа). Вложение — `DataRef` (ссылка на общий Storage-сервис); контакты/ссылки — поля
|
||||
> `SourceContent`. Telegram-поля остались только в тонком адаптере приёма telegram-сервиса. Tenant-миграции
|
||||
> пересозданы с нуля (данных нет). Детали — `docs/superpowers/specs/2026-09-11-source-contract-design.md`.
|
||||
- Отсев — источник решения `stop|ml|ai|stale|dup` (подписи «правила/ML/ИИ/система») и этап
|
||||
`length|stop|resume|type|budget|stale|spam_ml|spam_ai|filter_ai|dup` (подписи UI: «короткое сообщение»,
|
||||
«стоп-фраза», «резюме соискателя», «нет суммы», «устарело», «повтор»…), причина ≤500, kw — сработавшая
|
||||
|
||||
Reference in New Issue
Block a user