dana-medical-site/internal/api/report_bitrix.go
2026-10-14 13:33:13 +00:00

115 lines
5.8 KiB
Go
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.

package api
import (
"context"
"fmt"
"time"
"dana/internal/bitrix"
)
// bitrixSourceInfo — сколько записей и номеров забрали из Битрикс24 для «Отчёта по каналам».
type bitrixSourceInfo struct {
Records int `json:"records"` // лидов и контактов обработано всего
Phones int `json:"phones"` // из них с номером телефона, попавших в отчёт
Skipped int `json:"skipped"` // лидов с источником другой клиники (портал общий) или служебным — не учтены
}
// bitrixChannelBySource — источники лидов DANA (crm.status.list, ENTITY_ID=SOURCE) → канал отчёта.
// Белый список осознанно: в этом Битриксе есть вторая компания («Очевидно») со своими источниками —
// звонки/заявки не из этого списка в отчёт не попадают, даже если выглядят похоже.
var bitrixChannelBySource = map[string]string{
"132": "2гис", "137": "2гис", // 137 — тот же номер, но через WhatsApp
"134": "сайт", "136": "сайт", "112": "сайт",
"135": "яндекс", "138": "яндекс",
"143": "продокторов", "142": "продокторов",
"144": "сберздоровье",
"145": "zoon",
"146": "напоправку",
"147": "докту",
"154": "инстаграмм", "155": "инстаграмм", "156": "инстаграмм", "157": "инстаграмм", "158": "инстаграмм",
"20": "инстаграмм", "23": "инстаграмм", "26": "инстаграмм",
"44|PACT_INSTAGRAM_BUSINESS_CONNECTOR_101559": "инстаграмм",
"14|PACT_WHATSAPP_CONNECTOR_66018": "инстаграмм", // WhatsApp на 047-00-55: заявки приходят из инстаграма
"10|PACT_INSTAGRAM_BUSINESS_CONNECTOR_66018": "инстаграмм",
"STORE": "баннер", "166": "баннер",
"159": "маршрутки",
"160": "тв",
"148": "google", "149": "google",
"161": "радио",
"162": "полиграфия",
"140": "ролик",
"119": "max",
"27": "telegram",
"WZ0bdb58c8-907e-4632-84b0-81a30d250588": "вк",
"133|VK": "вк",
}
// bitrixGeneralSources — общая линия DANA без привязки к рекламному каналу: идёт в «все номера»,
// как колл-центр без атрибуции (так же, как необработанные линии АТС).
var bitrixGeneralSources = map[string]bool{
"113": true, // звонки на мегафоновский 047-00-55
}
// bitrixMatchOpts — выгрузка номеров из Битрикс24 для «Отчёта по каналам» за период [from, to):
// лиды раскладываются по каналам через SOURCE_ID (белый список выше), контакты (у них источника
// нет) идут в «все номера». Период обязателен — без него пришлось бы тянуть всю историю CRM.
func (a *API) bitrixMatchOpts(ctx context.Context, clinic *clinicDef, from, to time.Time, onProgress func(phase string, done, total int)) (matchOpts, error) {
if !a.bitrix.Configured() {
return matchOpts{}, fmt.Errorf("Битрикс24 не подключён: нет BITRIX_WEBHOOK_URL")
}
phase := func(name string) bitrix.Progress {
if onProgress == nil {
return nil
}
return func(done, total int) { onProgress(name, done, total) }
}
leads, err := a.bitrix.Leads(ctx, from, to, phase("leads"))
if err != nil {
return matchOpts{}, fmt.Errorf("Не удалось загрузить лиды из Битрикс24: %v", err)
}
var contacts []bitrix.Record
if clinic.bitrixContacts {
if contacts, err = a.bitrix.Contacts(ctx, from, to, phase("contacts")); err != nil {
return matchOpts{}, fmt.Errorf("Не удалось загрузить контакты из Битрикс24: %v", err)
}
}
lists := map[string][]string{}
phones, skipped := 0, 0
for _, l := range leads {
switch {
case clinic.bitrixSources[l.SourceID] != "":
ch := clinic.bitrixSources[l.SourceID]
lists[ch] = append(lists[ch], l.Phones...)
phones += len(l.Phones)
case clinic.bitrixGeneral[l.SourceID]:
lists["все номера"] = append(lists["все номера"], l.Phones...)
phones += len(l.Phones)
default:
skipped++ // источник другой клиники либо служебный (CALL, WEBFORM…) — не учитываем
}
}
for _, c := range contacts {
lists["все номера"] = append(lists["все номера"], c.Phones...)
phones += len(c.Phones)
}
if phones == 0 {
return matchOpts{}, fmt.Errorf("В Битрикс24 не нашлось ни одного номера %s за период — проверьте права вебхука", clinic.Title)
}
return matchOpts{pbxLists: lists, bitrixInfo: &bitrixSourceInfo{
Records: len(leads) + len(contacts), Phones: phones, Skipped: skipped,
}}, nil
}
// mergeChannelLists объединяет номера каналов из двух источников (например, АТС и Битрикс24)
// в один — списки одноимённых каналов складываются, а не перезаписываются.
func mergeChannelLists(dst, src map[string][]string) map[string][]string {
if dst == nil {
dst = map[string][]string{}
}
for k, v := range src {
dst[k] = append(dst[k], v...)
}
return dst
}