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