ci / build-test (push) Canceled after 0s
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 зелёные.
93 lines
6.6 KiB
Markdown
93 lines
6.6 KiB
Markdown
# Дейл — единая модель карточки (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, «третьи» дашборды (архитектура готова), разовые миграции.
|