Merge remote-tracking branch 'origin/amoCRM'

This commit is contained in:
skeris 2024-05-13 19:25:48 +03:00
commit 024f47a56d
17 changed files with 3996 additions and 11 deletions

@ -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,65 @@ func (d *DAL) Init() error {
}
return nil
}
type AmoDal struct {
conn *sql.DB
queries *sqlcgen.Queries
AmoRepo *amo.AmoRepository
QuestionRepo *question.QuestionRepository
AnswerRepo *answer.AnswerRepository
QuizRepo *quiz.QuizRepository
}
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,
})
questionRepo := question.NewQuestionRepository(question.Deps{
Queries: queries,
Pool: pool,
})
answerRepo := answer.NewAnswerRepository(answer.Deps{
Queries: queries,
Pool: pool,
})
quizRepo := quiz.NewQuizRepository(quiz.Deps{
Queries: queries,
Pool: pool,
})
return &AmoDal{
conn: pool,
queries: queries,
AmoRepo: amoRepo,
QuestionRepo: questionRepo,
AnswerRepo: answerRepo,
QuizRepo: quizRepo,
}, nil
}
func (d *AmoDal) Close(ctx context.Context) error {
err := d.conn.Close()
if err != nil {
return err
}
return nil
}

@ -670,3 +670,424 @@ SELECT cq.id AS quiz_id, q.title, q.description, q.questiontype, q.required, q.d
FROM question q
JOIN quiz old ON q.quiz_id = old.id
JOIN copied_quiz cq ON old.qid = $2;
-- amo methods:
-- name: CreateAmoAccount :exec
INSERT INTO users (AccountID, AmoID, Name, Email, Role, "Group", Deleted, CreatedAt, Subdomain, AmoUserID, Country)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11);
-- name: CreateWebHook :exec
INSERT INTO tokens (AccountID, RefreshToken, AccessToken, AuthCode, Expiration, CreatedAt)
VALUES ($1, $2, $3, $4, $5, $6);
-- name: WebhookUpdate :exec
UPDATE tokens SET AccessToken = $1,RefreshToken = $2,Expiration = to_timestamp(($3)::bigint) AT TIME ZONE 'UTC' + INTERVAL '3 hours',CreatedAt = to_timestamp(($4)::bigint) AT TIME ZONE 'UTC' + INTERVAL '3 hours'
WHERE accountID = $5;
-- name: GetAllTokens :many
SELECT * FROM tokens;
-- name: CheckExpired :many
SELECT * FROM tokens WHERE Expiration <= TO_TIMESTAMP(EXTRACT(EPOCH FROM NOW()) + (10 * 60));
-- name: WebhookDelete :exec
WITH userd AS (
UPDATE users SET Deleted = true WHERE AmoUserID = $1 RETURNING AccountID
)
DELETE FROM tokens WHERE AccountID IN (SELECT AccountID FROM userd);
-- name: SoftDeleteAccount :exec
WITH userd AS (
SELECT AmoUserID FROM users WHERE users.AccountID = $1
),
tokend AS (
UPDATE users SET Deleted = true WHERE AmoUserID IN (SELECT AmoUserID FROM userd) RETURNING users.AccountID
)
DELETE FROM tokens WHERE tokens.AccountID IN (SELECT AccountID FROM tokend);
-- name: GetCurrentAccount :one
SELECT * FROM users WHERE AccountID = $1;
-- name: CheckMainUser :exec
UPDATE users SET Name = $1, "Group" = $2, Email = $3, Role = $4 WHERE AmoID = $5;
-- name: GetUsersWithPagination :many
WITH user_data AS (
SELECT AmoID FROM users WHERE users.AccountID = $1 AND Deleted = false
)
SELECT u.*, COUNT(*) OVER() as total_count
FROM users u
JOIN user_data a ON u.AmoUserID = a.AmoID
WHERE u.Deleted = false
ORDER BY u.ID OFFSET ($2 - 1) * $3 LIMIT $3;
-- name: GetTagsWithPagination :many
SELECT t.*, COUNT(*) OVER() as total_count
FROM tags t JOIN (SELECT AmoID FROM users WHERE users.AccountID = $1) u ON t.AccountID = u.AmoID
WHERE t.Deleted = false
ORDER BY t.ID OFFSET ($2 - 1) * $3 LIMIT $3;
-- name: GetStepsWithPagination :many
SELECT s.*, COUNT(*) OVER() as total_count
FROM steps s JOIN (SELECT AmoID FROM users WHERE users.AccountID = $1) u ON s.AccountID = u.AmoID
WHERE s.Deleted = false
ORDER BY s.ID OFFSET ($2 - 1) * $3 LIMIT $3;
-- name: GetPipelinesWithPagination :many
SELECT p.*, COUNT(*) OVER() as total_count
FROM pipelines p JOIN (SELECT AmoID FROM users WHERE users.AccountID = $1) u ON p.AccountID = u.AmoID
WHERE p.Deleted = false
ORDER BY p.ID OFFSET ($2 - 1) * $3 LIMIT $3;
-- name: GetFieldsWithPagination :many
SELECT f.*, COUNT(*) OVER() as total_count
FROM fields f JOIN (SELECT AmoID FROM users WHERE users.AccountID = $1) u ON f.AccountID = u.AmoID
WHERE f.Deleted = false
ORDER BY f.ID OFFSET ($2 - 1) * $3 LIMIT $3;
-- name: UpdateTags :exec
UPDATE tags AS t
SET name = (update_data ->> 'Name')::varchar(512),
color = (update_data ->> 'Color')::varchar(50)
FROM json_array_elements($1::json) AS update_data
WHERE t.amoID = (update_data ->> 'AmoID')::INT
AND t.accountID = (update_data ->> 'AccountID')::INT
AND t.Entity = (update_data ->> 'Entity')::entitytype;
-- name: UpdatePipelines :exec
UPDATE pipelines AS p
SET name = (update_data ->> 'Name')::varchar(512),
isArchive = CASE WHEN (update_data ->> 'IsArchive') = 'true' THEN TRUE ELSE FALSE END
FROM json_array_elements($1::json) AS update_data
WHERE p.amoID = (update_data ->> 'AmoID')::INT
AND p.accountID = (update_data ->> 'AccountID')::INT;
-- name: UpdateSteps :exec
UPDATE steps AS s
SET name = (update_data ->> 'Name')::varchar(512),
color = (update_data ->> 'Color')::varchar(50)
FROM json_array_elements($1::json) AS update_data
WHERE s.amoID = (update_data ->> 'AmoID')::INT
AND s.accountID = (update_data ->> 'AccountID')::INT
AND s.pipelineID = (update_data ->> 'PipelineID')::INT;
-- name: UpdateFields :exec
UPDATE fields AS f
SET name = (update_data ->> 'Name')::varchar(512),
code = (update_data ->> 'Code')::varchar(255),
type = (update_data ->> 'Type')::fieldtype
FROM json_array_elements($1::json) AS update_data
WHERE f.amoID = (update_data ->> 'AmoID')::INT
AND f.accountID = (update_data ->> 'AccountID')::INT
AND f.Entity = (update_data ->> 'Entity')::entitytype;
-- name: CheckTags :many
WITH user_data AS (
SELECT AmoID
FROM users
WHERE users.AccountID = $1
), new_tags AS (
SELECT (tag->>'AmoID')::INT AS amoID,
(tag->>'Entity')::entitytype AS Entity,
COALESCE(tag->>'Name', '')::VARCHAR(512) AS name,
COALESCE(tag->>'Color', '')::VARCHAR(50) AS color
FROM json_array_elements($2::json) AS tag
), inserted_tags AS (
INSERT INTO tags (amoID, accountID, Entity, name, color, createdAt)
SELECT nt.amoID,
ud.AmoID,
nt.Entity,
nt.name,
nt.color,
CURRENT_TIMESTAMP
FROM new_tags nt
JOIN user_data ud ON true
ON CONFLICT (amoID, accountID, Entity) DO NOTHING
RETURNING *
)
SELECT nt.*,ud.AmoID
FROM new_tags nt
JOIN user_data ud ON true
WHERE NOT EXISTS (
SELECT *
FROM inserted_tags ins
JOIN user_data ud ON true
WHERE ins.amoID = nt.amoID AND ins.accountID = ud.amoid AND ins.Entity = nt.Entity
);
-- name: CheckPipelines :many
WITH new_pipelines AS (
SELECT (pipeline->>'AmoID')::INT AS amoID,
(pipeline->>'AccountID')::INT AS accountID,
COALESCE(pipeline->>'Name', '')::varchar(512) AS name,
CASE WHEN (pipeline->>'IsArchive') = 'true' THEN TRUE ELSE FALSE END AS isArchive,
CURRENT_TIMESTAMP AS createdAt
FROM json_array_elements($1::json) AS pipeline
), inserted_pipelines AS(
INSERT INTO pipelines (amoID, accountID, name, isArchive, createdAt)
SELECT np.amoID,
np.accountID,
np.name,
np.isArchive,
np.createdAt
FROM new_pipelines np
ON CONFLICT (amoID, accountID) DO NOTHING
RETURNING *
)
SELECT np.*
FROM new_pipelines np
WHERE NOT EXISTS (
SELECT *
FROM inserted_pipelines ins
WHERE ins.amoID = np.amoID AND ins.accountID = np.accountID
);
-- name: CheckFields :many
WITH user_data AS (
SELECT AmoID
FROM users
WHERE users.AccountID = $1
), new_fields AS (
SELECT (field->>'AmoID')::INT AS amoID,
COALESCE(field->>'Code', '')::varchar(255) AS code,
COALESCE(field->>'Name', '')::varchar(512) AS name,
CAST(field->>'Entity' AS entitytype) AS Entity,
COALESCE(field->>'Type', '')::fieldtype AS type,
CURRENT_TIMESTAMP AS createdAt
FROM json_array_elements($2::json) AS field
), inserted_fields AS(
INSERT INTO fields (amoID, code, accountID, name, Entity, type, createdAt)
SELECT nf.amoID,
nf.code,
ud.AmoID,
nf.name,
nf.Entity,
nf.type,
nf.createdAt
FROM new_fields nf
JOIN user_data ud ON true
ON CONFLICT (amoID, accountID, entity) DO NOTHING
RETURNING *
)
SELECT nf.*,ud.AmoID
FROM new_fields nf
JOIN user_data ud ON true
WHERE NOT EXISTS (
SELECT *
FROM inserted_fields ins
JOIN user_data ud ON true
WHERE ins.amoID = nf.amoID AND ins.accountID = ud.amoid AND ins.Entity = nf.Entity
);
-- name: CheckSteps :many
WITH new_steps AS (
SELECT (step->>'AmoID')::INT AS amoID,
(step->>'PipelineID')::INT AS pipelineID,
(step->>'AccountID')::INT AS accountID,
COALESCE(step->>'Name', '')::varchar(512) AS name,
COALESCE(step->>'Color', '')::varchar(50) AS color,
CURRENT_TIMESTAMP AS createdAt
FROM json_array_elements($1::json) AS step
), inserted_steps AS (
INSERT INTO steps (amoID, pipelineID, accountID, name, color, createdAt)
SELECT ns.amoID,
ns.pipelineID,
ns.accountID,
ns.name,
ns.color,
ns.createdAt
FROM new_steps ns
ON CONFLICT (amoID, accountID, PipelineID) DO NOTHING
RETURNING *
)
SELECT ns.*
FROM new_steps ns
WHERE NOT EXISTS (
SELECT *
FROM inserted_steps ins
WHERE ins.amoID = ns.amoID AND ins.accountID = ns.accountID AND ins.pipelineID = ns.pipelineID
);
-- name: GetTokenById :one
SELECT * FROM tokens WHERE accountID = $1;
-- name: DeletingUTM :exec
UPDATE utms SET Deleted = true WHERE ID = ANY($1::int[]);
-- name: GetUTMsWithPagination :many
SELECT ut.*, COUNT(*) OVER() as total_count
FROM utms ut JOIN (SELECT AmoID FROM users WHERE users.AccountID = $1) u ON ut.AccountID = u.AmoID
WHERE ut.Deleted = false and ut.QuizID = $4
ORDER BY ut.ID OFFSET ($2 - 1) * $3 LIMIT $3;
-- name: SaveUTMs :many
WITH user_data AS (
SELECT AmoID
FROM users
WHERE users.AccountID = $1
), new_UTMs AS (
SELECT (utm->>'AmoFieldID')::INT AS amoFieldID,
COALESCE(utm->>'QuizID', '')::INT AS quizID,
COALESCE(utm->>'Name', '')::varchar(512) AS name,
CURRENT_TIMESTAMP AS createdAt
FROM json_array_elements($2::json) AS utm
), inserted_utms AS(
INSERT INTO utms (AmoFieldID, QuizID, AccountID, Name, createdAt)
SELECT nu.amoFieldID,
nu.quizID,
ud.AmoID,
nu.name,
nu.createdAt
FROM new_UTMs nu
JOIN user_data ud ON true
RETURNING *
)
SELECT * from inserted_utms;
-- name: GetQuizRule :one
SELECT * FROM rules WHERE QuizID = $1 AND Deleted = false;
-- name: SetQuizSettings :exec
INSERT INTO rules (AccountID, QuizID, PerformerID, PipelineID, StepID, UTMS, FieldsRule)
SELECT u.AmoID AS AccountID,$1 AS QuizID,$2 AS PerformerID,$3 AS PipelineID,
$4 AS StepID,$5 AS UTMS,$6 AS FieldsRule FROM users u WHERE u.AccountID = $7;
-- name: ChangeQuizSettings :exec
UPDATE rules
SET PerformerID = $1,PipelineID = $2,StepID = $3,UTMS = $4,FieldsRule = $5
WHERE AccountID = (SELECT AmoID FROM users WHERE users.AccountID = $6) AND QuizID = $7 AND Deleted = false;
-- name: GetUtmsByID :many
SELECT ID,AmoFieldID,QuizID,AccountID,Name
FROM utms
WHERE
ID = ANY($1::int[]) AND Deleted = FALSE;
-- name: GetUserFieldsByID :many
SELECT ID,AmoID,Code,AccountID,Name,Entity,Type
FROM fields
WHERE AccountID = $1 AND Deleted = false;
-- name: UpdateUtms :exec
UPDATE utms AS u
SET name = (update_data ->> 'Name')::varchar(512),
AmoFieldID = (update_data ->> 'AmoFieldID')::INT
FROM json_array_elements($1::json) AS update_data
WHERE u.ID = (update_data ->> 'ID')::INT;
-- name: UpdateUtmsFields :exec
UPDATE utms AS u SET AmoFieldID = f.AmoID FROM fields AS f
WHERE u.Name = f.Name AND u.ID = ANY($1::int[]) AND u.Deleted = FALSE;
-- name: GetQuestionListByIDs :many
SELECT * FROM question WHERE id = ANY($1::int[]) AND deleted = FALSE;
-- name: UpdateFieldRules :exec
UPDATE rules SET FieldsRule = $1
WHERE AccountID = (SELECT AmoID FROM users WHERE users.AccountID = $2) AND QuizID = $3 AND Deleted = false;
-- name: UpdateUsers :exec
UPDATE users AS u
SET Name = (update_data ->> 'Name')::varchar(512),
Email = (update_data ->> 'Email')::varchar(50),
Role = (update_data ->> 'Role')::INT,
"Group" = (update_data ->> 'Group')::INT,
AmoUserID= (update_data ->> 'AmoUserID')::INT
FROM json_array_elements($1::json) AS update_data
WHERE u.AmoID = (update_data ->> 'AmocrmID')::INT;
-- name: CheckUsers :many
WITH new_users AS (
SELECT (u->>'AmocrmID')::INT AS AmoID,
(u->>'Name')::VARCHAR(512) AS Name,
(u->>'Group')::INT AS "Group",
(u->>'Role')::INT AS Role,
(u->>'Email')::VARCHAR(50) AS Email,
(u->>'AmoUserID')::INT AS AmoUserID,
CURRENT_TIMESTAMP AS createdAt
FROM json_array_elements($1::json) AS u
), inserted_users AS (
INSERT INTO users (AmoID, Name, "Group", Role, Email, AmoUserID,createdAt)
SELECT nu.AmoID,
nu.Name,
nu."Group",
nu.Role,
nu.Email,
nu.AmoUserID,
nu.createdAt
FROM new_users nu
ON CONFLICT (amoID) DO NOTHING
RETURNING *
)
SELECT nu.*
FROM new_users nu
WHERE NOT EXISTS (
SELECT *
FROM inserted_users ins
WHERE ins.amoID = nu.amoID
);
-- name: GettingAmoUsersTrueResults :many
SELECT a.quiz_id,a.id,a.result,a.question_id,a.content,a.session,t.accesstoken,r.accountid,r.utms,r.fieldsrule,r.performerid,r.stepid,r.pipelineid,(SELECT u.name FROM users u WHERE u.amoid = r.performerid) AS performer_name
FROM answer a
INNER JOIN quiz q ON a.quiz_id = q.id
LEFT JOIN amoCRMStatuses s ON a.id = s.AnswerID
INNER JOIN rules r ON q.id = r.QuizID
INNER JOIN tokens t ON q.accountid = t.AccountID
INNER JOIN users u ON q.accountid = u.accountid AND u.amoid = r.accountid
WHERE a.result = true
AND s.id IS NULL
AND a.deleted = false
AND r.deleted = false
AND q.deleted = false;
-- name: SettingDealAmoStatus :exec
INSERT INTO amoCRMStatuses (AccountID, DealID, AnswerID, Status)
SELECT u.AmoID, $1, $2, $3
FROM tokens AS t
JOIN users AS u ON t.AccountID = u.AccountID
WHERE t.AccessToken = $4;
-- name: UpdatingDealAmoStatus :exec
UPDATE amoCRMStatuses SET Status = $1
WHERE DealID = $2 AND AccountID = (SELECT u.AmoID FROM tokens AS t JOIN users AS u ON t.AccountID = u.AccountID WHERE t.AccessToken = $3);
-- name: DeleteFields :exec
UPDATE fields SET Deleted = true WHERE ID = ANY($1::bigint[]);
-- name: DeleteTags :exec
UPDATE tags SET Deleted = true WHERE ID = ANY($1::bigint[]);
-- name: DeleteSteps :exec
UPDATE steps SET Deleted = true WHERE ID = ANY($1::bigint[]);
-- name: DeletePipelines :exec
UPDATE pipelines SET Deleted = true WHERE ID = ANY($1::bigint[]);
-- name: DeleteUsers :exec
UPDATE users SET Deleted = true WHERE ID = ANY($1::bigint[]);
-- name: GetUserTagsByID :many
SELECT ID,AmoID,AccountID,Name,Entity,Color
FROM tags
WHERE AccountID = $1 AND Deleted = false;
-- name: GetUserStepsByID :many
SELECT ID,AmoID,PipelineID,AccountID,Name,Color
FROM steps
WHERE AccountID = $1 AND Deleted = false;
-- name: GetUserPipelinesByID :many
SELECT ID,AmoID,AccountID,Name,IsArchive
FROM pipelines
WHERE AccountID = $1 AND Deleted = false;
-- name: GetUserUsersByID :many
SELECT ID,AccountID,AmoID,Name,Email,Role,"Group",Subdomain,AmoUserID,Country
FROM users
WHERE AmoUserID = $1 AND Deleted = false;
-- name: GetFieldByAmoID :one
SELECT * FROM fields WHERE AmoID = $1 AND Deleted = false;

