diff --git a/dal/dal.go b/dal/dal.go index c64e4bc..3acc8a5 100644 --- a/dal/dal.go +++ b/dal/dal.go @@ -12,6 +12,7 @@ import ( "github.com/minio/minio-go/v7" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal/sqlcgen" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/repository/account" + "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/repository/amo" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/repository/answer" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/repository/question" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/repository/quiz" @@ -51,8 +52,8 @@ func New(ctx context.Context, cred string, minioClient *minio.Client) (*DAL, err queries := sqlcgen.New(pool) accountRepo := account.NewAccountRepository(account.Deps{ - Queries: queries, - Pool: pool, + Queries: queries, + Pool: pool, }) storerAnswer := &answer.StorerAnswer{} @@ -130,3 +131,44 @@ func (d *DAL) Init() error { } return nil } + +type AmoDal struct { + conn *sql.DB + queries *sqlcgen.Queries + AmoRepo *amo.AmoRepository +} + +func NewAmoDal(ctx context.Context, cred string) (*AmoDal, error) { + pool, err := sql.Open("postgres", cred) + if err != nil { + return nil, err + } + + timeoutCtx, cancel := context.WithTimeout(ctx, time.Second) + defer cancel() + + if err := pool.PingContext(timeoutCtx); err != nil { + return nil, err + } + + queries := sqlcgen.New(pool) + + amoRepo := amo.NewAmoRepository(amo.Deps{ + Queries: queries, + Pool: pool, + }) + + return &AmoDal{ + conn: pool, + queries: queries, + AmoRepo: amoRepo, + }, nil +} + +func (d *AmoDal) Close(ctx context.Context) error { + err := d.conn.Close() + if err != nil { + return err + } + return nil +} diff --git a/model/amo.go b/model/amo.go new file mode 100644 index 0000000..74e268a --- /dev/null +++ b/model/amo.go @@ -0,0 +1,188 @@ +package model + +type User struct { + /* - айдишник в нашей системе Primary Key*/ + ID int `json:"ID"` + /* - id пользователя из токена в нашей системе*/ + Accountid string `json:"AccountID"` + /* - айдишник пользователя в амо*/ + AmoID int `json:"AmocrmID"` + /* - имя аккаунта в амо*/ + Name string `json:"Name"` + /* - почта пользователя из амо*/ + Email string `json:"Email"` + /* - роль пользователя в амо*/ + Role string `json:"Role"` + /* - группы пользователя в амо*/ + Group []UserGroups `json:"Group"` + /* - флаг мягкого удаления*/ + Deleted bool `json:"Deleted"` + /* - таймштамп создания аккаунта*/ + Createdat int64 `json:"CreatedAt"` + /* - поддомен организации в амо*/ + Subdomain string `json:"Subdomain"` + /* - айдишник пользвателя, который подключал интеграцию*/ + Amouserid int `json:"AmoUserID"` + /* - страна указанная в настройках амо*/ + Country string `json:"Country"` +} + +type UserGroups struct { + ID int `json:"id" bson:"id"` + Name string `json:"name" bson:"name"` + UUID interface{} `json:"uuid" bson:"uuid"` +} + +type Token struct { + AccountID string `json:"account_id"` // id в квизе + RefreshToken string `json:"refresh_token"` // 80 дней + AccessToken string `json:"access_token"` // 20 минут + AuthCode string `json:"auth_code"` + Expiration int64 `json:"expiration"` // таймшамп времени когда кончится AccessToken + CreatedAt int64 `json:"created_at"` // таймшамп времени создания, нужен для отслеживания 80 дней +} + +type Pipeline struct { + // айдишник в нашей системе Primary Key + ID int `json:"ID"` + /* - айдишник воронки в амо*/ + Amoid int `json:"AmoID"` + /* - связь с аккаунтом в интеграции амо id аккаунта в амо*/ + AccountID int `json:"AccountID"` + /* - название воронки в амо*/ + Name string `json:"Name"` + /* - флаг архивной воронки в амо*/ + Isarchive bool `json:"IsArchive"` + /* - флаг мягкого удаления*/ + Deleted bool `json:"Deleted"` + /* - таймштамп создания воронки в нашей системе*/ + Createdat int64 `json:"CreatedAt"` + // время обновления + UpdateAt int64 `json:"UpdateAt"` +} + +type Step struct { + /* - айдишник в нашей системе Primary Key*/ + ID int `json:"ID"` + /* - айдишник шага воронки в амо*/ + Amoid int `json:"AmoID"` + /* - айдишник воронки в амо*/ + Pipelineid int `json:"PipelineID"` + /* - связь с аккаунтом в интеграции амо id в амо*/ + Accountid int `json:"AccountID"` + /* - название воронки в амо*/ + Name string `json:"Name"` + /* - цвет шага в амо*/ + Color string `json:"Color"` + /* - флаг мягкого удаления*/ + Deleted bool `json:"Deleted"` + /* - таймштамп создания воронки в нашей системе*/ + Createdat int64 `json:"CreatedAt"` + // время обновления + UpdateAt int64 `json:"UpdateAt"` +} + +type Tag struct { + /* - айдишник в нашей системе Primary Key*/ + ID int `json:"ID"` + /* - айдишник тега в амо*/ + Amoid int `json:"AmoID"` + /* - связь с аккаунтом в интеграции амо id аккаунта в амо*/ + Accountid int `json:"AccountID"` + /* - сущность, к которой принадлежит этот тег. Наверное, стоит сделать через enum в базе*/ + Entity EntityType `json:"Entity"` + /* - название тега в амо*/ + Name string `json:"Name"` + /* - цвет тега в амо*/ + Color *string `json:"Color"` + /* - флаг мягкого удаления*/ + Deleted bool `json:"Deleted"` + /* - таймштамп создания тега в нашей системе*/ + Createdat int64 `json:"CreatedAt"` + // время обновления + UpdateAt int64 `json:"UpdateAt"` +} + +type Field struct { + /* - айдишник в нашей системе Primary Key*/ + ID int `json:"ID"` + /* - айдишник кастомного поля в амо*/ + Amoid int `json:"AmoID"` + /* - кодовое слово в амо*/ + Code string `json:"Code"` + /* - связь с аккаунтом в интеграции амо id аккаунта в амо*/ + Accountid int `json:"AccountID"` + /* - название воронки в амо*/ + Name string `json:"Name"` + /* - тип сущности в амо, для которой это кастомное поле*/ + Entity EntityType `json:"Entity"` + /* - тип поля https://www.amocrm.ru/developers/content/crm_platform/custom-fields#%D0%94%D0%BE%D1%81%D1%82%D1%83%D0%BF%D0%BD%D1%8B%D0%B5-%D1%82%D0%B8%D0%BF%D1%8B-%D0%BF%D0%BE%D0%BB%D0%B5%D0%B9*/ + Type string `json:"Type"` + /* - флаг мягкого удаления*/ + Deleted bool `json:"Deleted"` + /* - таймштамп создания воронки в нашей системе*/ + Createdat int64 `json:"CreatedAt"` + // время обновления + UpdateAt int64 `json:"UpdateAt"` +} + +type EntityType string + +const ( + LeadsTags EntityType = "leads" + ContactsTags EntityType = "contacts" + CompaniesTags EntityType = "companies" + CustomersTags EntityType = "customers" +) + +type Rule struct { + /* - айдишник в нашей системе*/ + ID int `json:"ID"` + /* - связь с аккаунтом в интеграции амо id в амо*/ + Accountid int `json:"AccountID"` + /* - айдишник опроса*/ + Quizid int `json:"QuizID"` + /* - айдишник ответственного за сделку*/ + Performerid int `json:"PerformerID"` + /* - айдишник воронки*/ + Pipelineid int `json:"PipelineID"` + /* - айдишник этапа*/ + Stepid int `json:"StepID"` + /* - список UTM для этого опроса*/ + Utms []int `json:"UTMs"` + /* - правила заполнения полей сущностей в амо*/ + Fieldsrule Fieldsrule `json:"FieldsRule"` + /* - флаг мягкого удаления*/ + Deleted bool `json:"Deleted"` + /* - таймштамп создания воронки в нашей системе*/ + Createdat int `json:"CreatedAt"` +} + +type Fieldsrule struct { + Lead []FieldRule `json:"Lead"` + Contact []FieldRule `json:"Contact"` + Company []FieldRule `json:"Company"` + Customer []FieldRule `json:"Customer"` +} + +type FieldRule struct { + /* - сопоставление айдишника вопроса полю, которое будет заполняться ответом. соответственно QuestionID это айдишник вопроса. это я так мэпу пытался записать*/ + Questionid map[int]int `json:"QuestionID"` // ключ id вопроса значение id астомного поля +} + +type UTM struct { + /* - айдишник в нашей системе Primary Key*/ + ID int `json:"ID" bson:"ID"` + /* - айдишник кастомного поля в амо*/ + Amofieldid int `json:"AmoFieldID"` + /* - айдишник квиза*/ + Quizid int `json:"QuizID"` + /* - связь с аккаунтом в интеграции амо id амо*/ + Accountid int `json:"AccountID"` + /* - название тега в амо*/ + Name string `json:"Name"` + /* - флаг мягкого удаления*/ + Deleted bool `json:"Deleted"` + /* - таймштамп создания тега в нашей системе*/ + Createdat int64 `json:"CreatedAt"` +} diff --git a/model/amoReq.go b/model/amoReq.go new file mode 100644 index 0000000..859dfcc --- /dev/null +++ b/model/amoReq.go @@ -0,0 +1,23 @@ +package model + +type ListDeleteUTMIDsReq struct { + /* - список айдишников utm которые удалить*/ + Utms []string `json:"utms"` +} + +type PaginationReq struct { + /* - указание страницы пагинации. Если страница не указана, применять 0*/ + Page int `json:"page"` + /* - указание размера страницы пагинации. По умолчанию применять 25*/ + Size int `json:"size"` +} + +type RulesReq struct { + /* - ID квиза*/ + ID string `json:"ID"` +} + +type SaveUserListUTMReq struct { + /* - список utm для сохранения. сохранять только те, которых в этом аккаунте ещё нет*/ + Utms []UTM `json:"utms"` +} diff --git a/model/amoResp.go b/model/amoResp.go new file mode 100644 index 0000000..bcf81b2 --- /dev/null +++ b/model/amoResp.go @@ -0,0 +1,72 @@ +package model + +type ConnectAccountResp struct { + /* - ссылка для авторизации в амо*/ + Link string `json:"link"` +} + +type GetCurrentAccountResp struct { + /* - айдишник в нашей системе Primary Key*/ + ID int `json:"ID"` + /* - имя аккаунта в амо*/ + Name string `json:"Name"` + /* - поддомен организации в амо*/ + Subdomain string `json:"Subdomain"` + /* - id пользователя из токена в нашей системе*/ + Accountid string `json:"AccountID"` + /* - айдишник пользвателя, который подключал интеграцию*/ + Amouserid int `json:"AmoUserID"` + /* - связь с аккаунтом в амо*/ + Amocrmid int `json:"AmocrmID"` + /* - страна указанная в настройках амо*/ + Country string `json:"Country"` + /* - таймштамп создания аккаунта*/ + Createdat int64 `json:"CreatedAt"` +} + +type GetListUserUTMResp struct { + /* - общее количество юзеров, которые у нас закешированы для этого пользователя*/ + Count int `json:"count"` + /* - список юзеров, которые были закешированы нашим сервисом*/ + Items []UTM `json:"items"` +} + +type ListSavedIDUTMResp struct { + /* - список айдишников сохранённых меток*/ + Ids []string `json:"IDs"` +} + +type UserListFieldsResp struct { + /* - общее количество кастомных полей, которые у нас закешированы для этого пользователя*/ + Count int `json:"count"` + /* - список кастомных полей, которые были закешированы нашим сервисом*/ + Items []Field `json:"items"` +} + +type UserListPipelinesResp struct { + /* - общее количество воронок, которые у нас закешированы для этого пользователя*/ + Count int `json:"count"` + /* - список воронок, которые были закешированы нашим сервисом*/ + Items []Pipeline `json:"items"` +} + +type UserListResp struct { + /* - общее количество юзеров, которые у нас закешированы для этого пользователя*/ + Count int `json:"count"` + /* - список юзеров, которые были закешированы нашим сервисом*/ + Items []User `json:"items"` +} + +type UserListStepsResp struct { + /* - список шагов воронок, которые были закешированы нашим сервисом*/ + Items []Step `json:"items"` + /* - общее количество шагов воронок, которые у нас закешированы для этого пользователя*/ + Count int `json:"count"` +} + +type UserListTagsResp struct { + /* - общее количество тегов, которые у нас закешированы для этого пользователя*/ + Count int64 `json:"count"` + /* - список тегов, которые были закешированы нашим сервисом*/ + Items []Tag `json:"items"` +} diff --git a/repository/amo/amo.go b/repository/amo/amo.go new file mode 100644 index 0000000..ce39133 --- /dev/null +++ b/repository/amo/amo.go @@ -0,0 +1,255 @@ +package amo + +import ( + "context" + "database/sql" + "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal/sqlcgen" + "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model" +) + +type AmoRepository struct { + queries *sqlcgen.Queries + pool *sql.DB +} + +type Deps struct { + Queries *sqlcgen.Queries + Pool *sql.DB +} + +func NewAmoRepository(deps Deps) *AmoRepository { + return &AmoRepository{ + queries: deps.Queries, + pool: deps.Pool, + } +} + +// методы пользователя + +func (r *AmoRepository) UpdateListUsers(ctx context.Context) error { + //TODO:IMPLEMENT ME + + return nil + +} + +func (r *AmoRepository) GettingUserFromCash(ctx context.Context, req *model.PaginationReq) (*model.UserListResp, error) { + return nil, nil +} + +func (r *AmoRepository) SoftDeleteAccount(ctx context.Context, accountID string) error { + return nil +} + +func (r *AmoRepository) GetCurrentAccount(ctx context.Context, accountID string) (*model.User, error) { + return nil, nil +} + +func (r *AmoRepository) CreateAccount(ctx context.Context, accountID string) error { + return nil +} + +func (r *AmoRepository) UpdateAccount(ctx context.Context, accountID string, userInfo model.User) error { + return nil +} + +func (r *AmoRepository) CheckUsers(ctx context.Context, amouserid int, user model.User) error { + + return nil +} + +// методы webhook + +func (r *AmoRepository) WebhookCreate(ctx context.Context, tokens model.Token) error { + return nil +} + +func (r *AmoRepository) WebhookUpdate(ctx context.Context, tokens model.Token) error { + return nil +} + +// воркер запускается каждые 5 минут, поэтомму ищем токены котторые исекают менее чем через 10 минут отдаем их на обноление +func (r *AmoRepository) CheckExpired(ctx context.Context) ([]model.Token, error) { + return nil, nil +} + +func (r *AmoRepository) GetAllTokens(ctx context.Context) ([]model.Token, error) { + return nil, nil +} + +func (r *AmoRepository) WebhookDelete(ctx context.Context) error { + //TODO:IMPLEMENT ME + + return nil + +} + +// методы pipelines + +func (r *AmoRepository) UpdateListPipelines(ctx context.Context) error { + //TODO:IMPLEMENT ME + + return nil + +} + +func (r *AmoRepository) GettingPipelinesFromCash(ctx context.Context, req *model.PaginationReq) (*model.UserListPipelinesResp, error) { + return nil, nil +} + +func (r *AmoRepository) CheckPipelines(ctx context.Context, accountID string, pipelines []model.Pipeline) error { + return nil +} + +func (r *AmoRepository) GetPipelineByID(ctx context.Context, accountID string, amoid int) (*model.Pipeline, error) { + return nil, nil +} + +func (r *AmoRepository) UpdatePipeline(ctx context.Context, pipeline *model.Pipeline) error { + return nil +} + +func (r *AmoRepository) InsertPipeline(ctx context.Context, pipeline *model.Pipeline) error { + return nil +} + +// методы steps + +func (r *AmoRepository) GettingStepsFromCash(ctx context.Context, req *model.PaginationReq) (*model.UserListStepsResp, error) { + return nil, nil +} + +func (r *AmoRepository) UpdateListSteps(ctx context.Context) error { + //TODO:IMPLEMENT ME + + return nil + +} + +func (r *AmoRepository) CheckSteps(ctx context.Context, accountID string, steps []model.Step) error { + + return nil +} + +func (r *AmoRepository) GetStepByID(ctx context.Context, accountID string, amoid int) (*model.Step, error) { + return nil, nil +} + +func (r *AmoRepository) UpdateStep(ctx context.Context, step *model.Step) error { + return nil +} + +func (r *AmoRepository) InsertStep(ctx context.Context, step *model.Step) error { + return nil +} + +// методы tags + +func (r *AmoRepository) GettingTagsFromCash(ctx context.Context, req *model.PaginationReq) (*model.UserListTagsResp, error) { + return nil, nil +} + +func (r *AmoRepository) UpdateListTags(ctx context.Context) error { + //TODO:IMPLEMENT ME + + return nil + +} + +type CheckTagsDeps struct { + AccountID string // id quiz + ID int // id amo + EntityType model.EntityType + Tags []model.Tag +} + +func (r *AmoRepository) CheckTags(ctx context.Context, deps CheckTagsDeps) error { + + return nil +} + +func (r *AmoRepository) GetTagByID(ctx context.Context, accountID string, amoid int, entity model.EntityType) (*model.Tag, error) { + return nil, nil +} + +func (r *AmoRepository) UpdateTag(ctx context.Context, tag *model.Tag) error { + return nil +} + +func (r *AmoRepository) InsertTag(ctx context.Context, tag *model.Tag) error { + return nil +} + +// методы fields + +func (r *AmoRepository) GettingFieldsFromCash(ctx context.Context, req *model.PaginationReq) (*model.UserListFieldsResp, error) { + return nil, nil +} + +func (r *AmoRepository) UpdateListCustom(ctx context.Context) error { + //TODO:IMPLEMENT ME + + return nil + +} + +type CheckFieldsDeps struct { + AccountID string // id quiz + ID int // id amo + EntityType model.EntityType + Fields []model.Field +} + +func (r *AmoRepository) CheckFields(ctx context.Context, deps CheckFieldsDeps) error { + + return nil +} + +func (r *AmoRepository) GetFieldByID(ctx context.Context, accountID string, amoid int, entity model.EntityType) (*model.Field, error) { + return nil, nil +} + +func (r *AmoRepository) UpdateField(ctx context.Context, field *model.Field) error { + return nil +} + +func (r *AmoRepository) InsertField(ctx context.Context, field *model.Field) error { + return nil +} + +// методы rules + +func (r *AmoRepository) ChangeQuizSettings(ctx context.Context, request *model.RulesReq) error { + //TODO:IMPLEMENT ME + + return nil + +} + +func (r *AmoRepository) SetQuizSettings(ctx context.Context, request *model.RulesReq) error { + //TODO:IMPLEMENT ME + + return nil + +} + +func (r *AmoRepository) GettingQuizRules(ctx context.Context) (*model.Rule, error) { + //TODO:IMPLEMENT ME + + return &model.Rule{}, nil + +} + +// методы UTMs + +func (r *AmoRepository) DeletingUserUtm(ctx context.Context, request *model.ListDeleteUTMIDsReq) error { + return nil +} + +func (r *AmoRepository) SavingUserUtm(ctx context.Context, request *model.SaveUserListUTMReq, accountID string, quizID int) (*model.ListSavedIDUTMResp, error) { + return nil, nil +} + +func (r *AmoRepository) GettingUserUtm(ctx context.Context, request *model.PaginationReq, accountID string, quizID int) (*model.GetListUserUTMResp, error) { + return nil, nil +}