megre main into leadTarget

This commit is contained in:
Pavel 2024-06-24 13:45:58 +03:00
commit c23fda5f3f
20 changed files with 1076 additions and 609 deletions

@ -5,6 +5,8 @@ import (
"database/sql"
_ "embed"
"errors"
"fmt"
_ "github.com/ClickHouse/clickhouse-go"
_ "github.com/lib/pq"
"github.com/minio/minio-go/v7"
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal/sqlcgen"
@ -31,6 +33,7 @@ type DAL struct {
ResultRepo *result.ResultRepository
WorkerRepo *workers.WorkerRepository
StatisticsRepo *statistics.StatisticsRepository
WorkerAnsRepo *answer.WorkerAnswerRepository
}
func New(ctx context.Context, cred string, minioClient *minio.Client) (*DAL, error) {
@ -62,12 +65,17 @@ func New(ctx context.Context, cred string, minioClient *minio.Client) (*DAL, err
}
}
answerRepo := answer.NewAnswerRepository(answer.Deps{
workerAnsRepo := answer.NewWorkerAnswerRepo(answer.Deps{
Queries: queries,
Pool: pool,
AnswerMinio: storerAnswer,
})
answerRepo := answer.NewAnswerRepository(answer.Deps{
Queries: queries,
Pool: pool,
})
questionRepo := question.NewQuestionRepository(question.Deps{
Queries: queries,
Pool: pool,
@ -102,6 +110,7 @@ func New(ctx context.Context, cred string, minioClient *minio.Client) (*DAL, err
ResultRepo: resultRepo,
WorkerRepo: workerRepo,
StatisticsRepo: statisticsRepo,
WorkerAnsRepo: workerAnsRepo,
}, nil
}
@ -177,3 +186,38 @@ func (d *AmoDal) Close(ctx context.Context) error {
}
return nil
}
type ClickHouseDAL struct {
conn *sql.DB
StatisticClickRepo *statistics.StatisticClick
}
func NewClickHouseDAL(ctx context.Context, cred string) (*ClickHouseDAL, error) {
conn, err := sql.Open("clickhouse", cred)
if err != nil {
return nil, fmt.Errorf("error open database connection: %w", err)
}
timeoutCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
if err := conn.PingContext(timeoutCtx); err != nil {
return nil, fmt.Errorf("error ping database: %w", err)
}
statsClickRepo, err := statistics.NewClickStatistic(ctx, statistics.DepsClick{
Conn: conn,
})
if err != nil {
return nil, err
}
return &ClickHouseDAL{
conn: conn,
StatisticClickRepo: statsClickRepo,
}, nil
}
func (d *ClickHouseDAL) Close(ctx context.Context) error {
return d.conn.Close()
}

@ -20,7 +20,7 @@ INSERT INTO quiz (accountid,
qid
)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19)
RETURNING id, created_at, updated_at, qid;
RETURNING id, created_at, updated_at, qid;
-- name: InsertQuestion :one
INSERT INTO question (
@ -35,7 +35,7 @@ INSERT INTO question (
updated_at
)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)
RETURNING id, created_at, updated_at;
RETURNING id, created_at, updated_at;
-- name: DeleteQuestion :one
UPDATE question SET deleted=true WHERE id=$1 RETURNING question.*;
@ -51,7 +51,7 @@ INSERT INTO question(
SELECT $1, title, description, questiontype, required,
page, content, version, parent_ids
FROM question WHERE question.id=$2
RETURNING question.id, quiz_id, created_at, updated_at;
RETURNING question.id, quiz_id, created_at, updated_at;
-- name: DuplicateQuestion :one
INSERT INTO question(
@ -61,7 +61,7 @@ INSERT INTO question(
SELECT quiz_id, title, description, questiontype, required,
page, content, version, parent_ids
FROM question WHERE question.id=$1
RETURNING question.id, quiz_id, created_at, updated_at;
RETURNING question.id, quiz_id, created_at, updated_at;
-- name: MoveToHistory :one
INSERT INTO question(
@ -71,7 +71,7 @@ INSERT INTO question(
SELECT quiz_id, title, description, questiontype, required,
page, content, version, parent_ids, true as deleted
FROM question WHERE question.id=$1
RETURNING question.id, quiz_id, parent_ids;
RETURNING question.id, quiz_id, parent_ids;
-- name: MoveToHistoryQuiz :one
INSERT INTO quiz(deleted,
@ -81,7 +81,7 @@ INSERT INTO quiz(deleted,
SELECT true as deleted, accountid, archived,fingerprinting,repeatable,note_prevented,mail_notifications,unique_answers,name,description,config,
status,limit_answers,due_to,time_of_passing,pausable,version,version_comment,parent_ids,questions_count,answers_count,average_time_passing, super, group_id
FROM quiz WHERE quiz.id=$1 AND quiz.accountid=$2
RETURNING quiz.id, qid, parent_ids;
RETURNING quiz.id, qid, parent_ids;
-- name: CopyQuiz :one
INSERT INTO quiz(
@ -91,7 +91,7 @@ INSERT INTO quiz(
SELECT accountid, archived,fingerprinting,repeatable,note_prevented,mail_notifications,unique_answers,name,description,config,
status,limit_answers,due_to,time_of_passing,pausable,version,version_comment,parent_ids,questions_count, super, group_id
FROM quiz WHERE quiz.id=$1 AND quiz.accountId=$2
RETURNING id, qid,created_at, updated_at;
RETURNING id, qid,created_at, updated_at;
-- name: CopyQuizQuestions :exec
INSERT INTO question(
@ -134,6 +134,7 @@ SELECT
p.amount,
p.created_at,
a.id,
a.email,
qz.config
FROM
privileges AS p
@ -143,7 +144,7 @@ WHERE
qz.id = $1;
-- name: CreateAccount :one
INSERT INTO account (id, user_id, created_at, deleted) VALUES ($1, $2, $3, $4) RETURNING *;
INSERT INTO account (id, user_id, email, created_at, deleted) VALUES ($1, $2, $3, $4, $5) RETURNING *;;
-- name: DeletePrivilegeByAccID :exec
DELETE FROM privileges WHERE account_id = $1;
@ -232,11 +233,11 @@ SET
answers_count = COALESCE(aa.unique_true_answers_count, 0),
average_time_passing = COALESCE(sta.average_session_time, 0),
sessions_count = COALESCE(sta.sess,0)
FROM
FROM
(SELECT * FROM quiz WHERE deleted = FALSE AND archived = FALSE) q_sub
LEFT JOIN answer_aggregates aa ON q_sub.id = aa.quiz_id
LEFT JOIN question_aggregates qa ON q_sub.id = qa.quiz_id
LEFT JOIN session_times_aggregates sta ON q_sub.id = sta.quiz_id
LEFT JOIN answer_aggregates aa ON q_sub.id = aa.quiz_id
LEFT JOIN question_aggregates qa ON q_sub.id = qa.quiz_id
LEFT JOIN session_times_aggregates sta ON q_sub.id = sta.quiz_id
WHERE
q.id = q_sub.id;
@ -247,6 +248,7 @@ UPDATE privileges SET amount = $1 WHERE id = $2;
SELECT
a.id,
a.user_id,
a.email,
a.created_at,
COALESCE(p.ID,0),
coalesce(p.privilegeid,''),
@ -407,13 +409,13 @@ WITH TimeBucket AS (
SELECT
date_trunc('hour', timestamp_bucket)::TIMESTAMP AS time_interval_start,
COALESCE(LEAD(
date_trunc('hour', timestamp_bucket)::TIMESTAMP
) OVER (ORDER BY timestamp_bucket), NOW()) AS time_interval_end
date_trunc('hour', timestamp_bucket)::TIMESTAMP
) OVER (ORDER BY timestamp_bucket), NOW()) AS time_interval_end
FROM
generate_series(TO_TIMESTAMP($1), TO_TIMESTAMP($2), CASE
WHEN EXTRACT(epoch FROM TO_TIMESTAMP($2)) - EXTRACT(epoch FROM TO_TIMESTAMP($1)) > 172800 THEN '1 day'::interval
ELSE '1 hour'::interval
END) AS timestamp_bucket
generate_series(TO_TIMESTAMP($1), TO_TIMESTAMP($2), CASE
WHEN EXTRACT(epoch FROM TO_TIMESTAMP($2)) - EXTRACT(epoch FROM TO_TIMESTAMP($1)) > 172800 THEN '1 day'::interval
ELSE '1 hour'::interval
END) AS timestamp_bucket
),
OpenStats AS (
SELECT
@ -494,7 +496,7 @@ SELECT
CASE
WHEN COALESCE(os.open_count, 0) > 0 THEN COALESCE(rs.true_result_count, 0)::float / COALESCE(os.open_count, 0)::float
ELSE 0
END::float AS conversion,
END::float AS conversion,
COALESCE(at.avg_time, 0) AS avg_time
FROM
TimeBucket tb
@ -511,10 +513,10 @@ FROM
-- name: QuestionsStatistics :many
WITH Funnel AS (
SELECT
COUNT(DISTINCT a.session) FILTER (WHERE a.start = FALSE) AS count_start_false,
COUNT(DISTINCT a.session) FILTER (WHERE a.start = TRUE) AS count_start_true,
COUNT(DISTINCT CASE WHEN a.result = FALSE AND qid_true_result IS NOT NULL THEN a.session END) AS count_f_result_with_t_question,
COUNT(DISTINCT a.session) FILTER (WHERE a.result = TRUE) AS count_t_result
COUNT(DISTINCT a.session) FILTER (WHERE a.start = FALSE) AS count_start_false,
COUNT(DISTINCT a.session) FILTER (WHERE a.start = TRUE) AS count_start_true,
COUNT(DISTINCT CASE WHEN a.result = FALSE AND qid_true_result IS NOT NULL THEN a.session END) AS count_f_result_with_t_question,
COUNT(DISTINCT a.session) FILTER (WHERE a.result = TRUE) AS count_t_result
FROM
answer a
LEFT JOIN (
@ -529,23 +531,23 @@ WITH Funnel AS (
AND a.created_at <= TO_TIMESTAMP($3)
),
Results AS (
SELECT
COALESCE(q.title, '') AS question_title,
COUNT(*) AS total_answers,
CAST(COUNT(*) * 100.0 / NULLIF(SUM(COUNT(*)) FILTER (WHERE a.result = TRUE) OVER (PARTITION BY a.quiz_id), 0) AS FLOAT8) AS percentage
FROM
question q
JOIN answer a ON q.id = a.question_id
WHERE
a.quiz_id = $1
AND a.created_at >= TO_TIMESTAMP($2)
AND a.created_at <= TO_TIMESTAMP($3)
AND a.result = TRUE
GROUP BY
q.title, a.quiz_id, a.result
HAVING
COUNT(*) >= 1
),
SELECT
COALESCE(q.title, '') AS question_title,
COUNT(*) AS total_answers,
CAST(COUNT(*) * 100.0 / NULLIF(SUM(COUNT(*)) FILTER (WHERE a.result = TRUE) OVER (PARTITION BY a.quiz_id), 0) AS FLOAT8) AS percentage
FROM
question q
JOIN answer a ON q.id = a.question_id
WHERE
a.quiz_id = $1
AND a.created_at >= TO_TIMESTAMP($2)
AND a.created_at <= TO_TIMESTAMP($3)
AND a.result = TRUE
GROUP BY
q.title, a.quiz_id, a.result
HAVING
COUNT(*) >= 1
),
LastContent AS (
SELECT
a.question_id,
@ -561,35 +563,35 @@ WITH Funnel AS (
answer
WHERE
quiz_id = $1
AND start != true
AND start != true
AND created_at >= TO_TIMESTAMP($2)
AND created_at <= TO_TIMESTAMP($3)
GROUP BY
question_id, session
question_id, session
) AS last_created_at_one_session ON a.session = last_created_at_one_session.session AND a.question_id = last_created_at_one_session.question_id AND a.created_at = last_created_at_one_session.last_created_at
),
Questions AS (
SELECT
q.title AS question_title,
q.page AS question_page,
lc.last_answer_content AS answer_content,
CAST(
COUNT(CASE WHEN a.result = FALSE THEN 1 END) * 100.0 / NULLIF(SUM(COUNT(CASE WHEN a.result = FALSE THEN 1 END)) OVER (PARTITION BY q.id), 0) AS FLOAT8
) AS percentage
FROM
question q
JOIN LastContent lc ON q.id = lc.question_id
JOIN answer a ON q.id = a.question_id
WHERE
a.quiz_id = $1
AND a.start != true
AND a.created_at >= TO_TIMESTAMP($2)
AND a.created_at <= TO_TIMESTAMP($3)
GROUP BY
q.id, q.title, lc.last_answer_content
HAVING
COUNT(*) >= 1
)
SELECT
q.title AS question_title,
q.page AS question_page,
lc.last_answer_content AS answer_content,
CAST(
COUNT(CASE WHEN a.result = FALSE THEN 1 END) * 100.0 / NULLIF(SUM(COUNT(CASE WHEN a.result = FALSE THEN 1 END)) OVER (PARTITION BY q.id), 0) AS FLOAT8
) AS percentage
FROM
question q
JOIN LastContent lc ON q.id = lc.question_id
JOIN answer a ON q.id = a.question_id
WHERE
a.quiz_id = $1
AND a.start != true
AND a.created_at >= TO_TIMESTAMP($2)
AND a.created_at <= TO_TIMESTAMP($3)
GROUP BY
q.id, q.title, lc.last_answer_content
HAVING
COUNT(*) >= 1
)
SELECT
Funnel.count_start_false,
Funnel.count_start_true,
@ -603,34 +605,34 @@ SELECT
COALESCE(Questions.percentage, 0) AS questions_percentage
FROM
Funnel
LEFT JOIN Results ON true
LEFT JOIN Questions ON Questions.percentage >= 1;
LEFT JOIN Results ON true
LEFT JOIN Questions ON Questions.percentage >= 1;
-- name: QuizCopyQid :one
INSERT INTO quiz (
accountid, archived, fingerprinting, repeatable, note_prevented, mail_notifications, unique_answers, name, description, config,
status, limit_answers, due_to, time_of_passing, pausable, version, version_comment, parent_ids, questions_count, answers_count, average_time_passing, super, group_id
)
SELECT
$2, archived, fingerprinting, repeatable, note_prevented, mail_notifications, unique_answers, name, description, config,
status, limit_answers, due_to, time_of_passing, pausable, version, version_comment, parent_ids, questions_count, answers_count, average_time_passing, super, group_id
FROM
quiz as q
WHERE
q.qid = $1
RETURNING (select id from quiz where qid = $1),id, qid;
INSERT INTO quiz (
accountid, archived, fingerprinting, repeatable, note_prevented, mail_notifications, unique_answers, name, description, config,
status, limit_answers, due_to, time_of_passing, pausable, version, version_comment, parent_ids, questions_count, answers_count, average_time_passing, super, group_id
)
SELECT
$2, archived, fingerprinting, repeatable, note_prevented, mail_notifications, unique_answers, name, description, config,
status, limit_answers, due_to, time_of_passing, pausable, version, version_comment, parent_ids, questions_count, answers_count, average_time_passing, super, group_id
FROM
quiz as q
WHERE
q.qid = $1
RETURNING (select id from quiz where qid = $1),id, qid;
-- name: CopyQuestionQuizID :exec
INSERT INTO question (
quiz_id, title, description, questiontype, required,
page, content, version, parent_ids, created_at, updated_at
)
SELECT
SELECT
$2, title, description, questiontype, required,
page, content, version, parent_ids, created_at, updated_at
FROM
FROM
question
WHERE
WHERE
question.quiz_id = $1 AND deleted = false;
-- name: GetQidOwner :one
@ -656,7 +658,7 @@ SELECT
(SELECT registration_count FROM Registrations) AS registrations,
(SELECT quiz_count FROM Quizes) AS quizes,
(SELECT result_count FROM Results) AS results;
-- name: GetListStartQuiz :many
SELECT id FROM quiz WHERE accountid = $1 AND status = 'start';
@ -684,8 +686,8 @@ RETURNING quiz_id;
-- amo methods:
-- name: CreateAmoAccount :exec
INSERT INTO users (AccountID, AmoID, Name, Email, Role, "Group", Deleted, CreatedAt, Subdomain, AmoUserID, Country,DriveURL)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12);
INSERT INTO accountsAmo (AccountID, AmoID,Name, Subdomain, Country,DriveURL)
VALUES ($1, $2, $3, $4, $5, $6);
-- name: CreateWebHook :exec
INSERT INTO tokens (AccountID, RefreshToken, AccessToken, AuthCode, Expiration, CreatedAt)
@ -698,61 +700,80 @@ 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: CheckExpiredToken :one
SELECT * FROM tokens WHERE AccountID = $1 AND Expiration <= NOW();
-- name: UpdateAmoAccount :exec
UPDATE accountsAmo SET Name = $2, Subdomain = $3, Country = $4, DriveURL = $5 WHERE AccountID = $1 AND Deleted = false;
-- 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);
WITH companyDel AS (
UPDATE accountsAmo SET Deleted = true WHERE accountsAmo.AmoID = $1 RETURNING AccountID
),
userDel AS (
UPDATE usersAmo SET Deleted = true WHERE AmoID = $1
)
DELETE FROM tokens WHERE AccountID IN (SELECT AccountID FROM companyDel);
-- name: SoftDeleteAccount :exec
WITH userd AS (
SELECT AmoUserID FROM users WHERE users.AccountID = $1
WITH amoCompany AS (
SELECT AmoID FROM accountsAmo WHERE accountsAmo.AccountID = $1
),usersDel AS (
UPDATE usersAmo SET Deleted = true WHERE AmoID = (SELECT AmoID FROM amoCompany)
),
tokend AS (
UPDATE users SET Deleted = true WHERE AmoUserID IN (SELECT AmoUserID FROM userd)
companyDel AS ( UPDATE accountsAmo SET Deleted = true WHERE AmoID = (SELECT AmoID FROM amoCompany)
)
DELETE FROM tokens WHERE tokens.AccountID = $1;
-- name: GetCurrentAccount :one
SELECT * FROM users WHERE AccountID = $1 AND Deleted = false;
-- name: GetCurrentCompany :one
SELECT * FROM accountsAmo WHERE AccountID = $1 AND Deleted = false;
-- name: CheckMainUser :exec
UPDATE users SET Name = $1, "Group" = $2, Email = $3, Role = $4 WHERE AmoID = $5;
-- name: GetAllCompanyUsers :many
SELECT * FROM usersamo WHERE amoid = $1 AND deleted = false;
-- name: GetUsersWithPagination :many
WITH user_data AS (
SELECT AmoID FROM users WHERE users.AccountID = $1 AND Deleted = false
SELECT AmoID FROM accountsAmo WHERE accountsAmo.AccountID = $1 AND accountsAmo.Deleted = false
)
SELECT u.*, COUNT(*) OVER() as total_count
FROM users u
JOIN user_data a ON u.AmoUserID = a.AmoID
SELECT u.*, COUNT(*) OVER() as total_count
FROM usersAmo u
JOIN user_data a ON u.AmoID = a.AmoID
WHERE u.Deleted = false
ORDER BY u.ID OFFSET ($2 - 1) * $3 LIMIT $3;
-- name: GetTagsWithPagination :many
WITH user_data AS (
SELECT AmoID FROM accountsAmo WHERE accountsAmo.AccountID = $1 AND accountsAmo.Deleted = false
)
SELECT t.*, COUNT(*) OVER() as total_count
FROM tags t JOIN (SELECT AmoID FROM users WHERE users.AccountID = $1 AND Deleted = false) u ON t.AccountID = u.AmoID
FROM tags t JOIN user_data u ON t.AccountID = u.AmoID
WHERE t.Deleted = false
ORDER BY t.ID OFFSET ($2 - 1) * $3 LIMIT $3;
-- name: GetStepsWithPagination :many
WITH user_data AS (
SELECT AmoID FROM accountsAmo WHERE accountsAmo.AccountID = $1 AND accountsAmo.Deleted = false
)
SELECT s.*, COUNT(*) OVER() as total_count
FROM steps s JOIN (SELECT AmoID FROM users WHERE users.AccountID = $1 AND Deleted = false) u ON s.AccountID = u.AmoID
FROM steps s JOIN user_data u ON s.AccountID = u.AmoID
WHERE s.Deleted = false AND PipelineID = $4
ORDER BY s.ID OFFSET ($2 - 1) * $3 LIMIT $3;
-- name: GetPipelinesWithPagination :many
WITH user_data AS (
SELECT AmoID FROM accountsAmo WHERE accountsAmo.AccountID = $1 AND accountsAmo.Deleted = false
)
SELECT p.*, COUNT(*) OVER() as total_count
FROM pipelines p JOIN (SELECT AmoID FROM users WHERE users.AccountID = $1 AND Deleted = false) u ON p.AccountID = u.AmoID
FROM pipelines p JOIN user_data u ON p.AccountID = u.AmoID
WHERE p.Deleted = false
ORDER BY p.ID OFFSET ($2 - 1) * $3 LIMIT $3;
-- name: GetFieldsWithPagination :many
WITH user_data AS (
SELECT AmoID FROM accountsAmo WHERE accountsAmo.AccountID = $1 AND accountsAmo.Deleted = false
)
SELECT f.*, COUNT(*) OVER() as total_count
FROM fields f JOIN (SELECT AmoID FROM users WHERE users.AccountID = $1 AND Deleted = false) u ON f.AccountID = u.AmoID
FROM fields f JOIN user_data u ON f.AccountID = u.AmoID
WHERE f.Deleted = false
ORDER BY f.ID OFFSET ($2 - 1) * $3 LIMIT $3;
@ -791,12 +812,10 @@ 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
SELECT AmoID FROM accountsAmo WHERE accountsAmo.AccountID = $1 AND accountsAmo.Deleted = false
), new_tags AS (
SELECT (tag->>'AmoID')::INT AS amoID,
(tag->>'Entity')::entitytype AS Entity,
@ -818,7 +837,7 @@ WITH user_data AS (
)
SELECT nt.*,ud.AmoID
FROM new_tags nt
JOIN user_data ud ON true
JOIN user_data ud ON true
WHERE NOT EXISTS (
SELECT *
FROM inserted_tags ins
@ -836,14 +855,14 @@ WITH new_pipelines AS (
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.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
@ -855,34 +874,32 @@ WHERE NOT EXISTS (
-- name: CheckFields :many
WITH user_data AS (
SELECT AmoID
FROM users
WHERE users.AccountID = $1
SELECT AmoID FROM accountsAmo WHERE accountsAmo.AccountID = $1 AND accountsAmo.Deleted = false
), 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
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.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
JOIN user_data ud ON true
WHERE NOT EXISTS (
SELECT *
FROM inserted_fields ins
@ -901,15 +918,15 @@ WITH new_steps AS (
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.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
@ -928,13 +945,13 @@ SELECT * FROM rules WHERE QuizID = $1 AND Deleted = false;
-- name: SetQuizSettings :one
INSERT INTO rules (AccountID, QuizID, PerformerID, PipelineID, StepID, FieldsRule,TagsToAdd)
SELECT u.AmoID AS AccountID,$1 AS QuizID,$2 AS PerformerID,$3 AS PipelineID,
$4 AS StepID,$5 AS FieldsRule,$6 AS TagsToAdd FROM users u WHERE u.AccountID = $7 AND u.Deleted = false
RETURNING id;
$4 AS StepID,$5 AS FieldsRule,$6 AS TagsToAdd FROM accountsamo u WHERE u.AccountID = $7 AND u.Deleted = false
RETURNING id;
-- name: ChangeQuizSettings :one
UPDATE rules
SET PerformerID = $1,PipelineID = $2,StepID = $3,FieldsRule = $4, TagsToAdd=$5
WHERE AccountID = (SELECT AmoID FROM users WHERE users.AccountID = $6 AND users.Deleted = false) AND QuizID = $7 AND Deleted = false
WHERE AccountID = (SELECT AmoID FROM accountsAmo WHERE accountsAmo.AccountID = $6 AND accountsAmo.Deleted = false) AND QuizID = $7 AND Deleted = false
RETURNING id;
-- name: GetQuestionListByIDs :many
@ -942,62 +959,29 @@ 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 users.Deleted = false) AND QuizID = $3 AND Deleted = false;
WHERE AccountID = (SELECT AmoID FROM accountsAmo WHERE accountsAmo.AccountID = $2 AND accountsAmo.Deleted = false) 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: UpdateAmoAccountUser :exec
UPDATE usersAmo SET Name = $3, Email = $4, Role = $5, "Group" = $6
WHERE AmoID = $1 AND AmoUserID = $2 AND deleted = false;
-- 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: AddAmoAccountUser :exec
INSERT INTO usersAmo (AmoID, AmoUserID, Name, Email, Role, "Group")
VALUES ($1, $2, $3, $4, $5, $6);
-- name: GettingAmoUsersTrueResults :many
SELECT a.quiz_id,a.id,a.result,a.question_id,a.content,a.session,
(SELECT a2.utm
FROM answer a2
WHERE a2.start = true AND a2.session = a.session
LIMIT 1) AS utm
,t.accesstoken,r.accountid,r.fieldsrule,r.tagstoadd,r.performerid,r.stepid,r.pipelineid,(SELECT u.name FROM users u WHERE u.amoid = r.performerid) AS performer_name,u.subdomain,u.accountid,u.driveurl
COALESCE((SELECT a2.utm
FROM answer a2
WHERE a2.start = true AND a2.session = a.session
LIMIT 1), '{}'::jsonb) AS utm
,t.accesstoken,r.accountid,r.fieldsrule,r.tagstoadd,r.performerid,r.stepid,r.pipelineid,(SELECT u.name FROM usersAmo u WHERE u.AmoUserID = r.performerid AND u.deleted = false) AS performer_name,u.subdomain,u.accountid,u.driveurl
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
INNER JOIN accountsAmo u ON q.accountid = u.accountid AND u.amoid = r.accountid
WHERE a.result = true
AND s.id IS NULL
AND a.deleted = false
@ -1009,12 +993,12 @@ WHERE a.result = true
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
JOIN accountsAmo AS u ON t.AccountID = u.AccountID
WHERE t.AccessToken = $4 AND u.Deleted = false;
-- 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 AND u.Deleted = false);
WHERE DealID = $2 AND AccountID = (SELECT u.AmoID FROM tokens AS t JOIN accountsAmo AS u ON t.AccountID = u.AccountID WHERE t.AccessToken = $3 AND u.Deleted = false);
-- name: DeleteFields :exec
UPDATE fields SET Deleted = true WHERE ID = ANY($1::bigint[]);
@ -1029,7 +1013,7 @@ UPDATE steps SET Deleted = true WHERE ID = ANY($1::bigint[]);
UPDATE pipelines SET Deleted = true WHERE ID = ANY($1::bigint[]);
-- name: DeleteUsers :exec
UPDATE users SET Deleted = true WHERE ID = ANY($1::bigint[]);
UPDATE usersAmo SET Deleted = true WHERE ID = ANY($1::bigint[]);
-- name: GetUserTagsByID :many
SELECT ID,AmoID,AccountID,Name,Entity,Color
@ -1047,9 +1031,7 @@ 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;
SELECT * FROM usersAmo WHERE amoid = $1 AND Deleted = false;
-- name: GetFieldByAmoID :one
SELECT * FROM fields WHERE AmoID = $1 AND Deleted = false;
@ -1062,7 +1044,19 @@ WHERE AccountID = $1 AND Deleted = false;
-- name: DecrementManual :one
UPDATE privileges p SET amount = amount - 1 FROM account a
WHERE p.account_id = a.id AND a.user_id = $1 AND p.privilegeID = $2 AND p.amount > 0
RETURNING p.id, p.privilegeID, p.account_id, p.privilege_name, p.amount, p.created_at;;
RETURNING p.id, p.privilegeID, p.account_id, p.privilege_name, p.amount, p.created_at;
-- name: GetExistingContactAmo :many
WITH getAmoID AS (
SELECT AmoID FROM amoContact WHERE amoContact.AccountID = $1 AND amoContact.Field = ANY($2::text[])
) SELECT * FROM amoContact
WHERE amoContact.AccountID = $1 AND amoContact.AmoID IN (SELECT AmoID FROM getAmoID);
-- name: InsertContactAmo :one
INSERT INTO amoContact (AccountID, AmoID, Field) VALUES ($1, $2, $3) RETURNING AmoID;
-- name: UpdateAmoContact :exec
UPDATE amoContact SET Field = $1,AmoID=$3 WHERE ID = $2;
-- name: CreateLeadTarget :one
INSERT INTO leadtarget (accountID,type,quizID,target) VALUES ($1,$2,$3,$4) RETURNING *;

@ -0,0 +1,20 @@
DROP TABLE IF EXISTS usersAmo;
DROP INDEX IF EXISTS idx_unique_accountsAmo;
DROP TABLE IF EXISTS accountsAmo;
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 UNIQUE INDEX idx_unique_users ON users (amoID) WHERE Deleted = false;

@ -0,0 +1,28 @@
DROP TABLE IF EXISTS users;
DROP INDEX IF EXISTS idx_unique_users;
CREATE TABLE IF NOT EXISTS accountsAmo (
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 '',
Deleted BOOLEAN NOT NULL DEFAULT FALSE,
CreatedAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
Subdomain VARCHAR(50) NOT NULL DEFAULT '', -- поддомен - пример https://penadigitaltech.amocrm.ru
Country VARCHAR(50) NOT NULL DEFAULT '',
DriveURL VARCHAR(255) NOT NULL DEFAULT '' -- URL объктного хранилища
);
CREATE UNIQUE INDEX idx_unique_accountsAmo ON accountsAmo (amoID) WHERE Deleted = false;
CREATE TABLE IF NOT EXISTS usersAmo (
ID BIGSERIAL UNIQUE NOT NULL PRIMARY KEY,
AmoID INT NOT NULL, -- ID компании в амо (внешний ключ)
AmoUserID 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 NOT NULL DEFAULT CURRENT_TIMESTAMP
);

@ -0,0 +1,19 @@
UPDATE answer
SET content =
CASE
WHEN content ~ '<tr>|<td>' THEN
regexp_replace(content, '<\/?tr[^>]*>|<\/?td[^>]*>', '', 'g')
WHEN content ~ '<a download>[^<]+</a>' THEN
regexp_replace(content, '<a download>([^<]+)</a>', '\1', 'g')
WHEN content ~ '<img[^>]*src="([^"]*)"[^>]*' THEN
regexp_replace(content, '<img[^>]*src="\s*"[^>]*', '', 'g')
ELSE content
END;
UPDATE answer
SET content =
CASE
WHEN content ~ '<img' THEN
regexp_replace(content, '(.*?)(<img[^>]*src=["'']?([^"''>]+)["'']?[^>]*>)', '\1\3', 'g')
ELSE content
END;

@ -0,0 +1 @@
DROP TABLE IF EXISTS amoContact;

@ -0,0 +1,6 @@
CREATE TABLE IF NOT EXISTS amoContact (
ID BIGSERIAL UNIQUE NOT NULL PRIMARY KEY,
AccountID INT NOT NULL, -- ID "компании" в амо
AmoID INT NOT NULL, -- ID контакта в амо
Field text NOT NULL DEFAULT '' -- значение чего то email? phone? etc
)

@ -19,6 +19,25 @@ type Account struct {
Deleted sql.NullBool `db:"deleted" json:"deleted"`
}
type Accountsamo 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"`
Deleted bool `db:"deleted" json:"deleted"`
Createdat time.Time `db:"createdat" json:"createdat"`
Subdomain string `db:"subdomain" json:"subdomain"`
Country string `db:"country" json:"country"`
Driveurl string `db:"driveurl" json:"driveurl"`
}
type Amocontact struct {
ID int64 `db:"id" json:"id"`
Accountid int32 `db:"accountid" json:"accountid"`
Amoid int32 `db:"amoid" json:"amoid"`
Field string `db:"field" json:"field"`
}
type Amocrmstatus struct {
ID int64 `db:"id" json:"id"`
Accountid int32 `db:"accountid" json:"accountid"`
@ -182,18 +201,14 @@ type Token struct {
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"`
Driveurl string `db:"driveurl" json:"driveurl"`
type Usersamo struct {
ID int64 `db:"id" json:"id"`
Amoid int32 `db:"amoid" json:"amoid"`
Amouserid int32 `db:"amouserid" json:"amouserid"`
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 time.Time `db:"createdat" json:"createdat"`
}

@ -53,6 +53,32 @@ func (q *Queries) AccountPagination(ctx context.Context, arg AccountPaginationPa
return items, nil
}
const addAmoAccountUser = `-- name: AddAmoAccountUser :exec
INSERT INTO usersAmo (AmoID, AmoUserID, Name, Email, Role, "Group")
VALUES ($1, $2, $3, $4, $5, $6)
`
type AddAmoAccountUserParams struct {
Amoid int32 `db:"amoid" json:"amoid"`
Amouserid int32 `db:"amouserid" json:"amouserid"`
Name string `db:"name" json:"name"`
Email string `db:"email" json:"email"`
Role int32 `db:"role" json:"role"`
Group int32 `db:"Group" json:"Group"`
}
func (q *Queries) AddAmoAccountUser(ctx context.Context, arg AddAmoAccountUserParams) error {
_, err := q.db.ExecContext(ctx, addAmoAccountUser,
arg.Amoid,
arg.Amouserid,
arg.Name,
arg.Email,
arg.Role,
arg.Group,
)
return err
}
const allServiceStatistics = `-- name: AllServiceStatistics :one
WITH Registrations AS (
SELECT COUNT(*) AS registration_count
@ -110,7 +136,7 @@ func (q *Queries) ArchiveQuiz(ctx context.Context, arg ArchiveQuizParams) error
const changeQuizSettings = `-- name: ChangeQuizSettings :one
UPDATE rules
SET PerformerID = $1,PipelineID = $2,StepID = $3,FieldsRule = $4, TagsToAdd=$5
WHERE AccountID = (SELECT AmoID FROM users WHERE users.AccountID = $6 AND users.Deleted = false) AND QuizID = $7 AND Deleted = false
WHERE AccountID = (SELECT AmoID FROM accountsAmo WHERE accountsAmo.AccountID = $6 AND accountsAmo.Deleted = false) AND QuizID = $7 AND Deleted = false
RETURNING id
`
@ -157,45 +183,27 @@ func (q *Queries) CheckAndAddDefault(ctx context.Context, arg CheckAndAddDefault
return err
}
const checkExpired = `-- name: CheckExpired :many
SELECT accountid, refreshtoken, accesstoken, authcode, expiration, createdat FROM tokens WHERE Expiration <= TO_TIMESTAMP(EXTRACT(EPOCH FROM NOW()) + (10 * 60))
const checkExpiredToken = `-- name: CheckExpiredToken :one
SELECT accountid, refreshtoken, accesstoken, authcode, expiration, createdat FROM tokens WHERE AccountID = $1 AND Expiration <= NOW()
`
func (q *Queries) CheckExpired(ctx context.Context) ([]Token, error) {
rows, err := q.db.QueryContext(ctx, checkExpired)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Token
for rows.Next() {
var i Token
if err := rows.Scan(
&i.Accountid,
&i.Refreshtoken,
&i.Accesstoken,
&i.Authcode,
&i.Expiration,
&i.Createdat,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
func (q *Queries) CheckExpiredToken(ctx context.Context, accountid string) (Token, error) {
row := q.db.QueryRowContext(ctx, checkExpiredToken, accountid)
var i Token
err := row.Scan(
&i.Accountid,
&i.Refreshtoken,
&i.Accesstoken,
&i.Authcode,
&i.Expiration,
&i.Createdat,
)
return i, err
}
const checkFields = `-- name: CheckFields :many
WITH user_data AS (
SELECT AmoID
FROM users
WHERE users.AccountID = $1
SELECT AmoID FROM accountsAmo WHERE accountsAmo.AccountID = $1 AND accountsAmo.Deleted = false
), new_fields AS (
SELECT (field->>'AmoID')::INT AS amoID,
COALESCE(field->>'Code', '')::varchar(255) AS code,
@ -275,29 +283,6 @@ func (q *Queries) CheckFields(ctx context.Context, arg CheckFieldsParams) ([]Che
return items, nil
}
const checkMainUser = `-- name: CheckMainUser :exec
UPDATE users SET Name = $1, "Group" = $2, Email = $3, Role = $4 WHERE AmoID = $5
`
type CheckMainUserParams struct {
Name string `db:"name" json:"name"`
Group int32 `db:"Group" json:"Group"`
Email string `db:"email" json:"email"`
Role int32 `db:"role" json:"role"`
Amoid int32 `db:"amoid" json:"amoid"`
}
func (q *Queries) CheckMainUser(ctx context.Context, arg CheckMainUserParams) error {
_, err := q.db.ExecContext(ctx, checkMainUser,
arg.Name,
arg.Group,
arg.Email,
arg.Role,
arg.Amoid,
)
return err
}
const checkPipelines = `-- name: CheckPipelines :many
WITH new_pipelines AS (
SELECT (pipeline->>'AmoID')::INT AS amoID,
@ -480,9 +465,7 @@ func (q *Queries) CheckSteps(ctx context.Context, dollar_1 json.RawMessage) ([]C
const checkTags = `-- name: CheckTags :many
WITH user_data AS (
SELECT AmoID
FROM users
WHERE users.AccountID = $1
SELECT AmoID FROM accountsAmo WHERE accountsAmo.AccountID = $1 AND accountsAmo.Deleted = false
), new_tags AS (
SELECT (tag->>'AmoID')::INT AS amoID,
(tag->>'Entity')::entitytype AS Entity,
@ -555,79 +538,6 @@ func (q *Queries) CheckTags(ctx context.Context, arg CheckTagsParams) ([]CheckTa
return items, nil
}
const checkUsers = `-- 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 id, accountid, amoid, name, email, role, "Group", deleted, createdat, subdomain, amouserid, country, driveurl
)
SELECT nu.amoid, nu.name, nu."Group", nu.role, nu.email, nu.amouserid, nu.createdat
FROM new_users nu
WHERE NOT EXISTS (
SELECT id, accountid, amoid, name, email, role, "Group", deleted, createdat, subdomain, amouserid, country, driveurl
FROM inserted_users ins
WHERE ins.amoID = nu.amoID
)
`
type CheckUsersRow struct {
Amoid int32 `db:"amoid" json:"amoid"`
Name string `db:"name" json:"name"`
Group int32 `db:"Group" json:"Group"`
Role int32 `db:"role" json:"role"`
Email string `db:"email" json:"email"`
Amouserid int32 `db:"amouserid" json:"amouserid"`
Createdat interface{} `db:"createdat" json:"createdat"`
}
func (q *Queries) CheckUsers(ctx context.Context, dollar_1 json.RawMessage) ([]CheckUsersRow, error) {
rows, err := q.db.QueryContext(ctx, checkUsers, dollar_1)
if err != nil {
return nil, err
}
defer rows.Close()
var items []CheckUsersRow
for rows.Next() {
var i CheckUsersRow
if err := rows.Scan(
&i.Amoid,
&i.Name,
&i.Group,
&i.Role,
&i.Email,
&i.Amouserid,
&i.Createdat,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const copyQuestion = `-- name: CopyQuestion :one
INSERT INTO question(
quiz_id, title, description, questiontype, required,
@ -770,23 +680,17 @@ func (q *Queries) CreateAccount(ctx context.Context, arg CreateAccountParams) (A
const createAmoAccount = `-- name: CreateAmoAccount :exec
INSERT INTO users (AccountID, AmoID, Name, Email, Role, "Group", Deleted, CreatedAt, Subdomain, AmoUserID, Country,DriveURL)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
INSERT INTO accountsAmo (AccountID, AmoID,Name, Subdomain, Country,DriveURL)
VALUES ($1, $2, $3, $4, $5, $6)
`
type CreateAmoAccountParams struct {
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"`
Driveurl string `db:"driveurl" json:"driveurl"`
Accountid string `db:"accountid" json:"accountid"`
Amoid int32 `db:"amoid" json:"amoid"`
Name string `db:"name" json:"name"`
Subdomain string `db:"subdomain" json:"subdomain"`
Country string `db:"country" json:"country"`
Driveurl string `db:"driveurl" json:"driveurl"`
}
// amo methods:
@ -795,13 +699,7 @@ func (q *Queries) CreateAmoAccount(ctx context.Context, arg CreateAmoAccountPara
arg.Accountid,
arg.Amoid,
arg.Name,
arg.Email,
arg.Role,
arg.Group,
arg.Deleted,
arg.Createdat,
arg.Subdomain,
arg.Amouserid,
arg.Country,
arg.Driveurl,
)
@ -1034,7 +932,7 @@ func (q *Queries) DeleteTags(ctx context.Context, dollar_1 []int64) error {
}
const deleteUsers = `-- name: DeleteUsers :exec
UPDATE users SET Deleted = true WHERE ID = ANY($1::bigint[])
UPDATE usersAmo SET Deleted = true WHERE ID = ANY($1::bigint[])
`
func (q *Queries) DeleteUsers(ctx context.Context, dollar_1 []int64) error {
@ -1486,6 +1384,43 @@ func (q *Queries) GetAllAnswersByQuizID(ctx context.Context, session sql.NullStr
return items, nil
}
const getAllCompanyUsers = `-- name: GetAllCompanyUsers :many
SELECT id, amoid, amouserid, name, email, role, "Group", deleted, createdat FROM usersamo WHERE amoid = $1 AND deleted = false
`
func (q *Queries) GetAllCompanyUsers(ctx context.Context, amoid int32) ([]Usersamo, error) {
rows, err := q.db.QueryContext(ctx, getAllCompanyUsers, amoid)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Usersamo
for rows.Next() {
var i Usersamo
if err := rows.Scan(
&i.ID,
&i.Amoid,
&i.Amouserid,
&i.Name,
&i.Email,
&i.Role,
&i.Group,
&i.Deleted,
&i.Createdat,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getAllTokens = `-- name: GetAllTokens :many
SELECT accountid, refreshtoken, accesstoken, authcode, expiration, createdat FROM tokens
`
@ -1520,31 +1455,67 @@ func (q *Queries) GetAllTokens(ctx context.Context) ([]Token, error) {
return items, nil
}
const getCurrentAccount = `-- name: GetCurrentAccount :one
SELECT id, accountid, amoid, name, email, role, "Group", deleted, createdat, subdomain, amouserid, country, driveurl FROM users WHERE AccountID = $1 AND Deleted = false
const getCurrentCompany = `-- name: GetCurrentCompany :one
SELECT id, accountid, amoid, name, deleted, createdat, subdomain, country, driveurl FROM accountsAmo WHERE AccountID = $1 AND Deleted = false
`
func (q *Queries) GetCurrentAccount(ctx context.Context, accountid string) (User, error) {
row := q.db.QueryRowContext(ctx, getCurrentAccount, accountid)
var i User
func (q *Queries) GetCurrentCompany(ctx context.Context, accountid string) (Accountsamo, error) {
row := q.db.QueryRowContext(ctx, getCurrentCompany, accountid)
var i Accountsamo
err := row.Scan(
&i.ID,
&i.Accountid,
&i.Amoid,
&i.Name,
&i.Email,
&i.Role,
&i.Group,
&i.Deleted,
&i.Createdat,
&i.Subdomain,
&i.Amouserid,
&i.Country,
&i.Driveurl,
)
return i, err
}
const getExistingContactAmo = `-- name: GetExistingContactAmo :many
WITH getAmoID AS (
SELECT AmoID FROM amoContact WHERE amoContact.AccountID = $1 AND amoContact.Field = ANY($2::text[])
) SELECT id, accountid, amoid, field FROM amoContact
WHERE amoContact.AccountID = $1 AND amoContact.AmoID IN (SELECT AmoID FROM getAmoID)
`
type GetExistingContactAmoParams struct {
Accountid int32 `db:"accountid" json:"accountid"`
Column2 []string `db:"column_2" json:"column_2"`
}
func (q *Queries) GetExistingContactAmo(ctx context.Context, arg GetExistingContactAmoParams) ([]Amocontact, error) {
rows, err := q.db.QueryContext(ctx, getExistingContactAmo, arg.Accountid, pq.Array(arg.Column2))
if err != nil {
return nil, err
}
defer rows.Close()
var items []Amocontact
for rows.Next() {
var i Amocontact
if err := rows.Scan(
&i.ID,
&i.Accountid,
&i.Amoid,
&i.Field,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getExpiredCountPrivilege = `-- name: GetExpiredCountPrivilege :many
SELECT p.id, p.privilegeID, p.privilege_name, p.amount, p.created_at, a.user_id
FROM privileges p
@ -1661,8 +1632,11 @@ func (q *Queries) GetFieldByAmoID(ctx context.Context, amoid int32) (Field, erro
}
const getFieldsWithPagination = `-- name: GetFieldsWithPagination :many
WITH user_data AS (
SELECT AmoID FROM accountsAmo WHERE accountsAmo.AccountID = $1 AND accountsAmo.Deleted = false
)
SELECT f.id, f.amoid, f.code, f.accountid, f.name, f.entity, f.type, f.deleted, f.createdat, COUNT(*) OVER() as total_count
FROM fields f JOIN (SELECT AmoID FROM users WHERE users.AccountID = $1 AND Deleted = false) u ON f.AccountID = u.AmoID
FROM fields f JOIN user_data u ON f.AccountID = u.AmoID
WHERE f.Deleted = false
ORDER BY f.ID OFFSET ($2 - 1) * $3 LIMIT $3
`
@ -1817,8 +1791,11 @@ func (q *Queries) GetListStartQuiz(ctx context.Context, accountid string) ([]int
}
const getPipelinesWithPagination = `-- name: GetPipelinesWithPagination :many
WITH user_data AS (
SELECT AmoID FROM accountsAmo WHERE accountsAmo.AccountID = $1 AND accountsAmo.Deleted = false
)
SELECT p.id, p.amoid, p.accountid, p.name, p.isarchive, p.deleted, p.createdat, COUNT(*) OVER() as total_count
FROM pipelines p JOIN (SELECT AmoID FROM users WHERE users.AccountID = $1 AND Deleted = false) u ON p.AccountID = u.AmoID
FROM pipelines p JOIN user_data u ON p.AccountID = u.AmoID
WHERE p.Deleted = false
ORDER BY p.ID OFFSET ($2 - 1) * $3 LIMIT $3
`
@ -2432,8 +2409,11 @@ func (q *Queries) GetResultAnswers(ctx context.Context, id int64) ([]GetResultAn
}
const getStepsWithPagination = `-- name: GetStepsWithPagination :many
WITH user_data AS (
SELECT AmoID FROM accountsAmo WHERE accountsAmo.AccountID = $1 AND accountsAmo.Deleted = false
)
SELECT s.id, s.amoid, s.pipelineid, s.accountid, s.name, s.color, s.deleted, s.createdat, COUNT(*) OVER() as total_count
FROM steps s JOIN (SELECT AmoID FROM users WHERE users.AccountID = $1 AND Deleted = false) u ON s.AccountID = u.AmoID
FROM steps s JOIN user_data u ON s.AccountID = u.AmoID
WHERE s.Deleted = false AND PipelineID = $4
ORDER BY s.ID OFFSET ($2 - 1) * $3 LIMIT $3
`
@ -2496,8 +2476,11 @@ func (q *Queries) GetStepsWithPagination(ctx context.Context, arg GetStepsWithPa
}
const getTagsWithPagination = `-- name: GetTagsWithPagination :many
WITH user_data AS (
SELECT AmoID FROM accountsAmo WHERE accountsAmo.AccountID = $1 AND accountsAmo.Deleted = false
)
SELECT t.id, t.amoid, t.accountid, t.entity, t.name, t.color, t.deleted, t.createdat, COUNT(*) OVER() as total_count
FROM tags t JOIN (SELECT AmoID FROM users WHERE users.AccountID = $1 AND Deleted = false) u ON t.AccountID = u.AmoID
FROM tags t JOIN user_data u ON t.AccountID = u.AmoID
WHERE t.Deleted = false
ORDER BY t.ID OFFSET ($2 - 1) * $3 LIMIT $3
`
@ -2752,44 +2735,28 @@ func (q *Queries) GetUserTagsByID(ctx context.Context, accountid int32) ([]GetUs
}
const getUserUsersByID = `-- name: GetUserUsersByID :many
SELECT ID,AccountID,AmoID,Name,Email,Role,"Group",Subdomain,AmoUserID,Country
FROM users
WHERE AmoUserID = $1 AND Deleted = false
SELECT id, amoid, amouserid, name, email, role, "Group", deleted, createdat FROM usersAmo WHERE amoid = $1 AND Deleted = false
`
type GetUserUsersByIDRow 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"`
Subdomain string `db:"subdomain" json:"subdomain"`
Amouserid int32 `db:"amouserid" json:"amouserid"`
Country string `db:"country" json:"country"`
}
func (q *Queries) GetUserUsersByID(ctx context.Context, amouserid int32) ([]GetUserUsersByIDRow, error) {
rows, err := q.db.QueryContext(ctx, getUserUsersByID, amouserid)
func (q *Queries) GetUserUsersByID(ctx context.Context, amoid int32) ([]Usersamo, error) {
rows, err := q.db.QueryContext(ctx, getUserUsersByID, amoid)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetUserUsersByIDRow
var items []Usersamo
for rows.Next() {
var i GetUserUsersByIDRow
var i Usersamo
if err := rows.Scan(
&i.ID,
&i.Accountid,
&i.Amoid,
&i.Amouserid,
&i.Name,
&i.Email,
&i.Role,
&i.Group,
&i.Subdomain,
&i.Amouserid,
&i.Country,
&i.Deleted,
&i.Createdat,
); err != nil {
return nil, err
}
@ -2806,11 +2773,11 @@ func (q *Queries) GetUserUsersByID(ctx context.Context, amouserid int32) ([]GetU
const getUsersWithPagination = `-- name: GetUsersWithPagination :many
WITH user_data AS (
SELECT AmoID FROM users WHERE users.AccountID = $1 AND Deleted = false
SELECT AmoID FROM accountsAmo WHERE accountsAmo.AccountID = $1 AND accountsAmo.Deleted = false
)
SELECT u.id, u.accountid, u.amoid, u.name, u.email, u.role, u."Group", u.deleted, u.createdat, u.subdomain, u.amouserid, u.country, u.driveurl, COUNT(*) OVER() as total_count
FROM users u
JOIN user_data a ON u.AmoUserID = a.AmoID
SELECT u.id, u.amoid, u.amouserid, u.name, u.email, u.role, u."Group", u.deleted, u.createdat, COUNT(*) OVER() as total_count
FROM usersAmo u
JOIN user_data a ON u.AmoID = a.AmoID
WHERE u.Deleted = false
ORDER BY u.ID OFFSET ($2 - 1) * $3 LIMIT $3
`
@ -2822,20 +2789,16 @@ type GetUsersWithPaginationParams struct {
}
type GetUsersWithPaginationRow 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"`
Driveurl string `db:"driveurl" json:"driveurl"`
TotalCount int64 `db:"total_count" json:"total_count"`
ID int64 `db:"id" json:"id"`
Amoid int32 `db:"amoid" json:"amoid"`
Amouserid int32 `db:"amouserid" json:"amouserid"`
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 time.Time `db:"createdat" json:"createdat"`
TotalCount int64 `db:"total_count" json:"total_count"`
}
func (q *Queries) GetUsersWithPagination(ctx context.Context, arg GetUsersWithPaginationParams) ([]GetUsersWithPaginationRow, error) {
@ -2849,18 +2812,14 @@ func (q *Queries) GetUsersWithPagination(ctx context.Context, arg GetUsersWithPa
var i GetUsersWithPaginationRow
if err := rows.Scan(
&i.ID,
&i.Accountid,
&i.Amoid,
&i.Amouserid,
&i.Name,
&i.Email,
&i.Role,
&i.Group,
&i.Deleted,
&i.Createdat,
&i.Subdomain,
&i.Amouserid,
&i.Country,
&i.Driveurl,
&i.TotalCount,
); err != nil {
return nil, err
@ -2878,17 +2837,17 @@ func (q *Queries) GetUsersWithPagination(ctx context.Context, arg GetUsersWithPa
const gettingAmoUsersTrueResults = `-- name: GettingAmoUsersTrueResults :many
SELECT a.quiz_id,a.id,a.result,a.question_id,a.content,a.session,
(SELECT a2.utm
COALESCE((SELECT a2.utm
FROM answer a2
WHERE a2.start = true AND a2.session = a.session
LIMIT 1) AS utm
,t.accesstoken,r.accountid,r.fieldsrule,r.tagstoadd,r.performerid,r.stepid,r.pipelineid,(SELECT u.name FROM users u WHERE u.amoid = r.performerid) AS performer_name,u.subdomain,u.accountid,u.driveurl
LIMIT 1), '{}'::jsonb) AS utm
,t.accesstoken,r.accountid,r.fieldsrule,r.tagstoadd,r.performerid,r.stepid,r.pipelineid,(SELECT u.name FROM usersAmo u WHERE u.AmoUserID = r.performerid AND u.deleted = false) AS performer_name,u.subdomain,u.accountid,u.driveurl
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
INNER JOIN accountsAmo u ON q.accountid = u.accountid AND u.amoid = r.accountid
WHERE a.result = true
AND s.id IS NULL
AND a.deleted = false
@ -2904,7 +2863,7 @@ type GettingAmoUsersTrueResultsRow struct {
QuestionID int64 `db:"question_id" json:"question_id"`
Content sql.NullString `db:"content" json:"content"`
Session sql.NullString `db:"session" json:"session"`
Utm json.RawMessage `db:"utm" json:"utm"`
Utm interface{} `db:"utm" json:"utm"`
Accesstoken string `db:"accesstoken" json:"accesstoken"`
Accountid int32 `db:"accountid" json:"accountid"`
Fieldsrule json.RawMessage `db:"fieldsrule" json:"fieldsrule"`
@ -3038,6 +2997,23 @@ func (q *Queries) InsertAnswers(ctx context.Context, arg InsertAnswersParams) (A
return i, err
}
const insertContactAmo = `-- name: InsertContactAmo :one
INSERT INTO amoContact (AccountID, AmoID, Field) VALUES ($1, $2, $3) RETURNING AmoID
`
type InsertContactAmoParams struct {
Accountid int32 `db:"accountid" json:"accountid"`
Amoid int32 `db:"amoid" json:"amoid"`
Field string `db:"field" json:"field"`
}
func (q *Queries) InsertContactAmo(ctx context.Context, arg InsertContactAmoParams) (int32, error) {
row := q.db.QueryRowContext(ctx, insertContactAmo, arg.Accountid, arg.Amoid, arg.Field)
var amoid int32
err := row.Scan(&amoid)
return amoid, err
}
const insertPrivilege = `-- name: InsertPrivilege :exec
INSERT INTO privileges (privilegeID, account_id, privilege_name, amount, created_at) VALUES ($1, $2, $3, $4, $5)
`
@ -3439,7 +3415,7 @@ func (q *Queries) QuizCopyQid(ctx context.Context, arg QuizCopyQidParams) (QuizC
const setQuizSettings = `-- name: SetQuizSettings :one
INSERT INTO rules (AccountID, QuizID, PerformerID, PipelineID, StepID, FieldsRule,TagsToAdd)
SELECT u.AmoID AS AccountID,$1 AS QuizID,$2 AS PerformerID,$3 AS PipelineID,
$4 AS StepID,$5 AS FieldsRule,$6 AS TagsToAdd FROM users u WHERE u.AccountID = $7 AND u.Deleted = false
$4 AS StepID,$5 AS FieldsRule,$6 AS TagsToAdd FROM accountsamo u WHERE u.AccountID = $7 AND u.Deleted = false
RETURNING id
`
@ -3472,7 +3448,7 @@ const settingDealAmoStatus = `-- 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
JOIN accountsAmo AS u ON t.AccountID = u.AccountID
WHERE t.AccessToken = $4 AND u.Deleted = false
`
@ -3494,12 +3470,13 @@ func (q *Queries) SettingDealAmoStatus(ctx context.Context, arg SettingDealAmoSt
}
const softDeleteAccount = `-- 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)
)
WITH amoCompany AS (
SELECT AmoID FROM accountsAmo WHERE accountsAmo.AccountID = $1
),usersDel AS (
UPDATE usersAmo SET Deleted = true WHERE AmoID = (SELECT AmoID FROM amoCompany)
),
companyDel AS ( UPDATE accountsAmo SET Deleted = true WHERE AmoID = (SELECT AmoID FROM amoCompany)
)
DELETE FROM tokens WHERE tokens.AccountID = $1
`
@ -3545,9 +3522,73 @@ func (q *Queries) TemplateCopy(ctx context.Context, arg TemplateCopyParams) (int
return quiz_id, err
}
const updateAmoAccount = `-- name: UpdateAmoAccount :exec
UPDATE accountsAmo SET Name = $2, Subdomain = $3, Country = $4, DriveURL = $5 WHERE AccountID = $1 AND Deleted = false
`
type UpdateAmoAccountParams struct {
Accountid string `db:"accountid" json:"accountid"`
Name string `db:"name" json:"name"`
Subdomain string `db:"subdomain" json:"subdomain"`
Country string `db:"country" json:"country"`
Driveurl string `db:"driveurl" json:"driveurl"`
}
func (q *Queries) UpdateAmoAccount(ctx context.Context, arg UpdateAmoAccountParams) error {
_, err := q.db.ExecContext(ctx, updateAmoAccount,
arg.Accountid,
arg.Name,
arg.Subdomain,
arg.Country,
arg.Driveurl,
)
return err
}
const updateAmoAccountUser = `-- name: UpdateAmoAccountUser :exec
UPDATE usersAmo SET Name = $3, Email = $4, Role = $5, "Group" = $6
WHERE AmoID = $1 AND AmoUserID = $2 AND deleted = false
`
type UpdateAmoAccountUserParams struct {
Amoid int32 `db:"amoid" json:"amoid"`
Amouserid int32 `db:"amouserid" json:"amouserid"`
Name string `db:"name" json:"name"`
Email string `db:"email" json:"email"`
Role int32 `db:"role" json:"role"`
Group int32 `db:"Group" json:"Group"`
}
func (q *Queries) UpdateAmoAccountUser(ctx context.Context, arg UpdateAmoAccountUserParams) error {
_, err := q.db.ExecContext(ctx, updateAmoAccountUser,
arg.Amoid,
arg.Amouserid,
arg.Name,
arg.Email,
arg.Role,
arg.Group,
)
return err
}
const updateAmoContact = `-- name: UpdateAmoContact :exec
UPDATE amoContact SET Field = $1,AmoID=$3 WHERE ID = $2
`
type UpdateAmoContactParams struct {
Field string `db:"field" json:"field"`
ID int64 `db:"id" json:"id"`
Amoid int32 `db:"amoid" json:"amoid"`
}
func (q *Queries) UpdateAmoContact(ctx context.Context, arg UpdateAmoContactParams) error {
_, err := q.db.ExecContext(ctx, updateAmoContact, arg.Field, arg.ID, arg.Amoid)
return err
}
const updateFieldRules = `-- name: UpdateFieldRules :exec
UPDATE rules SET FieldsRule = $1
WHERE AccountID = (SELECT AmoID FROM users WHERE users.AccountID = $2 AND users.Deleted = false) AND QuizID = $3 AND Deleted = false
WHERE AccountID = (SELECT AmoID FROM accountsAmo WHERE accountsAmo.AccountID = $2 AND accountsAmo.Deleted = false) AND QuizID = $3 AND Deleted = false
`
type UpdateFieldRulesParams struct {
@ -3680,25 +3721,9 @@ func (q *Queries) UpdateTags(ctx context.Context, dollar_1 json.RawMessage) erro
return err
}
const updateUsers = `-- 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
`
func (q *Queries) UpdateUsers(ctx context.Context, dollar_1 json.RawMessage) error {
_, err := q.db.ExecContext(ctx, updateUsers, dollar_1)
return err
}
const updatingDealAmoStatus = `-- 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 AND u.Deleted = false)
WHERE DealID = $2 AND AccountID = (SELECT u.AmoID FROM tokens AS t JOIN accountsAmo AS u ON t.AccountID = u.AccountID WHERE t.AccessToken = $3 AND u.Deleted = false)
`
type UpdatingDealAmoStatusParams struct {
@ -3713,14 +3738,17 @@ func (q *Queries) UpdatingDealAmoStatus(ctx context.Context, arg UpdatingDealAmo
}
const webhookDelete = `-- name: WebhookDelete :exec
WITH userd AS (
UPDATE users SET Deleted = true WHERE AmoUserID = $1 RETURNING AccountID
WITH companyDel AS (
UPDATE accountsAmo SET Deleted = true WHERE accountsAmo.AmoID = $1 RETURNING AccountID
),
userDel AS (
UPDATE usersAmo SET Deleted = true WHERE AmoID = $1
)
DELETE FROM tokens WHERE AccountID IN (SELECT AccountID FROM userd)
DELETE FROM tokens WHERE AccountID IN (SELECT AccountID FROM companyDel)
`
func (q *Queries) WebhookDelete(ctx context.Context, amouserid int32) error {
_, err := q.db.ExecContext(ctx, webhookDelete, amouserid)
func (q *Queries) WebhookDelete(ctx context.Context, amoid int32) error {
_, err := q.db.ExecContext(ctx, webhookDelete, amoid)
return err
}

2
go.mod

@ -3,6 +3,7 @@ module penahub.gitlab.yandexcloud.net/backend/quiz/common.git
go 1.21.4
require (
github.com/ClickHouse/clickhouse-go v1.5.4
github.com/gofiber/fiber/v2 v2.52.0
github.com/golang-jwt/jwt/v5 v5.2.0
github.com/golang/protobuf v1.5.3
@ -19,6 +20,7 @@ require (
require (
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/google/go-cmp v0.6.0 // indirect

12
go.sum

@ -1,12 +1,19 @@
github.com/ClickHouse/clickhouse-go v1.5.4 h1:cKjXeYLNWVJIx2J1K6H2CqyRmfwVJVY1OV1coaaFcI0=
github.com/ClickHouse/clickhouse-go v1.5.4/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/bkaradzic/go-lz4 v1.0.0 h1:RXc4wYsyz985CkXXeX04y4VnZFGG8Rd43pRaHsOXAKk=
github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4=
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 h1:F1EaeKL/ta07PY/k9Os/UFtwERei2/XzGemhpGnBKNg=
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/gofiber/fiber/v2 v2.52.0 h1:S+qXi7y+/Pgvqq4DrSmREGiFwtB7Bu6+QFLuIHYw/UE=
github.com/gofiber/fiber/v2 v2.52.0/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
@ -20,6 +27,7 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
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=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=
@ -32,6 +40,7 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
@ -41,6 +50,7 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
github.com/minio/minio-go/v7 v7.0.69 h1:l8AnsQFyY1xiwa/DaQskY4NXSLA2yrGsW5iD9nRPVS0=
@ -52,6 +62,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=

@ -1,32 +1,30 @@
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"`
// урл объектного хранилища пользователя в амо
DriveURL string `json:"DriveURL"`
import "time"
type AmoAccountUser struct {
ID int64 `json:"id"` // ID пользователя
AmoID int32 `json:"amoID"` // ID компании в амо, к которой пользователь принадлежит
AmoUserID int32 `json:"amoUserID"` // ID пользователя в амо
Name string `json:"name"` // Имя
Email string `json:"email"` // Email
Role int32 `json:"role"` // Роль
Group int32 `json:"group"` // Группа
Deleted bool `json:"deleted"`
CreatedAt time.Time `json:"createdAt"`
}
type AmoAccount struct {
ID int64 `json:"id"` // ID компании
AccountID string `json:"accountID"` // ID аккаунта нас
AmoID int32 `json:"amoID"` // ID компании в амо
Name string `json:"name"` // Название
Deleted bool `json:"deleted"`
CreatedAt time.Time `json:"createdAt"`
Subdomain string `json:"subdomain"` // поддомен
Country string `json:"country"` // Страна
DriveURL string `json:"driveURL"` // URL объктного хранилища
Stale bool `json:"stale"` // флаг "не свежести" если с токенами все в порядке - false, если просрочились то true
}
type UserGroups struct {
@ -290,3 +288,11 @@ type AmoUsersTrueResults struct {
QuizAccountID string
DriveURL string
}
// возможно стоит добавить enum? тип ContactQuizConfig уже есть
type ContactAmo struct {
ID int64
AccountID int32 // id аккаунта в амо к которому привязан контакт
AmoID int32 // id контакта в амо
Field string // значение поля
}

@ -55,7 +55,7 @@ type UserListResp struct {
/* - общее количество юзеров, которые у нас закешированы для этого пользователя*/
Count int64 `json:"count"`
/* - список юзеров, которые были закешированы нашим сервисом*/
Items []User `json:"items"`
Items []AmoAccountUser `json:"items"`
}
type UserListStepsResp struct {

@ -6,6 +6,7 @@ import (
"encoding/json"
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal/sqlcgen"
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model"
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/pj_errors"
"time"
)
@ -39,20 +40,18 @@ func (r *AmoRepository) GettingUserWithPagination(ctx context.Context, req *mode
return nil, err
}
var count int64
var users []model.User
var users []model.AmoAccountUser
for _, row := range rows {
user := model.User{
user := model.AmoAccountUser{
ID: row.ID,
Accountid: row.Accountid,
AmoID: row.Amoid,
AmoUserID: row.Amouserid,
Name: row.Name,
Email: row.Email,
Group: row.Group,
Role: row.Role,
Createdat: row.Createdat.Time.Unix(),
Subdomain: row.Subdomain,
Amouserid: row.Amouserid,
Country: row.Country,
Deleted: row.Deleted,
CreatedAt: row.Createdat,
}
count = row.TotalCount
@ -75,41 +74,58 @@ func (r *AmoRepository) SoftDeleteAccount(ctx context.Context, accountID string)
return nil
}
func (r *AmoRepository) GetCurrentAccount(ctx context.Context, accountID string) (*model.User, error) {
row, err := r.queries.GetCurrentAccount(ctx, accountID)
func (r *AmoRepository) GetCurrentAccount(ctx context.Context, accountID string) (*model.AmoAccount, error) {
row, err := r.queries.GetCurrentCompany(ctx, accountID)
if err != nil {
return nil, err
}
user := model.User{
user := model.AmoAccount{
ID: row.ID,
Accountid: row.Accountid,
AccountID: row.Accountid,
AmoID: row.Amoid,
Name: row.Name,
Email: row.Email,
Role: row.Role,
Group: row.Group,
Createdat: row.Createdat.Time.Unix(),
Deleted: row.Deleted,
CreatedAt: row.Createdat,
Subdomain: row.Subdomain,
Amouserid: row.Amouserid,
Country: row.Country,
DriveURL: row.Driveurl,
}
_, err = r.queries.CheckExpiredToken(ctx, accountID)
if err != nil {
if err == sql.ErrNoRows {
user.Stale = false
return &user, nil
}
return nil, err
}
user.Stale = true
return &user, nil
}
func (r *AmoRepository) CreateAccount(ctx context.Context, accountID string, userInfo model.User) error {
func (r *AmoRepository) UpdateCurrentAccount(ctx context.Context, user model.AmoAccount) error {
err := r.queries.UpdateAmoAccount(ctx, sqlcgen.UpdateAmoAccountParams{
Accountid: user.AccountID,
Name: user.Name,
Subdomain: user.Subdomain,
Country: user.Country,
Driveurl: user.DriveURL,
})
if err != nil {
return err
}
return nil
}
func (r *AmoRepository) CreateAccount(ctx context.Context, userInfo model.AmoAccount) error {
err := r.queries.CreateAmoAccount(ctx, sqlcgen.CreateAmoAccountParams{
Accountid: accountID,
Accountid: userInfo.AccountID,
Amoid: userInfo.AmoID,
Name: userInfo.Name,
Email: userInfo.Email,
Role: userInfo.Role,
Group: userInfo.Group,
Createdat: sql.NullTime{Time: time.Now(), Valid: true},
Subdomain: userInfo.Subdomain,
Amouserid: userInfo.Amouserid,
Country: userInfo.Country,
Driveurl: userInfo.DriveURL,
})
@ -121,15 +137,17 @@ func (r *AmoRepository) CreateAccount(ctx context.Context, accountID string, use
return nil
}
func (r *AmoRepository) CheckMainUser(ctx context.Context, user model.User) error {
err := r.queries.CheckMainUser(ctx, sqlcgen.CheckMainUserParams{
Name: user.Name,
Group: user.Group,
Email: user.Email,
Role: user.Role,
Amoid: user.AmoID,
})
// todo возможно стоит обновлять еще и компанию пока не знаю
func (r *AmoRepository) AddAmoAccountUser(ctx context.Context, user model.AmoAccountUser) error {
err := r.queries.AddAmoAccountUser(ctx, sqlcgen.AddAmoAccountUserParams{
Amoid: user.AmoID,
Amouserid: user.AmoUserID,
Name: user.Name,
Email: user.Email,
Role: user.Role,
Group: user.Group,
})
if err != nil {
return err
}
@ -137,39 +155,18 @@ func (r *AmoRepository) CheckMainUser(ctx context.Context, user model.User) erro
return nil
}
func (r *AmoRepository) CheckAndUpdateUsers(ctx context.Context, users []model.User) error {
dollar1, err := json.Marshal(users)
func (r *AmoRepository) UpdateAmoAccountUser(ctx context.Context, user model.AmoAccountUser) error {
err := r.queries.UpdateAmoAccountUser(ctx, sqlcgen.UpdateAmoAccountUserParams{
Amoid: user.AmoID,
Amouserid: user.AmoUserID,
Name: user.Name,
Email: user.Email,
Role: user.Role,
Group: user.Group,
})
if err != nil {
return err
}
rows, err := r.queries.CheckUsers(ctx, dollar1)
if err != nil {
return err
}
if rows != nil {
var toUpdate []model.User
for _, row := range rows {
to := model.User{
AmoID: row.Amoid,
Name: row.Name,
Group: row.Group,
Role: row.Role,
Email: row.Email,
Amouserid: row.Amouserid,
}
toUpdate = append(toUpdate, to)
}
dollar1, err := json.Marshal(toUpdate)
if err != nil {
return err
}
err = r.queries.UpdateUsers(ctx, dollar1)
if err != nil {
return err
}
}
return nil
}
@ -195,27 +192,25 @@ func (r *AmoRepository) DeleteUsers(ctx context.Context, ids []int64) error {
return nil
}
func (r *AmoRepository) GetUserUsersByID(ctx context.Context, amoUserID int32) ([]model.User, error) {
func (r *AmoRepository) GetUserUsersByID(ctx context.Context, amoUserID int32) ([]model.AmoAccountUser, error) {
rows, err := r.queries.GetUserUsersByID(ctx, amoUserID)
if err != nil {
return nil, err
}
var users []model.User
var users []model.AmoAccountUser
for _, row := range rows {
user := model.User{
user := model.AmoAccountUser{
ID: row.ID,
Accountid: row.Accountid,
AmoID: row.Amoid,
AmoUserID: row.Amouserid,
Name: row.Name,
Email: row.Email,
Group: row.Group,
Role: row.Role,
Subdomain: row.Subdomain,
Amouserid: row.Amouserid,
Country: row.Country,
Deleted: row.Deleted,
CreatedAt: row.Createdat,
}
users = append(users, user)
}
@ -256,31 +251,6 @@ func (r *AmoRepository) WebhookUpdate(ctx context.Context, token model.Token) er
return nil
}
// воркер запускается каждые 5 минут, поэтомму ищем токены котторые исекают менее чем через 10 минут отдаем их на обноление
func (r *AmoRepository) CheckExpired(ctx context.Context) ([]model.Token, error) {
rows, err := r.queries.CheckExpired(ctx)
if err != nil {
return nil, err
}
var tokens []model.Token
for _, row := range rows {
token := model.Token{
AccountID: row.Accountid,
AccessToken: row.Accesstoken,
RefreshToken: row.Refreshtoken,
AuthCode: row.Authcode,
Expiration: row.Expiration.Unix(),
CreatedAt: row.Createdat.Time.Unix(),
}
tokens = append(tokens, token)
}
return tokens, nil
}
func (r *AmoRepository) GetAllTokens(ctx context.Context) ([]model.Token, error) {
rows, err := r.queries.GetAllTokens(ctx)
if err != nil {
@ -1038,7 +1008,7 @@ func (r *AmoRepository) GettingAmoUsersTrueResults(ctx context.Context) ([]model
}
var utm model.UTMSavingMap
err = json.Unmarshal(row.Utm, &utm)
err = json.Unmarshal(row.Utm.([]byte), &utm)
if err != nil {
return nil, err
}
@ -1111,3 +1081,60 @@ func (r *AmoRepository) UpdatingDealAmoStatus(ctx context.Context, deps SaveDeal
return nil
}
// методы для contact в амо
func (r *AmoRepository) GetExistingContactAmo(ctx context.Context, accountID int32, fields []string) (map[int32][]model.ContactAmo, error) {
rows, err := r.queries.GetExistingContactAmo(ctx, sqlcgen.GetExistingContactAmoParams{
Accountid: accountID,
Column2: fields,
})
if err != nil {
if err == sql.ErrNoRows {
return nil, pj_errors.ErrNotFound
}
return nil, err
}
result := make(map[int32][]model.ContactAmo)
for _, row := range rows {
result[row.Amoid] = append(result[row.Amoid], model.ContactAmo{
ID: row.ID,
AmoID: row.Amoid,
AccountID: row.Accountid,
Field: row.Field,
})
}
return result, nil
}
func (r *AmoRepository) InsertContactAmo(ctx context.Context, val model.ContactAmo) (int32, error) {
amoID, err := r.queries.InsertContactAmo(ctx, sqlcgen.InsertContactAmoParams{
Accountid: val.AccountID,
Amoid: val.AmoID,
Field: val.Field,
})
if err != nil {
return 0, err
}
return amoID, err
}
func (r *AmoRepository) UpdateAmoContact(ctx context.Context, id int64, field string, newAmoID int32) error {
err := r.queries.UpdateAmoContact(ctx, sqlcgen.UpdateAmoContactParams{
Field: field,
ID: id,
Amoid: newAmoID,
})
if err != nil {
return err
}
return nil
}

@ -15,16 +15,14 @@ type Deps struct {
}
type AnswerRepository struct {
queries *sqlcgen.Queries
pool *sql.DB
answerMinio *StorerAnswer
queries *sqlcgen.Queries
pool *sql.DB
}
func NewAnswerRepository(deps Deps) *AnswerRepository {
return &AnswerRepository{
queries: deps.Queries,
pool: deps.Pool,
answerMinio: deps.AnswerMinio,
queries: deps.Queries,
pool: deps.Pool,
}
}
@ -111,17 +109,6 @@ 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)
if err != nil {
fmt.Println("GetAnswerURL dal answer minio answer", err)
return nil, err
}
row.Content = sql.NullString{String: fmt.Sprintf("%s|%s", fileURL, row.Content.String), Valid: true}
}*/
resultAnswer := model.ResultAnswer{
Content: row.Content.String,
CreatedAt: row.CreatedAt.Time,

@ -0,0 +1,55 @@
package answer
import (
"context"
"database/sql"
"fmt"
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal/sqlcgen"
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model"
)
type WorkerAnswerRepository struct {
queries *sqlcgen.Queries
pool *sql.DB
answerMinio *StorerAnswer
}
func NewWorkerAnswerRepo(deps Deps) *WorkerAnswerRepository {
return &WorkerAnswerRepository{
queries: deps.Queries,
pool: deps.Pool,
answerMinio: deps.AnswerMinio,
}
}
func (r *WorkerAnswerRepository) GetAllAnswersByQuizID(ctx context.Context, session string) ([]model.ResultAnswer, error) {
var results []model.ResultAnswer
rows, err := r.queries.GetAllAnswersByQuizID(ctx, sql.NullString{String: session, Valid: true})
if err != nil {
return nil, err
}
for _, row := range rows {
if row.Questiontype == model.TypeFile && r.answerMinio != nil {
fmt.Println("GALL", row.Qid, row.QuestionID, row.Content)
fileURL, err := r.answerMinio.GetAnswerURL(ctx, row.Qid.UUID.String(), row.QuestionID, row.Content.String)
if err != nil {
fmt.Println("GetAnswerURL dal answer minio answer", err)
return nil, err
}
row.Content = sql.NullString{String: fmt.Sprintf("%s|%s", fileURL, row.Content.String), Valid: true}
}
resultAnswer := model.ResultAnswer{
Content: row.Content.String,
CreatedAt: row.CreatedAt.Time,
QuestionID: uint64(row.QuestionID),
AnswerID: uint64(row.ID),
}
results = append(results, resultAnswer)
}
return results, nil
}

@ -0,0 +1,199 @@
package statistics
import (
"context"
"database/sql"
"fmt"
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/utils"
"sort"
"strings"
)
type DepsClick struct {
Conn *sql.DB
}
type StatisticClick struct {
conn *sql.DB
}
func NewClickStatistic(ctx context.Context, deps DepsClick) (*StatisticClick, error) {
s := &StatisticClick{
conn: deps.Conn,
}
err := s.checkMW(ctx)
if err != nil {
fmt.Println("error check material view existing", err)
return nil, err
}
return s, nil
}
func (s *StatisticClick) checkMW(ctx context.Context) error {
query := `
CREATE MATERIALIZED VIEW IF NOT EXISTS mv_last_answers_events
ENGINE = MergeTree()
ORDER BY (ctxsession, event_time) POPULATE AS
SELECT
event_time, ctxsession, ctxquizid, ctxquestionid, ctxidint, message,ctxquiz
FROM statistics
WHERE message IN ('InfoQuizOpen', 'InfoAnswer', 'InfoResult') AND event_level = 'info';
`
_, err := s.conn.ExecContext(ctx, query)
if err != nil {
return err
}
return nil
}
type Statistic struct {
Count int64
QuestionID int64
}
type PipeLineStatsResp map[int64][]Statistic
// пример:
//"[0, 116783, 116810]"
//"[0, 116783, 116798]"
//"[0, 116783, 116798, 116831]"
//"[0, 116783, 116810, 116849]"
//[0]
//"[0, 116783]"
//"[0, 116783, 116810, 116843]"
//SELECT DISTINCT last_que, reversed
//FROM ( SELECT groupArray(ctxquestionid) AS reversed, arraySlice(arrayReverse(groupArray(ctxquestionid)), 1, 1)[1] AS last_que
//FROM statistics WHERE ctxquizid = 26276 GROUP BY ctxsession ) AS sub;
func (s *StatisticClick) getFunnel(ctx context.Context, quizID int64, from uint64, to uint64) (map[int64][]int64, error) {
query := `
SELECT DISTINCT last_que, reversed
FROM ( SELECT groupUniqArray(ctxquestionid) AS reversed, arraySlice(arrayReverse(groupArray(ctxquestionid)), 1, 1)[1] AS last_que
FROM mv_last_answers_events WHERE ctxquizid = ? AND event_time BETWEEN ? AND ? GROUP BY ctxsession ) AS sub;
`
rows, err := s.conn.QueryContext(ctx, query, quizID, from, to)
if err != nil {
return nil, err
}
defer rows.Close()
funnel := make(map[int64][]int64)
for rows.Next() {
var lastQue int64
var reversed []int64
if err := rows.Scan(&lastQue, &reversed); err != nil {
return nil, err
}
funnel[lastQue] = reversed
}
if err := rows.Err(); err != nil {
return nil, err
}
result := make(map[int64][]int64)
keys := make([]int64, 0, len(funnel))
for key := range funnel {
keys = append(keys, key)
}
sort.Slice(keys, func(i, j int) bool {
return keys[i] < keys[j]
})
for _, lastQue := range keys {
reversed := funnel[lastQue]
found := false
for _, otherLastQue := range keys {
if otherLastQue != lastQue {
otherReversed := funnel[otherLastQue]
sort.Slice(otherReversed, func(i, j int) bool {
return otherReversed[i] < otherReversed[j]
})
index := utils.BinarySearch(lastQue, otherReversed)
if index {
found = true
break
}
}
}
if !found {
result[lastQue] = reversed
}
}
return result, nil
}
func (s *StatisticClick) GetPipelinesStatistics(ctx context.Context, quizID int64, from uint64, to uint64) (PipeLineStatsResp, error) {
pipelines := make(PipeLineStatsResp)
funnel, err := s.getFunnel(ctx, quizID, from, to)
if err != nil {
return nil, err
}
for lastQue, idS := range funnel {
sesCount, err := s.countSession(ctx, quizID, from, to, idS)
if err != nil {
return nil, err
}
for _, queID := range idS {
if sessionCount, ok := sesCount[queID]; ok {
pipeline := Statistic{
QuestionID: queID,
Count: sessionCount,
}
pipelines[lastQue] = append(pipelines[lastQue], pipeline)
}
}
}
return pipelines, nil
}
func (s *StatisticClick) countSession(ctx context.Context, quizID int64, from uint64, to uint64, questionIDs []int64) (map[int64]int64, error) {
placeholders := make([]string, len(questionIDs))
args := make([]interface{}, len(questionIDs)+3)
args[0] = quizID
for i, id := range questionIDs {
placeholders[i] = "?"
args[i+1] = id
}
args[len(args)-2] = from
args[len(args)-1] = to
query := fmt.Sprintf(`
SELECT ctxquestionid, COUNT(DISTINCT ctxsession) AS session_count
FROM statistics
WHERE ctxquizid = ? AND ctxquestionid IN (%s) AND event_time BETWEEN ? AND ?
GROUP BY ctxquestionid;
`, strings.Join(placeholders, ","))
rows, err := s.conn.QueryContext(ctx, query, args...)
if err != nil {
return nil, err
}
defer rows.Close()
counts := make(map[int64]int64)
for rows.Next() {
var questionID int64
var count int64
err := rows.Scan(&questionID, &count)
if err != nil {
return nil, err
}
counts[questionID] = count
}
if err := rows.Err(); err != nil {
return nil, err
}
return counts, nil
}

@ -30,6 +30,10 @@ packages:
- "./dal/schema/000012_init.down.sql"
- "./dal/schema/000013_init.up.sql"
- "./dal/schema/000013_init.down.sql"
- "./dal/schema/000014_init.up.sql"
- "./dal/schema/000014_init.down.sql"
- "./dal/schema/000016_init.up.sql"
- "./dal/schema/000016_init.down.sql"
- "./dal/schema/000017_init.up.sql"
- "./dal/schema/000017_init.down.sql"
engine: "postgresql"

20
utils/binary_search.go Normal file

@ -0,0 +1,20 @@
package utils
func BinarySearch(target int64, array []int64) bool {
left := 0
right := len(array) - 1
for left <= right {
mid := (left + right) / 2
if array[mid] == target {
return true
} else if array[mid] < target {
left = mid + 1
} else if array[mid] > target {
right = mid - 1
}
}
return false
}