@ -0,0 +1,31 @@
DROP INDEX IF EXISTS idx_unique_users;
DROP INDEX IF EXISTS idx_unique_pipeline;
DROP INDEX IF EXISTS idx_unique_step;
DROP INDEX IF EXISTS idx_unique_field;
DROP INDEX IF EXISTS idx_unique_tag;
DROP INDEX IF EXISTS idx_unique_rules;
DROP TABLE IF EXISTS amoCRMStatuses;
DROP TABLE IF EXISTS rules;
DROP TABLE IF EXISTS utms;
DROP TABLE IF EXISTS tags;
DROP TABLE IF EXISTS fields;
DO $$
BEGIN
IF EXISTS (SELECT 1 FROM pg_type WHERE typname = 'entitytype') THEN
DROP TYPE EntityType;
END IF;
END $$;
DO $$
BEGIN
IF EXISTS (SELECT 1 FROM pg_type WHERE typname = 'fieldtype') THEN
DROP TYPE FieldType;
END IF;
END $$;
DROP TABLE IF EXISTS steps;
DROP TABLE IF EXISTS pipelines;
DROP TABLE IF EXISTS tokens;
DROP TABLE IF EXISTS users;

@ -0,0 +1,119 @@
CREATE TABLE IF NOT EXISTS tokens (
AccountID VARCHAR(30) PRIMARY KEY, -- связь с users AccountID неявная посредством join
RefreshToken TEXT NOT NULL ,
AccessToken TEXT NOT NULL ,
AuthCode TEXT NOT NULL , -- код авторизации который получаем при вебхук
Expiration TIMESTAMP NOT NULL, -- время истечения токенов
CreatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS users (
ID BIGSERIAL UNIQUE NOT NULL PRIMARY KEY,
AccountID VARCHAR(30) NOT NULL DEFAULT '', -- id квизе из токена
AmoID INT NOT NULL , -- id в амо
Name VARCHAR(512) NOT NULL DEFAULT '', -- имя в амо
Email VARCHAR(50) NOT NULL DEFAULT '', -- почта в амо
Role INT NOT NULL DEFAULT 0, -- роль в амо
"Group" INT NOT NULL DEFAULT 0, -- вложенная структура так как в амо группы хранятся массивом структур
Deleted BOOLEAN NOT NULL DEFAULT FALSE,
CreatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
Subdomain VARCHAR(50) NOT NULL DEFAULT '',
AmoUserID INT NOT NULL , -- id пользователя который подключал интеграцию
Country VARCHAR(50) NOT NULL DEFAULT '' -- страна в амо
);
CREATE TABLE IF NOT EXISTS pipelines (
ID BIGSERIAL UNIQUE NOT NULL PRIMARY KEY,
AmoID INT NOT NULL , --id воронки в амо
AccountID INT NOT NULL , --id аккаунта в амо связь с таблицей users AmoID неявная посредством join
Name VARCHAR(512) NOT NULL DEFAULT '', --название воронки в амо
IsArchive BOOLEAN NOT NULL DEFAULT FALSE, --флаг архивной воронки в амо
Deleted BOOLEAN NOT NULL DEFAULT FALSE,
CreatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS steps (
ID BIGSERIAL UNIQUE NOT NULL PRIMARY KEY,
AmoID INT NOT NULL, --id шага воронки в амо
PipelineID INT NOT NULL, --id воронки AmoID pipelines неявная посредством join
AccountID INT NOT NULL, --id аккаунта в амо связь с таблицей users AmoID неявная посредством join
Name VARCHAR(512) NOT NULL DEFAULT '', --название воронки в амо
Color VARCHAR(50) NOT NULL DEFAULT '', --цвет шага в амо*
Deleted BOOLEAN NOT NULL DEFAULT FALSE,
CreatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'entitytype') THEN
CREATE TYPE EntityType AS ENUM ('leads', 'contacts', 'companies', 'customers');
END IF;
END $$;
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'fieldtype') THEN
CREATE TYPE FieldType AS ENUM ('text', 'numeric', 'checkbox', 'select', 'multiselect', 'date', 'url', 'textarea', 'radiobutton', 'streetaddress', 'smart_address', 'birthday', 'legal_entity', 'date_time', 'price', 'category', 'items', 'tracking_data', 'linked_entity', 'chained_list', 'monetary', 'file', 'payer', 'supplier', 'multitext');
END IF;
END $$;
CREATE TABLE IF NOT EXISTS fields (
ID BIGSERIAL UNIQUE NOT NULL PRIMARY KEY,
AmoID INT NOT NULL, -- айдишник кастомного поля в амо
Code VARCHAR(255) NOT NULL DEFAULT '', -- кодовое слово в амо
AccountID INT NOT NULL, -- id аккаунта в амо связь с таблицей users AmoID неявная посредством join
Name VARCHAR(512) NOT NULL DEFAULT '', -- название воронки в амо
Entity EntityType NOT NULL, -- тип сущности в амо, для которой это кастомное поле
Type FieldType NOT NULL, -- тип поля
Deleted BOOLEAN NOT NULL DEFAULT FALSE,
CreatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS tags (
ID BIGSERIAL UNIQUE NOT NULL PRIMARY KEY,
AmoID INT NOT NULL, -- айдишник тега в амо
AccountID INT NOT NULL, -- id аккаунта в амо связь с таблицей users AmoID неявная посредством join
Entity EntityType NOT NULL, -- сущность, к которой принадлежит этот тег
Name VARCHAR(512) NOT NULL DEFAULT '', -- название тега в амо
Color VARCHAR(50) NOT NULL DEFAULT '', -- цвет тега в амо
Deleted BOOLEAN NOT NULL DEFAULT FALSE,
CreatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS utms (
ID BIGSERIAL UNIQUE NOT NULL PRIMARY KEY,
AmoFieldID INT NOT NULL DEFAULT 0, -- id field в амо
QuizID INT NOT NULL, -- id опроса
AccountID INT NOT NULL, -- id аккаунта в амо AMOID
Name VARCHAR(512) NOT NULL DEFAULT '', -- название utm
Deleted BOOLEAN NOT NULL DEFAULT FALSE,
CreatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS rules (
ID BIGSERIAL UNIQUE NOT NULL PRIMARY KEY,
AccountID INT NOT NULL, -- id аккаунта в амо AMOID
QuizID INT NOT NULL, -- id опроса
PerformerID INT NOT NULL, -- айдишник ответственного за сделку
PipelineID INT NOT NULL, --id воронки AmoID pipelines неявная посредством join
StepID INT NOT NULL , -- id этапа steps AmoID join
UTMS INTEGER[], -- список UTM для этого опроса id utm
FieldsRule JSONB NOT NULL DEFAULT '{}', -- вложенная структура
Deleted BOOLEAN NOT NULL DEFAULT FALSE,
CreatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE UNIQUE INDEX idx_unique_users ON users (amoID);
CREATE UNIQUE INDEX idx_unique_pipeline ON pipelines (amoID, accountID);
CREATE UNIQUE INDEX idx_unique_step ON steps (amoID, accountID, PipelineID);
CREATE UNIQUE INDEX idx_unique_field ON fields (amoID, accountID, entity);
CREATE UNIQUE INDEX idx_unique_tag ON tags (amoID, accountID, entity);
CREATE UNIQUE INDEX idx_unique_rules ON rules (accountID, QuizID);
CREATE TABLE IF NOT EXISTS amoCRMStatuses (
ID BIGSERIAL UNIQUE NOT NULL PRIMARY KEY,
AccountID INT NOT NULL, -- id аккаунта в амо
DealID INT NOT NULL, -- id сделки в которую добавлялось
AnswerID BIGINT NOT NULL REFERENCES answer(id), -- id true result который вызвал действие
Status TEXT NOT NULL DEFAULT '', -- запись о ошибке, либо успехе
CreatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.26.0
// sqlc v1.25.0
package sqlcgen

@ -1,11 +1,13 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.26.0
// sqlc v1.25.0
package sqlcgen
import (
"database/sql"
"encoding/json"
"time"
"github.com/google/uuid"
)
@ -18,6 +20,15 @@ type Account struct {
Deleted sql.NullBool `db:"deleted" json:"deleted"`
}
type Amocrmstatus struct {
ID int64 `db:"id" json:"id"`
Accountid int32 `db:"accountid" json:"accountid"`
Dealid int32 `db:"dealid" json:"dealid"`
Answerid int64 `db:"answerid" json:"answerid"`
Status string `db:"status" json:"status"`
Createdat sql.NullTime `db:"createdat" json:"createdat"`
}
type Answer struct {
ID int64 `db:"id" json:"id"`
Content sql.NullString `db:"content" json:"content"`
@ -38,6 +49,28 @@ type Answer struct {
Start bool `db:"start" json:"start"`
}
type Field struct {
ID int64 `db:"id" json:"id"`
Amoid int32 `db:"amoid" json:"amoid"`
Code string `db:"code" json:"code"`
Accountid int32 `db:"accountid" json:"accountid"`
Name string `db:"name" json:"name"`
Entity interface{} `db:"entity" json:"entity"`
Type interface{} `db:"type" json:"type"`
Deleted bool `db:"deleted" json:"deleted"`
Createdat sql.NullTime `db:"createdat" json:"createdat"`
}
type Pipeline struct {
ID int64 `db:"id" json:"id"`
Amoid int32 `db:"amoid" json:"amoid"`
Accountid int32 `db:"accountid" json:"accountid"`
Name string `db:"name" json:"name"`
Isarchive bool `db:"isarchive" json:"isarchive"`
Deleted bool `db:"deleted" json:"deleted"`
Createdat sql.NullTime `db:"createdat" json:"createdat"`
}
type Privilege struct {
ID int32 `db:"id" json:"id"`
Privilegeid sql.NullString `db:"privilegeid" json:"privilegeid"`
@ -94,3 +127,72 @@ type Quiz struct {
AverageTimePassing sql.NullInt32 `db:"average_time_passing" json:"average_time_passing"`
SessionsCount sql.NullInt32 `db:"sessions_count" json:"sessions_count"`
}
type Rule struct {
ID int64 `db:"id" json:"id"`
Accountid int32 `db:"accountid" json:"accountid"`
Quizid int32 `db:"quizid" json:"quizid"`
Performerid int32 `db:"performerid" json:"performerid"`
Pipelineid int32 `db:"pipelineid" json:"pipelineid"`
Stepid int32 `db:"stepid" json:"stepid"`
Utms []int32 `db:"utms" json:"utms"`
Fieldsrule json.RawMessage `db:"fieldsrule" json:"fieldsrule"`
Deleted bool `db:"deleted" json:"deleted"`
Createdat sql.NullTime `db:"createdat" json:"createdat"`
}
type Step struct {
ID int64 `db:"id" json:"id"`
Amoid int32 `db:"amoid" json:"amoid"`
Pipelineid int32 `db:"pipelineid" json:"pipelineid"`
Accountid int32 `db:"accountid" json:"accountid"`
Name string `db:"name" json:"name"`
Color string `db:"color" json:"color"`
Deleted bool `db:"deleted" json:"deleted"`
Createdat sql.NullTime `db:"createdat" json:"createdat"`
}
type Tag struct {
ID int64 `db:"id" json:"id"`
Amoid int32 `db:"amoid" json:"amoid"`
Accountid int32 `db:"accountid" json:"accountid"`
Entity interface{} `db:"entity" json:"entity"`
Name string `db:"name" json:"name"`
Color string `db:"color" json:"color"`
Deleted bool `db:"deleted" json:"deleted"`
Createdat sql.NullTime `db:"createdat" json:"createdat"`
}
type Token struct {
Accountid string `db:"accountid" json:"accountid"`
Refreshtoken string `db:"refreshtoken" json:"refreshtoken"`
Accesstoken string `db:"accesstoken" json:"accesstoken"`
Authcode string `db:"authcode" json:"authcode"`
Expiration time.Time `db:"expiration" json:"expiration"`
Createdat sql.NullTime `db:"createdat" json:"createdat"`
}
type User struct {
ID int64 `db:"id" json:"id"`
Accountid string `db:"accountid" json:"accountid"`
Amoid int32 `db:"amoid" json:"amoid"`
Name string `db:"name" json:"name"`
Email string `db:"email" json:"email"`
Role int32 `db:"role" json:"role"`
Group int32 `db:"Group" json:"Group"`
Deleted bool `db:"deleted" json:"deleted"`
Createdat sql.NullTime `db:"createdat" json:"createdat"`
Subdomain string `db:"subdomain" json:"subdomain"`
Amouserid int32 `db:"amouserid" json:"amouserid"`
Country string `db:"country" json:"country"`
}
type Utm struct {
ID int64 `db:"id" json:"id"`
Amofieldid int32 `db:"amofieldid" json:"amofieldid"`
Quizid int32 `db:"quizid" json:"quizid"`
Accountid int32 `db:"accountid" json:"accountid"`
Name string `db:"name" json:"name"`
Deleted bool `db:"deleted" json:"deleted"`
Createdat sql.NullTime `db:"createdat" json:"createdat"`
}

File diff suppressed because it is too large Load Diff

10
go.mod

@ -10,7 +10,7 @@ require (
github.com/google/uuid v1.6.0
github.com/lib/pq v1.10.9
github.com/rs/xid v1.5.0
google.golang.org/protobuf v1.32.0
google.golang.org/protobuf v1.33.0
penahub.gitlab.yandexcloud.net/backend/penahub_common v0.0.0-20240202120244-c4ef330cfe5d
penahub.gitlab.yandexcloud.net/backend/quiz/core.git v0.0.0-20240219174804-d78fd38511af
)
@ -18,7 +18,7 @@ require (
require (
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/json-iterator/go v1.1.12 // indirect
@ -33,11 +33,13 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/sqlc-dev/pqtype v0.3.0 // indirect
github.com/sqlc-dev/sqlc v1.26.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.51.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
golang.org/x/crypto v0.19.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
golang.org/x/crypto v0.20.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect

9
go.sum

@ -33,6 +33,7 @@ github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@ -86,6 +87,10 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/sqlc-dev/pqtype v0.3.0 h1:b09TewZ3cSnO5+M1Kqq05y0+OjqIptxELaSayg7bmqk=
github.com/sqlc-dev/pqtype v0.3.0/go.mod h1:oyUjp5981ctiL9UYvj1bVvCKi8OXkCa0u645hce7CAs=
github.com/sqlc-dev/sqlc v1.26.0 h1:bW6TA1vVdi2lfqsEddN5tSznRMYcWez7hf+AOqSiEp8=
github.com/sqlc-dev/sqlc v1.26.0/go.mod h1:k2F3RWilLCup3D0XufrzZENCyXjtplALmHDmOt4v5bs=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
@ -98,8 +103,11 @@ github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVS
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg=
golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ=
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
@ -122,6 +130,7 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

@ -36,6 +36,7 @@ func AnswererChain() fiber.Handler {
func JWTAuth() fiber.Handler {
return func(c *fiber.Ctx) error {
//todo также сделать для хуков на добавление удаление в амо
if c.Path() == "/quiz/logo" {
return c.Next()
}

277
model/amo.go Normal file

@ -0,0 +1,277 @@
package model
type User struct {
/* - айдишник в нашей системе Primary Key*/
ID int64 `json:"ID"`
/* - id пользователя из токена в нашей системе*/
Accountid string `json:"AccountID"`
/* - айдишник пользователя в амо*/
AmoID int32 `json:"AmocrmID"`
/* - имя аккаунта в амо*/
Name string `json:"Name"`
/* - почта пользователя из амо*/
Email string `json:"Email"`
/* - роль пользователя в амо*/
Role int32 `json:"Role"`
/* - группы пользователя в амо*/
Group int32 `json:"Group"`
/* - флаг мягкого удаления*/
Deleted bool `json:"Deleted"`
/* - таймштамп создания аккаунта*/
Createdat int64 `json:"CreatedAt"`
/* - поддомен организации в амо*/
Subdomain string `json:"Subdomain"`
/* - айдишник пользвателя, который подключал интеграцию*/
Amouserid int32 `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 int64 `json:"ID"`
/* - айдишник воронки в амо*/
Amoid int32 `json:"AmoID"`
/* - связь с аккаунтом в интеграции амо id аккаунта в амо*/
AccountID int32 `json:"AccountID"`
/* - название воронки в амо*/
Name string `json:"Name"`
/* - флаг архивной воронки в амо*/
Isarchive bool `json:"IsArchive"`
/* - флаг мягкого удаления*/
Deleted bool `json:"Deleted"`
/* - таймштамп создания воронки в нашей системе*/
Createdat int64 `json:"CreatedAt"`
}
type Step struct {
/* - айдишник в нашей системе Primary Key*/
ID int64 `json:"ID"`
/* - айдишник шага воронки в амо*/
Amoid int32 `json:"AmoID"`
/* - айдишник воронки в амо*/
Pipelineid int32 `json:"PipelineID"`
/* - связь с аккаунтом в интеграции амо id в амо*/
Accountid int32 `json:"AccountID"`
/* - название воронки в амо*/
Name string `json:"Name"`
/* - цвет шага в амо*/
Color string `json:"Color"`
/* - флаг мягкого удаления*/
Deleted bool `json:"Deleted"`
/* - таймштамп создания воронки в нашей системе*/
Createdat int64 `json:"CreatedAt"`
}
type Tag struct {
/* - айдишник в нашей системе Primary Key*/
ID int64 `json:"ID"`
/* - айдишник тега в амо*/
Amoid int32 `json:"AmoID"`
/* - связь с аккаунтом в интеграции амо id аккаунта в амо*/
Accountid int32 `json:"AccountID"`
/* - сущность, к которой принадлежит этот тег. Наверное, стоит сделать через enum в базе*/
Entity EntityType `json:"Entity"`
/* - название тега в амо*/
Name string `json:"Name"`
/* - цвет тега в амо*/
Color *string `json:"Color"`
/* - флаг мягкого удаления*/
Deleted bool `json:"Deleted"`
/* - таймштамп создания тега в нашей системе*/
Createdat int64 `json:"CreatedAt"`
}
type Field struct {
/* - айдишник в нашей системе Primary Key*/
ID int64 `json:"ID"`
/* - айдишник кастомного поля в амо*/
Amoid int32 `json:"AmoID"`
/* - кодовое слово в амо*/
Code string `json:"Code"`
/* - связь с аккаунтом в интеграции амо id аккаунта в амо*/
Accountid int32 `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 FieldType `json:"Type"`
/* - флаг мягкого удаления*/
Deleted bool `json:"Deleted"`
/* - таймштамп создания воронки в нашей системе*/
Createdat int64 `json:"CreatedAt"`
}
type EntityType string
const (
LeadsType EntityType = "leads"
ContactsType EntityType = "contacts"
CompaniesType EntityType = "companies"
CustomersType EntityType = "customers"
)
type Rule struct {
/* - айдишник в нашей системе*/
ID int64 `json:"ID"`
/* - связь с аккаунтом в интеграции амо id в амо*/
Accountid int32 `json:"AccountID"`
/* - айдишник опроса*/
Quizid int32 `json:"QuizID"`
/* - айдишник ответственного за сделку*/
Performerid int32 `json:"PerformerID"`
/* - айдишник воронки*/
Pipelineid int32 `json:"PipelineID"`
/* - айдишник этапа*/
Stepid int32 `json:"StepID"`
/* - список UTM для этого опроса*/
Utms []int32 `json:"UTMs"`
/* - правила заполнения полей сущностей в амо*/
Fieldsrule Fieldsrule `json:"FieldsRule"`
/* - флаг мягкого удаления*/
Deleted bool `json:"Deleted"`
/* - таймштамп создания воронки в нашей системе*/
Createdat int64 `json:"CreatedAt"`
}
type Fieldsrule struct {
Lead []FieldRule `json:"Lead"`
Contact ContactRules `json:"Contact"`
Company []FieldRule `json:"Company"`
Customer []FieldRule `json:"Customer"`
}
type FieldRule struct {
/* - сопоставление айдишника вопроса полю, которое будет заполняться ответом. соответственно QuestionID это айдишник вопроса. это я так мэпу пытался записать*/
Questionid map[int]int `json:"QuestionID"` // ключ id вопроса значение id астомного поля
}
type ContactRules struct {
// ключ имя, значение id кастомного поля
ContactRuleMap map[string]int
}
type QuizContact struct {
FormContact struct {
Fields struct {
Name ContactField `json:"name"`
Email ContactField `json:"email"`
Phone ContactField `json:"phone"`
Text ContactField `json:"text"`
Address ContactField `json:"address"`
} `json:"fields"`
} `json:"formContact"`
}
type ContactField struct {
Text string `json:"text"`
InnerText string `json:"innerText"`
Key string `json:"key"`
Required bool `json:"required"`
Used bool `json:"used"`
}
type ContactQuizConfig string
const (
TypeContactName ContactQuizConfig = "name"
TypeContactEmail ContactQuizConfig = "email"
TypeContactPhone ContactQuizConfig = "phone"
TypeContactText ContactQuizConfig = "text"
TypeContactAddress ContactQuizConfig = "address"
)
type UTM struct {
/* - айдишник в нашей системе Primary Key*/
ID int64 `json:"ID"`
/* - айдишник кастомного поля в амо*/
Amofieldid int32 `json:"AmoFieldID"`
/* - айдишник квиза*/
Quizid int32 `json:"QuizID"`
/* - связь с аккаунтом в интеграции амо id амо*/
Accountid int32 `json:"AccountID"`
/* - название тега в амо*/
Name string `json:"Name"`
/* - флаг мягкого удаления*/
Deleted bool `json:"Deleted"`
/* - таймштамп создания тега в нашей системе*/
Createdat int64 `json:"CreatedAt"`
}
type FieldType string
const (
TypeAmoText FieldType = "text" //Текст
TypeAmoNumeric FieldType = "numeric" //Число
TypeAmoCheckbox FieldType = "checkbox" //Флаг
TypeAmoSelect FieldType = "select" //Список
TypeAmoMultiselect FieldType = "multiselect" //Мультисписок
TypeAmoDate FieldType = "date" //Дата
TypeAmoUrl FieldType = "url" // Ссылка
TypeAmoTextarea FieldType = "textarea" // Текстовая область
TypeAmoRadiobutton FieldType = "radiobutton" // Переключатель
TypeAmoStreetAddress FieldType = "streetaddress" // Короткий адрес
TypeAmoSmartAddress FieldType = "smart_address" // Адрес
TypeAmoBirthday FieldType = "birthday" // День рождения
TypeAmoLegalEntity FieldType = "legal_entity" // Юр. лицо
TypeAmoDateTime FieldType = "date_time" // Дата и время
TypeAmoPrice FieldType = "price" //Цена
TypeAmoCategory FieldType = "category" // Категория
TypeAmoItems FieldType = "items" // Предметы
TypeAmoTrackingData FieldType = "tracking_data" // Отслеживаемые данные
TypeAmoLinkedEntity FieldType = "linked_entity" // Связь с другим элементом
TypeAmoChainedList FieldType = "chained_list" // Каталоги и списки (платная опция Супер-поля)
TypeAmoMonetary FieldType = "monetary" // Денежное (платная опция Супер-поля)
TypeAmoFile FieldType = "file" // Файл
TypeAmoPayer FieldType = "payer" // Плательщик (только в списке Счета-покупки)
TypeAmoSupplier FieldType = "supplier" // Поставщик (только в списке Счета-покупки)
TypeAmoMultiText FieldType = "multitext" // что то чего нет в списке полей но она есть в амо))
)
var TypeMapping = map[string]FieldType{
"variant": TypeAmoText, //TypeAmoChainedList,
"images": TypeAmoText, //TypeAmoFile,
"varimg": TypeAmoText, //TypeAmoFile,
"file": TypeAmoFile,
"text": TypeAmoText,
"emoji": TypeAmoText,
"select": TypeAmoText, //TypeAmoSelect,
"date": TypeAmoText, //TypeAmoDate,
"number": TypeAmoText, //TypeAmoNumeric,
"page": TypeAmoText,
"rating": TypeAmoText,
"result": TypeAmoText,
}
type AmoUsersTrueResults struct {
QuizID int64
AnswerID int64
Result bool
QuestionID int64
Content string
Session string
AccessToken string
AmoAccountID int32
UTMs []int32
FieldsRule Fieldsrule
PerformerID int32
StepID int32
PipelineID int32
PerformerName string
}

26
model/amoReq.go Normal file

@ -0,0 +1,26 @@
package model
type ListDeleteUTMIDsReq struct {
/* - список айдишников utm которые удалить*/
Utms []int32 `json:"utms"`
}
type PaginationReq struct {
/* - указание страницы пагинации. Если страница не указана, применять 0*/
Page int `json:"page"`
/* - указание размера страницы пагинации. По умолчанию применять 25*/
Size int32 `json:"size"`
}
type RulesReq struct {
PerformerID int32 // айдишник ответственного за сделку
PipelineID int32 // айдишник воронки
StepID int32 // айдишник этапа
Utms []int32 // список UTM для этого опроса
Fieldsrule Fieldsrule // правила заполнения полей сущностей в амо
}
type SaveUserListUTMReq struct {
/* - список utm для сохранения. сохранять только те, которых в этом аккаунте ещё нет*/
Utms []UTM `json:"utms"`
}

72
model/amoResp.go Normal file

@ -0,0 +1,72 @@
package model
type ConnectAccountResp struct {
/* - ссылка для авторизации в амо*/
Link string `json:"link"`
}
type GetCurrentAccountResp struct {
/* - айдишник в нашей системе Primary Key*/
ID int64 `json:"ID"`
/* - имя аккаунта в амо*/
Name string `json:"Name"`
/* - поддомен организации в амо*/
Subdomain string `json:"Subdomain"`
/* - id пользователя из токена в нашей системе*/
Accountid string `json:"AccountID"`
/* - айдишник пользвателя, который подключал интеграцию*/
Amouserid int32 `json:"AmoUserID"`
/* - связь с аккаунтом в амо*/
Amocrmid int32 `json:"AmocrmID"`
/* - страна указанная в настройках амо*/
Country string `json:"Country"`
/* - таймштамп создания аккаунта*/
Createdat int64 `json:"CreatedAt"`
}
type GetListUserUTMResp struct {
/* - общее количество юзеров, которые у нас закешированы для этого пользователя*/
Count int64 `json:"count"`
/* - список юзеров, которые были закешированы нашим сервисом*/
Items []UTM `json:"items"`
}
type ListSavedIDUTMResp struct {
/* - список айдишников сохранённых меток*/
Ids []int64 `json:"IDs"`
}
type UserListFieldsResp struct {
/* - общее количество кастомных полей, которые у нас закешированы для этого пользователя*/
Count int64 `json:"count"`
/* - список кастомных полей, которые были закешированы нашим сервисом*/
Items []Field `json:"items"`
}
type UserListPipelinesResp struct {
/* - общее количество воронок, которые у нас закешированы для этого пользователя*/
Count int64 `json:"count"`
/* - список воронок, которые были закешированы нашим сервисом*/
Items []Pipeline `json:"items"`
}
type UserListResp struct {
/* - общее количество юзеров, которые у нас закешированы для этого пользователя*/
Count int64 `json:"count"`
/* - список юзеров, которые были закешированы нашим сервисом*/
Items []User `json:"items"`
}
type UserListStepsResp struct {
/* - список шагов воронок, которые были закешированы нашим сервисом*/
Items []Step `json:"items"`
/* - общее количество шагов воронок, которые у нас закешированы для этого пользователя*/
Count int64 `json:"count"`
}
type UserListTagsResp struct {
/* - общее количество тегов, которые у нас закешированы для этого пользователя*/
Count int64 `json:"count"`
/* - список тегов, которые были закешированы нашим сервисом*/
Items []Tag `json:"items"`
}

1074
repository/amo/amo.go Normal file

File diff suppressed because it is too large Load Diff

@ -83,6 +83,7 @@ func (r *AnswerRepository) GetAllAnswersByQuizID(ctx context.Context, session st
}
for _, row := range rows {
//todo тут забыл добавить проверку на то что minio !=nil
/*if row.Questiontype == model.TypeFile {
fmt.Println("GALL", row.Qid, row.QuestionID, row.Content)
fileURL, err := r.answerMinio.GetAnswerURL(ctx, row.Qid.UUID.String(), row.QuestionID, row.Content.String)

@ -245,12 +245,12 @@ func (r *QuestionRepository) UpdateQuestion(ctx context.Context, record model.Qu
params = append(params, record.Required, record.Version, record.Id)
var placeholders []any
for i:=1;i<=len(params);i++ {
for i := 1; i <= len(params); i++ {
placeholders = append(placeholders, i)
}
query = fmt.Sprintf(query, placeholders...)
_, err := r.pool.ExecContext(ctx, query, params...)
return err
}
@ -455,3 +455,33 @@ func (r *QuestionRepository) ForSortingResults(ctx context.Context, allAnswers [
return sortedAllAnswers, nil
}
func (r *QuestionRepository) GetQuestionListByIDs(ctx context.Context, ids []int32) ([]model.Question, error) {
rows, err := r.queries.GetQuestionListByIDs(ctx, ids)
if err != nil {
return nil, err
}
var questions []model.Question
for _, row := range rows {
question := model.Question{
Id: uint64(row.ID),
QuizId: uint64(row.QuizID),
Title: row.Title,
Description: row.Description.String,
Type: string(row.Questiontype.([]byte)),
Required: row.Required.Bool,
Deleted: row.Deleted.Bool,
Page: int(row.Page.Int16),
Content: row.Content.String,
Version: int(row.Version.Int16),
ParentIds: row.ParentIds,
CreatedAt: row.CreatedAt.Time,
UpdatedAt: row.UpdatedAt.Time,
}
questions = append(questions, question)
}
return questions, nil
}

@ -18,6 +18,12 @@ packages:
- "./dal/schema/000006_init.down.sql"
- "./dal/schema/000007_init.up.sql"
- "./dal/schema/000007_init.down.sql"
- "./dal/schema/000008_init.up.sql"
- "./dal/schema/000008_init.down.sql"
- "./dal/schema/000009_init.up.sql"
- "./dal/schema/000009_init.down.sql"
- "./dal/schema/000010_init.up.sql"
- "./dal/schema/000010_init.down.sql"
engine: "postgresql"
emit_json_tags: true
emit_db_tags: true