using Deal.Contracts.Integrations.Models; using Deal.Modules.Kanban.Application; using Deal.Modules.Kanban.Application.Models; using Deal.Modules.Pipeline.Application.Models; namespace Deal.Modules.Pipeline.Application; /// /// Создание карточки пайплайна через публичный интерфейс Kanban (план Task 7 L400–402, Ruling 3/4; /// python _store_lead L483–514: INSERT карточки → UPDATE dedup.lead_id → чтение после записи). /// /// /// Тонкая обёртка создания: генерирует id карточки (c_, / — /// переиспользуем генератор владельца), собирает полный снимок через и пишет его /// , после чего связывает заявку дедупа с карточкой /// ( — порядок AddCard → Link 1:1 с Ruling 4, L512–513) и перечитывает /// полный (для SSE new_card, Ruling 8/9). /// /// Проверку «уже есть карточка по дедупу» обёртка НЕ дублирует — её делает воркер на «new»-проходе pump /// (Ruling 8, L940–951): к моменту записи сообщение уже прошло дедуп-гвард и его хэш заявлен (LeadId=null). /// Транзакционности карточка + dedup-link нет (как в прототипе — два отдельных statement'а адаптеров на общем /// TenantDbContext, Ruling 3/4): сбой LinkAsync оставляет карточку без связи и пробрасывается вызывающему. /// /// public sealed class PipelineCardWriter(ICardStore kanjStore, IPipelineStore pipelineStore, CardComposer composer) { /// /// Создаёт карточку из разобранного сообщения и связывает её с заявкой дедупа. /// /// Разбор сообщения (ИИ/локальный путь; колонка разбора пройдёт ContainerAccepts-страховку). /// Строка очереди с сообщением-источником (метаданные канала/текст/время). /// SHA1-hex хэша текста (заявка дедупа уже создана воркером, Ruling 8). /// Токен отмены. /// Полная карточка (чтение после записи — §4.1, как lead_to_dict после INSERT, L514). /// Карточка не прочиталась сразу после создания. public async Task CreateCardAsync( AiParsedCardDto parsed, QueueItemDto message, string dedupHash, CancellationToken ct) { string cardId = PrefixId.New(KanbanIdPrefixes.Card); CardSnapshot snapshot = await composer.BuildAsync(parsed, message, cardId, ct); await kanjStore.AddCardAsync(snapshot, ct); await pipelineStore.LinkAsync(dedupHash, cardId, ct); return await kanjStore.GetCardAsync(cardId, ct) ?? throw new InvalidOperationException("Карточка не прочиталась после создания: " + cardId); } }