SaaS-мониторинг Telegram: ядро (модули Cards/Kanban/Pipeline/Tenants/Settings/ Discovery, Api, Infrastructure), сервисы telegram/ai/ml/storage, фронт Vue, контракты и grpc-hosting, деплой-конфиги (dev/prod/observability/CI-раннер), Gitea Actions CI, документация (ТЗ, техдок, api-map, код-стайл, планы, бэклог). Текущее состояние: все этапы роадмапа 0–12 закрыты, сборка 5 sln 0/0, тесты 1340/130/52/38/9 зелёные.
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
# Дейл — единая модель карточки (unified card)
|
||||
|
||||
> Исторический документ (дизайн этапа 9, 2026-09-09). Актуальное состояние — `docs/superpowers/STATUS.md` и `docs/technical/Техническая-документация-Дейл.md`.
|
||||
|
||||
> Дата: 2026-09-09
|
||||
> Статус: дизайн согласован с владельцем продукта (в чате), начало реализации.
|
||||
> Связанные документы: `docs/spec/ТЗ-дейл-новая-архитектура.md`, `docs/architecture/2026-09-05-deal-architecture-design.md`.
|
||||
|
||||
## Проблема
|
||||
|
||||
Сейчас в системе **два «домена» карточек**, хотя по смыслу это одна сущность:
|
||||
|
||||
| | Канбан (`/api/leads`) | «Выбранные» (`/api/projects`) |
|
||||
|---|---|---|
|
||||
| Таблица | `Cards` | `ProjectCards` |
|
||||
| Контейнер | колонка `inbox/board/archive/trash/taken` | стадия `planned…finished/rejected` |
|
||||
| «Взять в работу» | `col=taken` + **копия полей** в `ProjectCards` | создание второй записи |
|
||||
| Драйвер/карточка | `CardDto` | `ProjectCardDto` |
|
||||
|
||||
Переход «лид → проектная карточка» — это **клонирование в другую сущность**: у карточки меняется id, теряется связность истории, третий вид карточки/дашборда потребует третьей таблицы и третьего конвейера.
|
||||
|
||||
**Решение (согласовано):** карточка — **один агрегат** во всех дашбордах. Понятие «лид» упраздняется: сообщение из канала — это *входные данные*, из которых создаётся карточка. «Взял в работу» — это **переход карточки в другой контейнер** той же доски пространства «Выбранные», а не создание новой записи.
|
||||
|
||||
## Модель (C#)
|
||||
|
||||
### Ядро
|
||||
|
||||
```csharp
|
||||
/// Единственное, что есть у любой карточки.
|
||||
public interface ICard
|
||||
{
|
||||
string Id { get; }
|
||||
string Title { get; }
|
||||
ISource Source { get; } // откуда пришла (см. ниже)
|
||||
}
|
||||
|
||||
/// Типизированная проекция для сценариев, которым нужен конкретный источник.
|
||||
public interface ICard<TSource> : ICard where TSource : ISource
|
||||
{
|
||||
new TSource Source { get; }
|
||||
}
|
||||
```
|
||||
|
||||
### Источники (ISource) — иерархия, а не enum-свойство
|
||||
|
||||
- `ISource` — общее: `DisplayName`, `OriginRef`, `RawPayload`, `ReceivedAt`.
|
||||
- Простые: `ILocalSource`, `IWebSource`, `IFileSource`.
|
||||
- Сложные: `ITelegramSource` (dialogId/messageId/peer/topic), `IRowSource` (импорт колонки/строки), `IApiSource`, `IAiSource` (провайдер+модель+агент), `ICompositeSource { Origin, Pipeline[] }`.
|
||||
|
||||
### Модули-роли карточки (опциональные части одного агрегата)
|
||||
|
||||
`IContentCard` (блок «О заявке»), `IBudgetedCard`, `IContactCard`, `IAttributedCard` (стек/грейд/локация — настраиваемые атрибуты тенанта), `ICommentableCard`, `ILinkCard`, `IFileCard`, `ITzCard`, `ITraceableCard` (история), `IRemindableCard`, `ILocatedCard` (контейнер + prev + isNew).
|
||||
|
||||
Вид карточки = композиция модулей, **не класс-наследник**. Новый дашборд/вид — новая композиция + при необходимости новый модуль.
|
||||
|
||||
### Контейнеры (общая база колонок/стадий/зон)
|
||||
|
||||
```csharp
|
||||
public interface IContainer
|
||||
{
|
||||
string Id { get; }
|
||||
string Name { get; }
|
||||
string Color { get; }
|
||||
int Order { get; }
|
||||
IContainerRules? Rules { get; } // фильтры попадания (пользовательские колонки)
|
||||
IContainerPolicy Policy { get; } // поведение (роль, не enum)
|
||||
}
|
||||
```
|
||||
|
||||
Политики: возврат/очистка (корзина 7д, архив 90д), терминальность («Отклонено/Выполнено» — только ручная очистка), «выбранные не попадают в архив дашборда». Отсев пайплайна — **не карточка**, вне этой модели.
|
||||
|
||||
### Переходы
|
||||
|
||||
Один `ICardMover.MoveAsync(card, toContainerId, ctx)`; правила — в политиках контейнеров и «воротах» между пространствами; побочные эффекты карточка делает через свои модули (`ITraceableCard` пишет историю, `IRemindableCard` сбрасывает напоминание, `ILocatedCard.IsNew=false`).
|
||||
|
||||
## Терминология
|
||||
|
||||
- ~~лид, lead~~ → **карточка (card)**; входное сообщение → **сообщение-источник**.
|
||||
- ~~проектная карточка~~ → карточка в контейнерах пространства «Выбранные».
|
||||
- «Взять в работу» → переход в контейнер `planned`.
|
||||
|
||||
## Что меняется
|
||||
|
||||
- **БД**: таблицы `Cards` + `ProjectCards` → одна `Cards` (+ модульные данные); доски и стадии — единый реестр контейнеров; удаляется `ProjectCards`, перенос `LeadComments` в модуль карточки.
|
||||
- **Бэк**: модули Kanban и Projects объединяются в один модуль карточки/контейнеров; порты/сервисы/адаптеры/DTO — единые.
|
||||
- **Pipeline**: создаёт карточку (не «лид»), кладёт в контейнер по правилам.
|
||||
- **API**: единый контракт `/api/cards` + `/api/containers`; `/api/leads`, `/api/projects` упраздняются (фронт переписывается).
|
||||
- **Фронт**: один state-слайс карточек, один рендер карточки/драйвера, один канбан-компонент.
|
||||
|
||||
## Границы этапа
|
||||
|
||||
Данные тестовые — схема пересоздаётся, миграции данных нет. Вне рамок: Kafka, «третьи» дашборды (архитектура готова), разовые миграции.
|
||||
Reference in New Issue
Block a user