diff --git a/app/app.go b/app/app.go index 2a57e17..3fbda83 100644 --- a/app/app.go +++ b/app/app.go @@ -5,10 +5,10 @@ import ( "errors" "fmt" "gitea.pena/SQuiz/answerer/clients" + "gitea.pena/SQuiz/answerer/dal" + "gitea.pena/SQuiz/answerer/healthchecks" + "gitea.pena/SQuiz/answerer/middleware" "gitea.pena/SQuiz/answerer/service" - "gitea.pena/SQuiz/common/dal" - "gitea.pena/SQuiz/common/healthchecks" - "gitea.pena/SQuiz/common/middleware" "github.com/gofiber/fiber/v2" "github.com/skeris/appInit" "go.uber.org/zap" @@ -79,7 +79,7 @@ func New(ctx context.Context, opts interface{}, ver appInit.Version) (appInit.Co zap.String("SvcBuildTime", ver.BuildTime), ) - pgdal, err := dal.New(ctx, options.PostgresCredentials, nil) + pgdal, err := dal.New(ctx, options.PostgresCredentials) if err != nil { return nil, err } diff --git a/dal/dal.go b/dal/dal.go new file mode 100644 index 0000000..e2f515b --- /dev/null +++ b/dal/dal.go @@ -0,0 +1,255 @@ +package dal + +import ( + "context" + "database/sql" + _ "embed" + "encoding/json" + "fmt" + "gitea.pena/SQuiz/answerer/dal/sqlcgen" + "gitea.pena/SQuiz/answerer/model" + _ "github.com/ClickHouse/clickhouse-go" + "github.com/google/uuid" + "github.com/lib/pq" + _ "github.com/lib/pq" + "time" +) + +type DAL struct { + pool *sql.DB + queries *sqlcgen.Queries +} + +func New(ctx context.Context, cred string) (*DAL, 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) + + return &DAL{ + pool: pool, + queries: queries, + }, nil +} + +func (d *DAL) Close(ctx context.Context) error { + err := d.pool.Close() + if err != nil { + return err + } + return nil +} + +func (d *DAL) GetQuizByQid(ctx context.Context, qid string) (model.Quiz, error) { + _, err := uuid.Parse(qid) + if err != nil { + return model.Quiz{}, err + } + + fmt.Println("QUID", ` +SELECT * FROM quiz +WHERE + deleted = false AND + archived = false AND + status = 'start' AND + qid = $1; +`) + rows, err := d.pool.QueryContext(ctx, ` +SELECT * FROM quiz +WHERE + deleted = false AND + archived = false AND + (status = 'start' OR status = 'ai') AND + qid = $1; +`, qid) + if err != nil { + return model.Quiz{}, err + } + defer rows.Close() + + if !rows.Next() { + return model.Quiz{}, rows.Err() + } + + var piece model.Quiz + pIds := pq.Int32Array{} + if err := rows.Scan( + &piece.Id, + &piece.Qid, + &piece.AccountId, + &piece.Deleted, + &piece.Archived, + &piece.Fingerprinting, + &piece.Repeatable, + &piece.NotePrevented, + &piece.MailNotifications, + &piece.UniqueAnswers, + &piece.Super, + &piece.GroupId, + &piece.Name, + &piece.Description, + &piece.Config, + &piece.Status, + &piece.Limit, + &piece.DueTo, + &piece.TimeOfPassing, + &piece.Pausable, + &piece.Version, + &piece.VersionComment, + &pIds, + &piece.CreatedAt, + &piece.UpdatedAt, + &piece.QuestionsCount, + &piece.PassedCount, + &piece.AverageTime, + &piece.SessionCount, + &piece.GigaChat, + ); err != nil { + return model.Quiz{}, err + } + + piece.ParentIds = pIds + + return piece, nil +} + +func (d *DAL) GetQuestionListByIDs(ctx context.Context, ids []int32) ([]model.Question, error) { + rows, err := d.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, + Auditory: row.Auditory, + } + + questions = append(questions, question) + } + + return questions, nil +} + +func (d *DAL) CreateQuestion(ctx context.Context, record *model.Question) (uint64, error) { + params := sqlcgen.InsertQuestionParams{ + QuizID: int64(record.QuizId), + Title: record.Title, + Description: sql.NullString{String: record.Description, Valid: true}, + Questiontype: record.Type, + Required: sql.NullBool{Bool: record.Required, Valid: true}, + Page: sql.NullInt16{Int16: int16(record.Page), Valid: true}, + Content: sql.NullString{String: record.Content, Valid: true}, + ParentIds: record.ParentIds, + UpdatedAt: sql.NullTime{Time: time.Now(), Valid: true}, + Session: record.Session, + Auditory: record.Auditory, + } + + data, err := d.queries.InsertQuestion(ctx, params) + if err != nil { + return 0, err + } + + record.Id = uint64(data.ID) + record.CreatedAt = data.CreatedAt.Time + record.UpdatedAt = data.UpdatedAt.Time + + return record.Id, nil +} + +func (d *DAL) CreateAnswers(ctx context.Context, answers []model.Answer, session, fp string, quizID uint64) ([]model.Answer, []error) { + var ( + createdAnswers []model.Answer + errs []error + ) + + tx, err := d.pool.BeginTx(ctx, nil) + if err != nil { + return nil, []error{err} + } + + for _, ans := range answers { + if ans.Utm == nil { + ans.Utm = make(model.UTMSavingMap) + } + utmJSON, err := json.Marshal(ans.Utm) + if err != nil { + return nil, []error{err} + } + + params := sqlcgen.InsertAnswersParams{ + Content: sql.NullString{String: ans.Content, Valid: true}, + QuizID: int64(quizID), + QuestionID: int64(ans.QuestionId), + Fingerprint: sql.NullString{String: fp, Valid: true}, + Session: sql.NullString{String: session, Valid: true}, + Result: sql.NullBool{Bool: ans.Result, Valid: true}, + Email: ans.Email, + Device: ans.Device, + DeviceType: ans.DeviceType, + Ip: ans.IP, + Browser: ans.Browser, + Os: ans.OS, + Start: ans.Start, + Utm: utmJSON, + Version: ans.Version, + } + + row, err := d.queries.InsertAnswers(ctx, params) + createdAnswer := model.Answer{ + Id: uint64(row.ID), + Content: row.Content.String, + QuizId: uint64(row.QuizID), + QuestionId: uint64(row.QuestionID), + Fingerprint: row.Fingerprint.String, + Session: row.Session.String, + Result: row.Result.Bool, + New: row.New.Bool, + Email: row.Email, + DeviceType: row.DeviceType, + Device: row.Device, + OS: row.Os, + Browser: row.Browser, + IP: row.Ip, + Start: row.Start, + Version: row.Version, + } + + if err != nil { + errs = append(errs, err) + } else { + createdAnswers = append(createdAnswers, createdAnswer) + } + } + + err = tx.Commit() + if err != nil { + errs = append(errs, err) + return nil, errs + } + + return createdAnswers, errs +} diff --git a/dal/db_query/queries.sql b/dal/db_query/queries.sql new file mode 100644 index 0000000..780de63 --- /dev/null +++ b/dal/db_query/queries.sql @@ -0,0 +1,39 @@ +-- name: GetQuestionListByIDs :many +SELECT * FROM question WHERE id = ANY($1::int[]) AND deleted = FALSE; + +-- name: InsertQuestion :one +INSERT INTO question ( + quiz_id, + title, + description, + questiontype, + required, + page, + content, + parent_ids, + updated_at, + session, + auditory +) +VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11) + RETURNING id, created_at, updated_at; + +-- name: InsertAnswers :one +INSERT INTO answer( + content, + quiz_id, + question_id, + fingerprint, + session, + result, + email, + device_type, + device, + os, + browser, + ip, + start, + utm, + version +) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15) + RETURNING *; \ No newline at end of file diff --git a/dal/schema/000001_init.down.sql b/dal/schema/000001_init.down.sql new file mode 100644 index 0000000..54966a0 --- /dev/null +++ b/dal/schema/000001_init.down.sql @@ -0,0 +1,24 @@ +-- Drop indexes +DROP INDEX IF EXISTS subquizes; +DROP INDEX IF EXISTS birthtime; +DROP INDEX IF EXISTS groups; +DROP INDEX IF EXISTS timeouted; +DROP INDEX IF EXISTS active ON quiz; +DROP INDEX IF EXISTS questiontype; +DROP INDEX IF EXISTS required; +DROP INDEX IF EXISTS relation; +DROP INDEX IF EXISTS active ON question; + +-- Drop tables +DROP TABLE IF EXISTS privileges; +DROP TABLE IF EXISTS answer; +DROP TABLE IF EXISTS question; +DROP TABLE IF EXISTS quiz; +DROP TABLE IF EXISTS account; + +-- Drop types +DO $$ +BEGIN +DROP TYPE IF EXISTS question_type; +DROP TYPE IF EXISTS quiz_status; +END$$; \ No newline at end of file diff --git a/dal/schema/000001_init.up.sql b/dal/schema/000001_init.up.sql new file mode 100644 index 0000000..9aefa67 --- /dev/null +++ b/dal/schema/000001_init.up.sql @@ -0,0 +1,120 @@ +-- Create types +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'question_type') THEN +CREATE TYPE question_type AS ENUM ( + 'variant', + 'images', + 'varimg', + 'emoji', + 'text', + 'select', + 'date', + 'number', + 'file', + 'page', + 'rating' + ); +END IF; + + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'quiz_status') THEN +CREATE TYPE quiz_status AS ENUM ( + 'draft', + 'template', + 'stop', + 'start', + 'timeout', + 'offlimit' + ); +END IF; + + CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; +END$$; + +-- Create tables +CREATE TABLE IF NOT EXISTS account ( + id UUID PRIMARY KEY, + user_id VARCHAR(24), + email VARCHAR(50), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + deleted BOOLEAN DEFAULT false + ); + +CREATE TABLE IF NOT EXISTS quiz ( + id bigserial UNIQUE NOT NULL PRIMARY KEY, + qid uuid DEFAULT uuid_generate_v4(), + accountid varchar(30) NOT NULL, + deleted boolean DEFAULT false, + archived boolean DEFAULT false, + fingerprinting boolean DEFAULT false, + repeatable boolean DEFAULT false, + note_prevented boolean DEFAULT false, + mail_notifications boolean DEFAULT false, + unique_answers boolean DEFAULT false, + super boolean DEFAULT false, + group_id bigint DEFAULT 0, + name varchar(280), + description text, + config text, + status quiz_status DEFAULT 'draft', + limit_answers integer DEFAULT 0, + due_to integer DEFAULT 0, + time_of_passing integer DEFAULT 0, + pausable boolean DEFAULT false, + version smallint DEFAULT 0, + version_comment text DEFAULT '', + parent_ids integer[], + created_at timestamp DEFAULT CURRENT_TIMESTAMP, + updated_at timestamp DEFAULT CURRENT_TIMESTAMP, + questions_count integer DEFAULT 0, + answers_count integer DEFAULT 0, + average_time_passing integer DEFAULT 0 + ); + +CREATE TABLE IF NOT EXISTS question ( + id bigserial UNIQUE NOT NULL PRIMARY KEY, + quiz_id bigint NOT NULL, + title varchar(512) NOT NULL, + description text, + questiontype question_type DEFAULT 'text', + required boolean DEFAULT false, + deleted boolean DEFAULT false, + page smallint DEFAULT 0, + content text, + version smallint DEFAULT 0, + parent_ids integer[], + created_at timestamp DEFAULT CURRENT_TIMESTAMP, + updated_at timestamp DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT quiz_relation FOREIGN KEY(quiz_id) REFERENCES quiz(id) + ); + +CREATE TABLE IF NOT EXISTS answer ( + id bigserial UNIQUE NOT NULL PRIMARY KEY, + content text, + quiz_id bigint NOT NULL REFERENCES quiz(id), + question_id bigint NOT NULL REFERENCES question(id), + fingerprint varchar(1024), + session varchar(20), + created_at timestamp DEFAULT CURRENT_TIMESTAMP + ); + +CREATE TABLE IF NOT EXISTS privileges ( + id SERIAL PRIMARY KEY, + privilegeID VARCHAR(50), + account_id UUID, + privilege_name VARCHAR(255), + amount INT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (account_id) REFERENCES account (id) + ); + +-- Create indexes +CREATE INDEX IF NOT EXISTS active ON question(deleted) WHERE deleted=false; +CREATE INDEX IF NOT EXISTS relation ON question(quiz_id DESC); +CREATE INDEX IF NOT EXISTS required ON question(required DESC); +CREATE INDEX IF NOT EXISTS questiontype ON question(questiontype); +CREATE INDEX IF NOT EXISTS active ON quiz(deleted, archived, status) WHERE deleted = false AND archived = false AND status = 'start'; +CREATE INDEX IF NOT EXISTS timeouted ON quiz(due_to DESC) WHERE deleted = false AND due_to <> 0 AND status <> 'timeout'; +CREATE INDEX IF NOT EXISTS groups ON quiz(super) WHERE super = true; +CREATE INDEX IF NOT EXISTS birthtime ON quiz(created_at DESC); +CREATE INDEX IF NOT EXISTS subquizes ON quiz(group_id DESC) WHERE group_id <> 0; diff --git a/dal/schema/000002_init.down.sql b/dal/schema/000002_init.down.sql new file mode 100644 index 0000000..0aa3d9e --- /dev/null +++ b/dal/schema/000002_init.down.sql @@ -0,0 +1 @@ +ALTER TABLE answer DROP COLUMN IF EXISTS result; diff --git a/dal/schema/000002_init.up.sql b/dal/schema/000002_init.up.sql new file mode 100644 index 0000000..4083153 --- /dev/null +++ b/dal/schema/000002_init.up.sql @@ -0,0 +1 @@ +ALTER TABLE answer ADD COLUMN result BOOLEAN DEFAULT FALSE; \ No newline at end of file diff --git a/dal/schema/000003_init.down.sql b/dal/schema/000003_init.down.sql new file mode 100644 index 0000000..94e352f --- /dev/null +++ b/dal/schema/000003_init.down.sql @@ -0,0 +1 @@ +ALTER TABLE quiz DROP COLUMN IF EXISTS sessions_count; diff --git a/dal/schema/000003_init.up.sql b/dal/schema/000003_init.up.sql new file mode 100644 index 0000000..a292d62 --- /dev/null +++ b/dal/schema/000003_init.up.sql @@ -0,0 +1 @@ +ALTER TABLE quiz ADD COLUMN sessions_count integer; diff --git a/dal/schema/000004_init.down.sql b/dal/schema/000004_init.down.sql new file mode 100644 index 0000000..b6d5ec4 --- /dev/null +++ b/dal/schema/000004_init.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE quiz DROP COLUMN IF EXISTS new; +ALTER TABLE quiz DROP COLUMN IF EXISTS deleted; \ No newline at end of file diff --git a/dal/schema/000004_init.up.sql b/dal/schema/000004_init.up.sql new file mode 100644 index 0000000..9dc5591 --- /dev/null +++ b/dal/schema/000004_init.up.sql @@ -0,0 +1,2 @@ +ALTER TABLE answer ADD COLUMN new BOOLEAN DEFAULT TRUE; +ALTER TABLE answer ADD COLUMN deleted BOOLEAN DEFAULT FALSE; \ No newline at end of file diff --git a/dal/schema/000005_init.down.sql b/dal/schema/000005_init.down.sql new file mode 100644 index 0000000..ccae4f5 --- /dev/null +++ b/dal/schema/000005_init.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE answer DROP COLUMN IF EXISTS email; +DROP INDEX IF EXISTS answer_email_unique_idx; \ No newline at end of file diff --git a/dal/schema/000005_init.up.sql b/dal/schema/000005_init.up.sql new file mode 100644 index 0000000..f2bfe97 --- /dev/null +++ b/dal/schema/000005_init.up.sql @@ -0,0 +1,2 @@ +ALTER TABLE answer ADD COLUMN email VARCHAR(50) NOT NULL DEFAULT ''; +CREATE UNIQUE INDEX IF NOT EXISTS answer_email_unique_idx ON answer (quiz_id, email) WHERE email <> ''; \ No newline at end of file diff --git a/dal/schema/000006_init.down.sql b/dal/schema/000006_init.down.sql new file mode 100644 index 0000000..355ba3b --- /dev/null +++ b/dal/schema/000006_init.down.sql @@ -0,0 +1,6 @@ +ALTER TABLE answer +DROP COLUMN device_type, +DROP COLUMN device, +DROP COLUMN os, +DROP COLUMN browser, +DROP COLUMN ip; \ No newline at end of file diff --git a/dal/schema/000006_init.up.sql b/dal/schema/000006_init.up.sql new file mode 100644 index 0000000..4dc1487 --- /dev/null +++ b/dal/schema/000006_init.up.sql @@ -0,0 +1,6 @@ +ALTER TABLE answer +ADD COLUMN device_type VARCHAR(50) NOT NULL DEFAULT '', +ADD COLUMN device VARCHAR(100) NOT NULL DEFAULT '', +ADD COLUMN os VARCHAR(100) NOT NULL DEFAULT '', +ADD COLUMN browser VARCHAR(100) NOT NULL DEFAULT '', +ADD COLUMN ip VARCHAR(50) NOT NULL DEFAULT ''; \ No newline at end of file diff --git a/dal/schema/000007_init.down.sql b/dal/schema/000007_init.down.sql new file mode 100644 index 0000000..374c55d --- /dev/null +++ b/dal/schema/000007_init.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE answer +DROP COLUMN start; \ No newline at end of file diff --git a/dal/schema/000007_init.up.sql b/dal/schema/000007_init.up.sql new file mode 100644 index 0000000..6b41425 --- /dev/null +++ b/dal/schema/000007_init.up.sql @@ -0,0 +1,2 @@ +ALTER TABLE answer +ADD COLUMN start BOOLEAN NOT NULL DEFAULT FALSE; \ No newline at end of file diff --git a/dal/schema/000008_init.down.sql b/dal/schema/000008_init.down.sql new file mode 100644 index 0000000..4f06fcb --- /dev/null +++ b/dal/schema/000008_init.down.sql @@ -0,0 +1,4 @@ +ALTER TABLE answer +ALTER COLUMN device TYPE VARCHAR(100) NOT NULL DEFAULT '', +ALTER COLUMN os TYPE VARCHAR(100) NOT NULL DEFAULT '', +ALTER COLUMN browser TYPE VARCHAR(100) NOT NULL DEFAULT '', diff --git a/dal/schema/000008_init.up.sql b/dal/schema/000008_init.up.sql new file mode 100644 index 0000000..fcba321 --- /dev/null +++ b/dal/schema/000008_init.up.sql @@ -0,0 +1,4 @@ +ALTER TABLE answer +ALTER COLUMN device TYPE VARCHAR(1024), +ALTER COLUMN os TYPE VARCHAR(1024), +ALTER COLUMN browser TYPE VARCHAR(1024); diff --git a/dal/schema/000009_init.down.sql b/dal/schema/000009_init.down.sql new file mode 100644 index 0000000..34fb3c6 --- /dev/null +++ b/dal/schema/000009_init.down.sql @@ -0,0 +1,4 @@ +ALTER TABLE answer +ALTER COLUMN device VARCHAR(100) NOT NULL DEFAULT '', +ALTER COLUMN os VARCHAR(100) NOT NULL DEFAULT '', +ALTER COLUMN browser VARCHAR(100) NOT NULL DEFAULT '', diff --git a/dal/schema/000009_init.up.sql b/dal/schema/000009_init.up.sql new file mode 100644 index 0000000..d0c81cf --- /dev/null +++ b/dal/schema/000009_init.up.sql @@ -0,0 +1,4 @@ +ALTER TABLE answer +ALTER COLUMN device TYPE VARCHAR(512), +ALTER COLUMN os TYPE VARCHAR(512), +ALTER COLUMN browser TYPE VARCHAR(512); diff --git a/dal/schema/000010_init.down.sql b/dal/schema/000010_init.down.sql new file mode 100644 index 0000000..02f12e4 --- /dev/null +++ b/dal/schema/000010_init.down.sql @@ -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; \ No newline at end of file diff --git a/dal/schema/000010_init.up.sql b/dal/schema/000010_init.up.sql new file mode 100644 index 0000000..37ee29e --- /dev/null +++ b/dal/schema/000010_init.up.sql @@ -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 +); \ No newline at end of file diff --git a/dal/schema/000011_init.down.sql b/dal/schema/000011_init.down.sql new file mode 100644 index 0000000..bcaa9ff --- /dev/null +++ b/dal/schema/000011_init.down.sql @@ -0,0 +1,15 @@ +ALTER TABLE answer +DROP COLUMN utm; + +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 +); + +ALTER TABLE rules + ADD COLUMN UTMS INTEGER[]; \ No newline at end of file diff --git a/dal/schema/000011_init.up.sql b/dal/schema/000011_init.up.sql new file mode 100644 index 0000000..f268036 --- /dev/null +++ b/dal/schema/000011_init.up.sql @@ -0,0 +1,5 @@ +ALTER TABLE answer +ADD COLUMN utm jsonb NOT NULL DEFAULT '{}'; +DROP TABLE IF EXISTS utms; +ALTER TABLE rules +DROP COLUMN IF EXISTS UTMS; \ No newline at end of file diff --git a/dal/schema/000012_init.down.sql b/dal/schema/000012_init.down.sql new file mode 100644 index 0000000..0f34183 --- /dev/null +++ b/dal/schema/000012_init.down.sql @@ -0,0 +1,4 @@ +DROP INDEX IF EXISTS idx_unique_users; +DROP INDEX IF EXISTS idx_unique_rules; +CREATE UNIQUE INDEX idx_unique_users ON users (amoID); +CREATE UNIQUE INDEX idx_unique_rules ON rules (accountID, QuizID); \ No newline at end of file diff --git a/dal/schema/000012_init.up.sql b/dal/schema/000012_init.up.sql new file mode 100644 index 0000000..87913a8 --- /dev/null +++ b/dal/schema/000012_init.up.sql @@ -0,0 +1,4 @@ +DROP INDEX IF EXISTS idx_unique_users; +DROP INDEX IF EXISTS idx_unique_rules; +CREATE UNIQUE INDEX idx_unique_users ON users (amoID) WHERE Deleted = false; +CREATE UNIQUE INDEX idx_unique_rules ON rules (accountID, QuizID) WHERE Deleted = false; \ No newline at end of file diff --git a/dal/schema/000013_init.down.sql b/dal/schema/000013_init.down.sql new file mode 100644 index 0000000..26cb247 --- /dev/null +++ b/dal/schema/000013_init.down.sql @@ -0,0 +1,4 @@ +ALTER TABLE rules +DROP COLUMN IF EXISTS TagsToAdd; +ALTER TABLE users +DROP COLUMN IF EXISTS DriveURL; \ No newline at end of file diff --git a/dal/schema/000013_init.up.sql b/dal/schema/000013_init.up.sql new file mode 100644 index 0000000..44d4155 --- /dev/null +++ b/dal/schema/000013_init.up.sql @@ -0,0 +1,4 @@ +ALTER TABLE rules +ADD COLUMN TagsToAdd JSONB NOT NULL DEFAULT '{}'; +ALTER TABLE users +ADD COLUMN DriveURL text NOT NULL DEFAULT ''; diff --git a/dal/schema/000014_init.down.sql b/dal/schema/000014_init.down.sql new file mode 100644 index 0000000..5ddb16f --- /dev/null +++ b/dal/schema/000014_init.down.sql @@ -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; \ No newline at end of file diff --git a/dal/schema/000014_init.up.sql b/dal/schema/000014_init.up.sql new file mode 100644 index 0000000..9542bbb --- /dev/null +++ b/dal/schema/000014_init.up.sql @@ -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 +); \ No newline at end of file diff --git a/dal/schema/000015_init.down.sql b/dal/schema/000015_init.down.sql new file mode 100644 index 0000000..e69de29 diff --git a/dal/schema/000015_init.up.sql b/dal/schema/000015_init.up.sql new file mode 100644 index 0000000..3985517 --- /dev/null +++ b/dal/schema/000015_init.up.sql @@ -0,0 +1,19 @@ +UPDATE answer +SET content = + CASE + WHEN content ~ '|' THEN + regexp_replace(content, '<\/?tr[^>]*>|<\/?td[^>]*>', '', 'g') + WHEN content ~ '[^<]+' THEN + regexp_replace(content, '([^<]+)', '\1', 'g') + WHEN content ~ ']*src="([^"]*)"[^>]*' THEN + regexp_replace(content, ']*src="\s*"[^>]*', '', 'g') + ELSE content + END; + +UPDATE answer +SET content = + CASE + WHEN content ~ ']*src=["'']?([^"''>]+)["'']?[^>]*>)', '\1\3', 'g') + ELSE content + END; diff --git a/dal/schema/000016_init.down.sql b/dal/schema/000016_init.down.sql new file mode 100644 index 0000000..28b2333 --- /dev/null +++ b/dal/schema/000016_init.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS amoContact; \ No newline at end of file diff --git a/dal/schema/000016_init.up.sql b/dal/schema/000016_init.up.sql new file mode 100644 index 0000000..a4866d0 --- /dev/null +++ b/dal/schema/000016_init.up.sql @@ -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 +) \ No newline at end of file diff --git a/dal/schema/000017_init.down.sql b/dal/schema/000017_init.down.sql new file mode 100644 index 0000000..c2969e1 --- /dev/null +++ b/dal/schema/000017_init.down.sql @@ -0,0 +1,11 @@ +ALTER table question DROP column session; +ALTER TABLE account ADD column email varchar(50) NOT NULL default ''; + +DO $$ + BEGIN + IF EXISTS (SELECT 1 FROM pg_type WHERE typname = 'leadtargettype') THEN + DROP TYPE LeadTargetType; + END IF; + END $$; + +DROP TABLE IF EXISTS leadtarget; diff --git a/dal/schema/000017_init.up.sql b/dal/schema/000017_init.up.sql new file mode 100644 index 0000000..0f5aa5e --- /dev/null +++ b/dal/schema/000017_init.up.sql @@ -0,0 +1,31 @@ +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_enum + WHERE enumlabel = 'ai' AND enumtypid = 'quiz_status'::regtype + ) THEN +ALTER TYPE quiz_status ADD VALUE 'ai'; +END IF; +END $$; + +ALTER TABLE question ADD column session varchar(20) NOT NULL DEFAULT ''; + +AlTER TABLE account DROP column email; + +DO $$ + BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'leadtargettype') THEN + CREATE TYPE LeadTargetType AS ENUM ('mail', 'telegram', 'whatsapp'); + END IF; + END $$; + +CREATE TABLE IF NOT EXISTS leadtarget( + ID BIGSERIAL UNIQUE NOT NULL PRIMARY KEY, + AccountID varchar(30) NOT NULL, + Type LeadTargetType NOT NULL, + QuizID integer NOT NULL DEFAULT 0, + Target text NOT NULL, + InviteLink text NOT NULL DEFAULT '', + Deleted boolean NOT NULL DEFAULT false, + CreatedAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); diff --git a/dal/schema/000018_init.down.sql b/dal/schema/000018_init.down.sql new file mode 100644 index 0000000..6186e3f --- /dev/null +++ b/dal/schema/000018_init.down.sql @@ -0,0 +1,12 @@ +DROP TABLE IF EXISTS gigachatAudience; + +DROP TABLE IF EXISTS tgAccounts; + +DO $$ + BEGIN + IF EXISTS (SELECT 1 FROM pg_type WHERE typname = 'tgAccountStatus') THEN + DROP TYPE TgAccountStatus; + END IF; + END $$; + +DROP INDEX IF EXISTS idx_apiid_apihash; diff --git a/dal/schema/000018_init.up.sql b/dal/schema/000018_init.up.sql new file mode 100644 index 0000000..d6cf7fa --- /dev/null +++ b/dal/schema/000018_init.up.sql @@ -0,0 +1,29 @@ +CREATE TABLE IF NOT EXISTS gigachatAudience ( + ID BIGSERIAL UNIQUE NOT NULL PRIMARY KEY, + QuizID BIGINT NOT NULL, + Sex BOOLEAN NOT NULL, + Age VARCHAR(5) NOT NULL, + Deleted BOOLEAN NOT NULL DEFAULT FALSE, + CreatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT fk_quiz FOREIGN KEY (QuizID) REFERENCES quiz(ID) +); + +DO $$ + BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'tgAccountStatus') THEN + CREATE TYPE TgAccountStatus AS ENUM ('active', 'inactive', 'ban'); + END IF; + END $$; + +CREATE TABLE IF NOT EXISTS tgAccounts ( + id bigserial primary key , + ApiID integer not null, + ApiHash text not null , + PhoneNumber text not null , + Password text not null , + Status TgAccountStatus not null, + Deleted bool not null default false, + CreatedAt timestamp not null default current_timestamp +); + +CREATE UNIQUE INDEX idx_apiid_apihash ON tgAccounts (ApiID, ApiHash) WHERE Deleted = false; diff --git a/dal/schema/000019_init.down.sql b/dal/schema/000019_init.down.sql new file mode 100644 index 0000000..e76faa2 --- /dev/null +++ b/dal/schema/000019_init.down.sql @@ -0,0 +1,11 @@ +ALTER TABLE account +ALTER COLUMN user_id DROP NOT NULL, +ALTER COLUMN created_at DROP NOT NULL, +ALTER COLUMN deleted DROP NOT NULL; + +ALTER TABLE privileges +ALTER COLUMN privilegeID DROP NOT NULL, +ALTER COLUMN account_id DROP NOT NULL, +ALTER COLUMN privilege_name DROP NOT NULL, +ALTER COLUMN amount DROP NOT NULL, +ALTER COLUMN created_at DROP NOT NULL; \ No newline at end of file diff --git a/dal/schema/000019_init.up.sql b/dal/schema/000019_init.up.sql new file mode 100644 index 0000000..ac84b67 --- /dev/null +++ b/dal/schema/000019_init.up.sql @@ -0,0 +1,11 @@ +ALTER TABLE account +ALTER COLUMN user_id SET NOT NULL, +ALTER COLUMN created_at SET NOT NULL, +ALTER COLUMN deleted SET NOT NULL; + +ALTER TABLE privileges +ALTER COLUMN privilegeID SET NOT NULL, +ALTER COLUMN account_id SET NOT NULL, +ALTER COLUMN privilege_name SET NOT NULL, +ALTER COLUMN amount SET NOT NULL, +ALTER COLUMN created_at SET NOT NULL; \ No newline at end of file diff --git a/dal/schema/000020_init.down.sql b/dal/schema/000020_init.down.sql new file mode 100644 index 0000000..b2ec5da --- /dev/null +++ b/dal/schema/000020_init.down.sql @@ -0,0 +1,19 @@ +DROP TABLE IF EXISTS telegram_integration; +DROP TABLE IF EXISTS respondent_state; +DROP TABLE IF EXISTS telegram_integration_instance; +drop table if exists telegram_user_quiz_result; +DROP INDEX results_for_quiz; + +DO $$ + BEGIN + IF EXISTS (SELECT 1 FROM pg_type WHERE typname = 'bot_status') THEN + DROP TYPE bot_status; + END IF; +END $$; + +DO $$ + BEGIN + IF EXISTS (SELECT 1 FROM pg_type WHERE typname = 'user_result_tg_status') THEN + DROP TYPE user_result_tg_status; + END IF; +END $$; diff --git a/dal/schema/000020_init.up.sql b/dal/schema/000020_init.up.sql new file mode 100644 index 0000000..aee2174 --- /dev/null +++ b/dal/schema/000020_init.up.sql @@ -0,0 +1,57 @@ +DO $$ + BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'bot_status') THEN + CREATE TYPE bot_status AS ENUM ('active','stopped', 'banned'); + END IF; + END $$; + +DO $$ + BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'user_result_tg_status') THEN + CREATE TYPE user_result_tg_status AS ENUM ('in_progress','completed'); + END IF; +END $$; + +CREATE INDEX IF NOT EXISTS results_for_quiz ON answer(quiz_id, result); + +CREATE TABLE IF NOT EXISTS telegram_integration( + id bigserial unique not null primary key, + accountid varchar(30) NOT NULL, -- связь с аккаунтом квиза + quizID integer NOT NULL , -- айдишник квиза, с которым связываем интеграцию + bot_token text NOT NULL, -- токен бота + bot_name text NOT NULL DEFAULT '', -- имя бота + repeatable boolean DEFAULT false, -- флаг возможности повторить опрос + deleted boolean NOT NULL default false, -- флаг удаленного + status bot_status NOT NULL DEFAULT 'active', + instance_id integer NOT NULL default 0 +); + +CREATE TABLE IF NOT EXISTS respondent_state( + id bigserial unique not null primary key, + telegram_id integer NOT NULL, -- айдишник пользователя в телеграме, скорее всего чат id + quizID integer NOT NULL, -- айдишник квиза, который проходится этим респондентом + state bigint NOT NULL, + lang text NOT NULL default '', -- язык опрашиваемого + contact text NOT NULL default '', -- ник пользователя в телеге, если доступен + finish boolean not null default false, -- статус, пройден опрос или нет + session varchar(20) -- сессия пользователя +); + +CREATE TABLE IF NOT EXISTS telegram_integration_instance( + id bigserial unique not null primary key, + checkout bigint not null , -- время "подтверждения активности". в идеале должно быть актуальным, но отличаться от всех остальных минимум на 10 секунд. т.е. если у нас есть бот с временем в 10:10:10 и бот с временем в 10:10:31, а сейчас 10:10:32, то валидные слоты это 10:10:21 и 10:10:41. но как это сделать я не знаю. как вариант, можно просто взять бот с максимальным временем и сделать ему +10. и наверное это правильныйй вариант + limited integer not null, --константа с лимитом ботов + amount integer not null -- актуальное количество ботов +); + +CREATE TABLE IF NOT EXISTS telegram_user_quiz_result( + id BIGSERIAL PRIMARY KEY, + bot_id BIGINT NOT NULL, + quiz_id BIGINT NOT NULL, + current_field TEXT, + user_answer TEXT DEFAULT '', + state user_result_tg_status not null default 'in_progress', + session varchar(20) NOT NULL , + question_id bigint NOT NULL, -- id вопроса результата + iter integer not null +) diff --git a/dal/schema/000021_init.down.sql b/dal/schema/000021_init.down.sql new file mode 100644 index 0000000..1324e47 --- /dev/null +++ b/dal/schema/000021_init.down.sql @@ -0,0 +1,35 @@ +DROP TABLE IF EXISTS BitrixTokens; +DROP TABLE IF EXISTS StepBitrix; + +DROP TABLE IF EXISTS PipelineBitrix; + +DROP TABLE IF EXISTS BitrixAccounts; + +DROP TABLE IF EXISTS BitrixAccountUsers; + +DROP TABLE IF EXISTS BitrixFields; + +DROP TABLE IF EXISTS BitrixRule; + +DROP TABLE IF EXISTS bitrixCRMStatuses; + +DROP TABLE IF EXISTS bitrixContact; + +DO $$ +BEGIN + IF EXISTS (SELECT 1 FROM pg_type WHERE typname = 'CustomFieldsType') THEN +DROP TYPE CustomFieldsType; +END IF; +END $$; + +DO $$ +BEGIN + IF EXISTS (SELECT 1 FROM pg_type WHERE typname = 'FieldsType') THEN +DROP TYPE FieldsType; +END IF; +END $$; + +DROP INDEX IF EXISTS idx_unique_pipeline_bitrix; +DROP INDEX IF EXISTS idx_unique_step_bitrix; +DROP INDEX IF EXISTS idx_unique_field_bitrix; +ALTER TABLE answer DROP COLUMN IF EXISTS version; diff --git a/dal/schema/000021_init.up.sql b/dal/schema/000021_init.up.sql new file mode 100644 index 0000000..d28c75e --- /dev/null +++ b/dal/schema/000021_init.up.sql @@ -0,0 +1,149 @@ +CREATE TABLE IF NOT EXISTS BitrixTokens ( + 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 NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'FieldsType') THEN +CREATE TYPE FieldsType AS ENUM ( + 'CRM_LEAD', + 'CRM_COMPANY', + 'CRM_CONTACT', + 'CRM_DEAL', + 'CRM_INVOICE', + 'CRM_SMART_INVOICE', + 'CRM_QUOTE', + 'CRM_REQUISITE' +); +END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'CustomFieldsType') THEN +CREATE TYPE CustomFieldsType AS ENUM ( + 'string', + 'integer', + 'double', + 'boolean', + 'datetime', + 'enumeration', + 'iblock_section', + 'iblock_element', + 'employee', + 'crm_status', + 'crm', + 'address', + 'money', + 'url', + 'file', + 'crm_pena_tag' + ); +END IF; +END $$; + + +CREATE TABLE IF NOT EXISTS BitrixFields ( + ID BIGSERIAL UNIQUE NOT NULL PRIMARY KEY, + BitrixID VARCHAR(255) NOT NULL, -- Айдишник field в битриксе + AccountID VARCHAR(255) NOT NULL,-- ID портала битрикса + EntityID FieldsType NOT NULL,-- тип поля + FieldName VARCHAR(255) NOT NULL,-- имя поля + EditFromLabel VARCHAR(255) NOT NULL,-- заголовок вопроса + FieldType CustomFieldsType NOT NULL, -- тип поля + Deleted BOOLEAN NOT NULL DEFAULT FALSE, + CreatedAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE IF NOT EXISTS BitrixAccountUsers ( + ID BIGSERIAL UNIQUE NOT NULL PRIMARY KEY, + AccountID VARCHAR(255) NOT NULL, -- ID портала битрикса + BitrixIDUserID VARCHAR(255) NOT NULL, -- ID пользователя в битриксе + Name VARCHAR(255) NOT NULL default '', -- Имя + LastName VARCHAR(255) NOT NULL default '',-- Фамилия + SecondName VARCHAR(255) NOT NULL default '', -- Отчество + Title VARCHAR(255) NOT NULL default '', + Email VARCHAR(255) NOT NULL default '', -- Почта + UFDepartment INT[], -- Массив департаментов + WorkPosition VARCHAR(255) NOT NULL default '', -- Должность + Deleted BOOLEAN NOT NULL DEFAULT FALSE, + CreatedAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE IF NOT EXISTS BitrixAccounts ( + ID BIGSERIAL UNIQUE NOT NULL PRIMARY KEY, + AccountID VARCHAR(30) NOT NULL, -- ID аккаунта у нас + BitrixID VARCHAR(255) NOT NULL, -- ID портала битрикса + Deleted BOOLEAN NOT NULL DEFAULT FALSE, + CreatedAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + Subdomain VARCHAR(50) NOT NULL -- поддомен +); + +CREATE TABLE IF NOT EXISTS PipelineBitrix ( + ID BIGSERIAL UNIQUE NOT NULL PRIMARY KEY, + BitrixID INT NOT NULL, -- Айдишник воронки в битриксе + AccountID VARCHAR(255) NOT NULL, -- ID портала битрикса + Name VARCHAR(255) NOT NULL, -- Название воронки + EntityTypeId INT NOT NULL, -- Тип по номерам + Deleted BOOLEAN NOT NULL DEFAULT FALSE, + CreatedAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT validEntityType CHECK (EntityTypeId IN (1, 2, 3, 4, 5, 7, 8, 31)) +); + +CREATE TABLE IF NOT EXISTS StepBitrix ( + ID BIGSERIAL UNIQUE NOT NULL PRIMARY KEY, + AccountID VARCHAR(255) NOT NULL, -- ID портала битрикса + BitrixID VARCHAR(255) NOT NULL, -- Айдишник состояния в битриксе + EntityID VARCHAR(255) NOT NULL, -- Тип сущности + StatusID VARCHAR(255) NOT NULL, -- Текстовый формат ID статуса + Name VARCHAR(255) NOT NULL default '', -- Название + NameInit VARCHAR(255) NOT NULL default '', -- возможно это изначальное название + Color VARCHAR(50) NOT NULL default '', -- Цвет + PipelineID INT NOT NULL, -- ID воронки + Deleted BOOLEAN NOT NULL DEFAULT FALSE, + CreatedAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE IF NOT EXISTS BitrixRule ( + ID BIGSERIAL UNIQUE NOT NULL PRIMARY KEY, + AccountID VARCHAR(255) NOT NULL, -- ID портала битрикса + QuizID INT NOT NULL, -- ID квиза на которое вешается правило + PerformerID VARCHAR(255) NOT NULL, -- ID пользователя в битриксе который ответсвенный + PipelineID INT NOT NULL, -- ID воронки + TypeID VARCHAR(255) NOT NULL, -- шаг сделки только с "ENTITY_ID":"DEAL_TYPE","STATUS_ID":"SALE" + StageID VARCHAR(255) NOT NULL, -- стадия сделки, шаг "ENTITY_ID":"DEAL_STAGE","STATUS_ID":"NEW" + SourceID VARCHAR(255) NOT NULL, -- тип источника, шаг "ENTITY_ID":"SOURCE","STATUS_ID":"CALL" + StatusID VARCHAR(255) NOT NULL, -- тип источника, шаг "ENTITY_ID":"STATUS","STATUS_ID":"IN_PROCESS" + FieldsRule JSONB NOT NULL DEFAULT '{}', -- вложенная структура + TagsToAdd JSONB NOT NULL DEFAULT '{}', -- структура тегов которые надо дбавлять + Deleted BOOLEAN NOT NULL DEFAULT FALSE, + CreatedAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + + LeadFlag BOOLEAN NOT NULL DEFAULT FALSE -- если true то делаем лид а не сделку +); + +CREATE TABLE IF NOT EXISTS bitrixCRMStatuses ( + ID BIGSERIAL UNIQUE NOT NULL PRIMARY KEY, + AccountID VARCHAR(255) 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 NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE IF NOT EXISTS bitrixContact ( + ID BIGSERIAL UNIQUE NOT NULL PRIMARY KEY, + AccountID VARCHAR(255) NOT NULL, -- ID "компании" в амо + BitrixID INT NOT NULL, -- ID контакта в амо + Field text NOT NULL DEFAULT '' -- значение чего то email? phone? etc +); + +CREATE UNIQUE INDEX idx_unique_pipeline_bitrix ON PipelineBitrix (BitrixID, AccountID) WHERE Deleted = false; +CREATE UNIQUE INDEX idx_unique_step_bitrix ON StepBitrix (BitrixID, AccountID) WHERE Deleted = false; +CREATE UNIQUE INDEX idx_unique_field_bitrix ON BitrixFields (BitrixID, AccountID, EntityID); +ALTER TABLE answer ADD COLUMN version integer NOT NULL default 0; diff --git a/dal/schema/000022_init.down.sql b/dal/schema/000022_init.down.sql new file mode 100644 index 0000000..80de4b6 --- /dev/null +++ b/dal/schema/000022_init.down.sql @@ -0,0 +1 @@ +ALTER TABLE question DROP COLUMN IF EXISTS auditory; \ No newline at end of file diff --git a/dal/schema/000022_init.up.sql b/dal/schema/000022_init.up.sql new file mode 100644 index 0000000..dfa9cb5 --- /dev/null +++ b/dal/schema/000022_init.up.sql @@ -0,0 +1 @@ +ALTER TABLE question ADD COLUMN auditory BIGINT NOT NULL DEFAULT 0; diff --git a/dal/schema/000023_init.down.sql b/dal/schema/000023_init.down.sql new file mode 100644 index 0000000..e69de29 diff --git a/dal/schema/000023_init.up.sql b/dal/schema/000023_init.up.sql new file mode 100644 index 0000000..94c6a96 --- /dev/null +++ b/dal/schema/000023_init.up.sql @@ -0,0 +1,14 @@ +BEGIN; +ALTER TABLE gigachatAudience RENAME COLUMN Sex TO Sex_old; +ALTER TABLE gigachatAudience ADD COLUMN Sex INTEGER; +UPDATE gigachatAudience +SET Sex = CASE + WHEN Sex_old = FALSE THEN 0 + WHEN Sex_old = TRUE THEN 1 + ELSE 2 +END; +ALTER TABLE gigachatAudience DROP COLUMN Sex_old; +ALTER TABLE gigachatAudience ALTER COLUMN Sex SET NOT NULL; +ALTER TABLE gigachatAudience + ADD CONSTRAINT check_sex_valid_values CHECK (Sex IN (0, 1, 2)); +COMMIT; diff --git a/dal/schema/000024_init.down.sql b/dal/schema/000024_init.down.sql new file mode 100644 index 0000000..249ee63 --- /dev/null +++ b/dal/schema/000024_init.down.sql @@ -0,0 +1 @@ +ALTER TABLE quiz DROP COLUMN IF EXISTS gigachat; \ No newline at end of file diff --git a/dal/schema/000024_init.up.sql b/dal/schema/000024_init.up.sql new file mode 100644 index 0000000..21deb16 --- /dev/null +++ b/dal/schema/000024_init.up.sql @@ -0,0 +1 @@ +ALTER TABLE quiz ADD COLUMN gigachat boolean NOT NULL DEFAULT false; \ No newline at end of file diff --git a/dal/schema/000025_init.down.sql b/dal/schema/000025_init.down.sql new file mode 100644 index 0000000..b1b24af --- /dev/null +++ b/dal/schema/000025_init.down.sql @@ -0,0 +1 @@ +drop table if exists quiz_utm; \ No newline at end of file diff --git a/dal/schema/000025_init.up.sql b/dal/schema/000025_init.up.sql new file mode 100644 index 0000000..a36273f --- /dev/null +++ b/dal/schema/000025_init.up.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS quiz_utm ( + id bigserial UNIQUE NOT NULL PRIMARY KEY, + quizID bigint not null, + utm text not null default '', + deleted boolean not null default false, + created_at TIMESTAMP not null DEFAULT CURRENT_TIMESTAMP +); \ No newline at end of file diff --git a/dal/schema/000026_init.down.sql b/dal/schema/000026_init.down.sql new file mode 100644 index 0000000..a4c0cfa --- /dev/null +++ b/dal/schema/000026_init.down.sql @@ -0,0 +1,2 @@ +DROP INDEX IF EXISTS idx_quiz_privilege_unique; +drop table if exists quiz_privilege_usage; \ No newline at end of file diff --git a/dal/schema/000026_init.up.sql b/dal/schema/000026_init.up.sql new file mode 100644 index 0000000..cc91ba5 --- /dev/null +++ b/dal/schema/000026_init.up.sql @@ -0,0 +1,10 @@ +CREATE TABLE quiz_privilege_usage ( + id bigserial UNIQUE NOT NULL PRIMARY KEY, + quiz_id BIGINT NOT NULL REFERENCES quiz(id) ON DELETE CASCADE, + privilege_id VARCHAR(50) NOT NULL, + used_count INT NOT NULL DEFAULT 0, + created_at TIMESTAMP not null DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP not null DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX idx_quiz_privilege_unique ON quiz_privilege_usage (quiz_id, privilege_id); \ No newline at end of file diff --git a/dal/sqlcgen/db.go b/dal/sqlcgen/db.go new file mode 100644 index 0000000..3bd707e --- /dev/null +++ b/dal/sqlcgen/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.29.0 + +package sqlcgen + +import ( + "context" + "database/sql" +) + +type DBTX interface { + ExecContext(context.Context, string, ...interface{}) (sql.Result, error) + PrepareContext(context.Context, string) (*sql.Stmt, error) + QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) + QueryRowContext(context.Context, string, ...interface{}) *sql.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/dal/sqlcgen/models.go b/dal/sqlcgen/models.go new file mode 100644 index 0000000..c266b2c --- /dev/null +++ b/dal/sqlcgen/models.go @@ -0,0 +1,400 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.29.0 + +package sqlcgen + +import ( + "database/sql" + "encoding/json" + "time" + + "github.com/google/uuid" +) + +type Account struct { + ID uuid.UUID `db:"id" json:"id"` + UserID string `db:"user_id" json:"user_id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + Deleted bool `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"` + 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"` + QuizID int64 `db:"quiz_id" json:"quiz_id"` + QuestionID int64 `db:"question_id" json:"question_id"` + Fingerprint sql.NullString `db:"fingerprint" json:"fingerprint"` + Session sql.NullString `db:"session" json:"session"` + CreatedAt sql.NullTime `db:"created_at" json:"created_at"` + Result sql.NullBool `db:"result" json:"result"` + New sql.NullBool `db:"new" json:"new"` + Deleted sql.NullBool `db:"deleted" json:"deleted"` + Email string `db:"email" json:"email"` + DeviceType string `db:"device_type" json:"device_type"` + Device string `db:"device" json:"device"` + Os string `db:"os" json:"os"` + Browser string `db:"browser" json:"browser"` + Ip string `db:"ip" json:"ip"` + Start bool `db:"start" json:"start"` + Utm json.RawMessage `db:"utm" json:"utm"` + Version int32 `db:"version" json:"version"` +} + +type Bitrixaccount struct { + ID int64 `db:"id" json:"id"` + Accountid string `db:"accountid" json:"accountid"` + Bitrixid string `db:"bitrixid" json:"bitrixid"` + Deleted bool `db:"deleted" json:"deleted"` + Createdat time.Time `db:"createdat" json:"createdat"` + Subdomain string `db:"subdomain" json:"subdomain"` +} + +type Bitrixaccountuser struct { + ID int64 `db:"id" json:"id"` + Accountid string `db:"accountid" json:"accountid"` + Bitrixiduserid string `db:"bitrixiduserid" json:"bitrixiduserid"` + Name string `db:"name" json:"name"` + Lastname string `db:"lastname" json:"lastname"` + Secondname string `db:"secondname" json:"secondname"` + Title string `db:"title" json:"title"` + Email string `db:"email" json:"email"` + Ufdepartment []int32 `db:"ufdepartment" json:"ufdepartment"` + Workposition string `db:"workposition" json:"workposition"` + Deleted bool `db:"deleted" json:"deleted"` + Createdat time.Time `db:"createdat" json:"createdat"` +} + +type Bitrixcontact struct { + ID int64 `db:"id" json:"id"` + Accountid string `db:"accountid" json:"accountid"` + Bitrixid int32 `db:"bitrixid" json:"bitrixid"` + Field string `db:"field" json:"field"` +} + +type Bitrixcrmstatus struct { + ID int64 `db:"id" json:"id"` + Accountid string `db:"accountid" json:"accountid"` + Dealid int32 `db:"dealid" json:"dealid"` + Answerid int64 `db:"answerid" json:"answerid"` + Status string `db:"status" json:"status"` + Createdat time.Time `db:"createdat" json:"createdat"` +} + +type Bitrixfield struct { + ID int64 `db:"id" json:"id"` + Bitrixid string `db:"bitrixid" json:"bitrixid"` + Accountid string `db:"accountid" json:"accountid"` + Entityid interface{} `db:"entityid" json:"entityid"` + Fieldname string `db:"fieldname" json:"fieldname"` + Editfromlabel string `db:"editfromlabel" json:"editfromlabel"` + Fieldtype interface{} `db:"fieldtype" json:"fieldtype"` + Deleted bool `db:"deleted" json:"deleted"` + Createdat time.Time `db:"createdat" json:"createdat"` +} + +type Bitrixrule struct { + ID int64 `db:"id" json:"id"` + Accountid string `db:"accountid" json:"accountid"` + Quizid int32 `db:"quizid" json:"quizid"` + Performerid string `db:"performerid" json:"performerid"` + Pipelineid int32 `db:"pipelineid" json:"pipelineid"` + Typeid string `db:"typeid" json:"typeid"` + Stageid string `db:"stageid" json:"stageid"` + Sourceid string `db:"sourceid" json:"sourceid"` + Statusid string `db:"statusid" json:"statusid"` + Fieldsrule json.RawMessage `db:"fieldsrule" json:"fieldsrule"` + Tagstoadd json.RawMessage `db:"tagstoadd" json:"tagstoadd"` + Deleted bool `db:"deleted" json:"deleted"` + Createdat time.Time `db:"createdat" json:"createdat"` + Leadflag bool `db:"leadflag" json:"leadflag"` +} + +type Bitrixtoken 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 time.Time `db:"createdat" json:"createdat"` +} + +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 Gigachataudience struct { + ID int64 `db:"id" json:"id"` + Quizid int64 `db:"quizid" json:"quizid"` + Age string `db:"age" json:"age"` + Deleted bool `db:"deleted" json:"deleted"` + Createdat sql.NullTime `db:"createdat" json:"createdat"` + Sex int32 `db:"sex" json:"sex"` +} + +type Leadtarget struct { + ID int64 `db:"id" json:"id"` + Accountid string `db:"accountid" json:"accountid"` + Type interface{} `db:"type" json:"type"` + Quizid int32 `db:"quizid" json:"quizid"` + Target string `db:"target" json:"target"` + Invitelink string `db:"invitelink" json:"invitelink"` + Deleted bool `db:"deleted" json:"deleted"` + Createdat time.Time `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 Pipelinebitrix struct { + ID int64 `db:"id" json:"id"` + Bitrixid int32 `db:"bitrixid" json:"bitrixid"` + Accountid string `db:"accountid" json:"accountid"` + Name string `db:"name" json:"name"` + Entitytypeid int32 `db:"entitytypeid" json:"entitytypeid"` + Deleted bool `db:"deleted" json:"deleted"` + Createdat time.Time `db:"createdat" json:"createdat"` +} + +type Privilege struct { + ID int32 `db:"id" json:"id"` + Privilegeid string `db:"privilegeid" json:"privilegeid"` + AccountID uuid.UUID `db:"account_id" json:"account_id"` + PrivilegeName string `db:"privilege_name" json:"privilege_name"` + Amount int32 `db:"amount" json:"amount"` + CreatedAt time.Time `db:"created_at" json:"created_at"` +} + +type Question struct { + ID int64 `db:"id" json:"id"` + QuizID int64 `db:"quiz_id" json:"quiz_id"` + Title string `db:"title" json:"title"` + Description sql.NullString `db:"description" json:"description"` + Questiontype interface{} `db:"questiontype" json:"questiontype"` + Required sql.NullBool `db:"required" json:"required"` + Deleted sql.NullBool `db:"deleted" json:"deleted"` + Page sql.NullInt16 `db:"page" json:"page"` + Content sql.NullString `db:"content" json:"content"` + Version sql.NullInt16 `db:"version" json:"version"` + ParentIds []int32 `db:"parent_ids" json:"parent_ids"` + CreatedAt sql.NullTime `db:"created_at" json:"created_at"` + UpdatedAt sql.NullTime `db:"updated_at" json:"updated_at"` + Session string `db:"session" json:"session"` + Auditory int64 `db:"auditory" json:"auditory"` +} + +type Quiz struct { + ID int64 `db:"id" json:"id"` + Qid uuid.NullUUID `db:"qid" json:"qid"` + Accountid string `db:"accountid" json:"accountid"` + Deleted sql.NullBool `db:"deleted" json:"deleted"` + Archived sql.NullBool `db:"archived" json:"archived"` + Fingerprinting sql.NullBool `db:"fingerprinting" json:"fingerprinting"` + Repeatable sql.NullBool `db:"repeatable" json:"repeatable"` + NotePrevented sql.NullBool `db:"note_prevented" json:"note_prevented"` + MailNotifications sql.NullBool `db:"mail_notifications" json:"mail_notifications"` + UniqueAnswers sql.NullBool `db:"unique_answers" json:"unique_answers"` + Super sql.NullBool `db:"super" json:"super"` + GroupID sql.NullInt64 `db:"group_id" json:"group_id"` + Name sql.NullString `db:"name" json:"name"` + Description sql.NullString `db:"description" json:"description"` + Config sql.NullString `db:"config" json:"config"` + Status interface{} `db:"status" json:"status"` + LimitAnswers sql.NullInt32 `db:"limit_answers" json:"limit_answers"` + DueTo sql.NullInt32 `db:"due_to" json:"due_to"` + TimeOfPassing sql.NullInt32 `db:"time_of_passing" json:"time_of_passing"` + Pausable sql.NullBool `db:"pausable" json:"pausable"` + Version sql.NullInt16 `db:"version" json:"version"` + VersionComment sql.NullString `db:"version_comment" json:"version_comment"` + ParentIds []int32 `db:"parent_ids" json:"parent_ids"` + CreatedAt sql.NullTime `db:"created_at" json:"created_at"` + UpdatedAt sql.NullTime `db:"updated_at" json:"updated_at"` + QuestionsCount sql.NullInt32 `db:"questions_count" json:"questions_count"` + AnswersCount sql.NullInt32 `db:"answers_count" json:"answers_count"` + AverageTimePassing sql.NullInt32 `db:"average_time_passing" json:"average_time_passing"` + SessionsCount sql.NullInt32 `db:"sessions_count" json:"sessions_count"` + Gigachat bool `db:"gigachat" json:"gigachat"` +} + +type QuizPrivilegeUsage struct { + ID int64 `db:"id" json:"id"` + QuizID int64 `db:"quiz_id" json:"quiz_id"` + PrivilegeID string `db:"privilege_id" json:"privilege_id"` + UsedCount int32 `db:"used_count" json:"used_count"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` +} + +type QuizUtm struct { + ID int64 `db:"id" json:"id"` + Quizid int64 `db:"quizid" json:"quizid"` + Utm string `db:"utm" json:"utm"` + Deleted bool `db:"deleted" json:"deleted"` + CreatedAt time.Time `db:"created_at" json:"created_at"` +} + +type RespondentState struct { + ID int64 `db:"id" json:"id"` + TelegramID int32 `db:"telegram_id" json:"telegram_id"` + Quizid int32 `db:"quizid" json:"quizid"` + State int64 `db:"state" json:"state"` + Lang string `db:"lang" json:"lang"` + Contact string `db:"contact" json:"contact"` + Finish bool `db:"finish" json:"finish"` + Session sql.NullString `db:"session" json:"session"` +} + +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"` + Fieldsrule json.RawMessage `db:"fieldsrule" json:"fieldsrule"` + Deleted bool `db:"deleted" json:"deleted"` + Createdat sql.NullTime `db:"createdat" json:"createdat"` + Tagstoadd json.RawMessage `db:"tagstoadd" json:"tagstoadd"` +} + +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 Stepbitrix struct { + ID int64 `db:"id" json:"id"` + Accountid string `db:"accountid" json:"accountid"` + Bitrixid string `db:"bitrixid" json:"bitrixid"` + Entityid string `db:"entityid" json:"entityid"` + Statusid string `db:"statusid" json:"statusid"` + Name string `db:"name" json:"name"` + Nameinit string `db:"nameinit" json:"nameinit"` + Color string `db:"color" json:"color"` + Pipelineid int32 `db:"pipelineid" json:"pipelineid"` + Deleted bool `db:"deleted" json:"deleted"` + Createdat time.Time `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 TelegramIntegration struct { + ID int64 `db:"id" json:"id"` + Accountid string `db:"accountid" json:"accountid"` + Quizid int32 `db:"quizid" json:"quizid"` + BotToken string `db:"bot_token" json:"bot_token"` + BotName string `db:"bot_name" json:"bot_name"` + Repeatable sql.NullBool `db:"repeatable" json:"repeatable"` + Deleted bool `db:"deleted" json:"deleted"` + Status interface{} `db:"status" json:"status"` + InstanceID int32 `db:"instance_id" json:"instance_id"` +} + +type TelegramIntegrationInstance struct { + ID int64 `db:"id" json:"id"` + Checkout int64 `db:"checkout" json:"checkout"` + Limited int32 `db:"limited" json:"limited"` + Amount int32 `db:"amount" json:"amount"` +} + +type TelegramUserQuizResult struct { + ID int64 `db:"id" json:"id"` + BotID int64 `db:"bot_id" json:"bot_id"` + QuizID int64 `db:"quiz_id" json:"quiz_id"` + CurrentField sql.NullString `db:"current_field" json:"current_field"` + UserAnswer sql.NullString `db:"user_answer" json:"user_answer"` + State interface{} `db:"state" json:"state"` + Session string `db:"session" json:"session"` + QuestionID int64 `db:"question_id" json:"question_id"` + Iter int32 `db:"iter" json:"iter"` +} + +type Tgaccount struct { + ID int64 `db:"id" json:"id"` + Apiid int32 `db:"apiid" json:"apiid"` + Apihash string `db:"apihash" json:"apihash"` + Phonenumber string `db:"phonenumber" json:"phonenumber"` + Password string `db:"password" json:"password"` + Status interface{} `db:"status" json:"status"` + Deleted bool `db:"deleted" json:"deleted"` + Createdat time.Time `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 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"` +} diff --git a/dal/sqlcgen/queries.sql.go b/dal/sqlcgen/queries.sql.go new file mode 100644 index 0000000..cb87201 --- /dev/null +++ b/dal/sqlcgen/queries.sql.go @@ -0,0 +1,196 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.29.0 +// source: queries.sql + +package sqlcgen + +import ( + "context" + "database/sql" + "encoding/json" + + "github.com/lib/pq" +) + +const getQuestionListByIDs = `-- name: GetQuestionListByIDs :many +SELECT id, quiz_id, title, description, questiontype, required, deleted, page, content, version, parent_ids, created_at, updated_at, session, auditory FROM question WHERE id = ANY($1::int[]) AND deleted = FALSE +` + +func (q *Queries) GetQuestionListByIDs(ctx context.Context, dollar_1 []int32) ([]Question, error) { + rows, err := q.db.QueryContext(ctx, getQuestionListByIDs, pq.Array(dollar_1)) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Question + for rows.Next() { + var i Question + if err := rows.Scan( + &i.ID, + &i.QuizID, + &i.Title, + &i.Description, + &i.Questiontype, + &i.Required, + &i.Deleted, + &i.Page, + &i.Content, + &i.Version, + pq.Array(&i.ParentIds), + &i.CreatedAt, + &i.UpdatedAt, + &i.Session, + &i.Auditory, + ); 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 insertAnswers = `-- name: InsertAnswers :one +INSERT INTO answer( + content, + quiz_id, + question_id, + fingerprint, + session, + result, + email, + device_type, + device, + os, + browser, + ip, + start, + utm, + version +) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15) + RETURNING id, content, quiz_id, question_id, fingerprint, session, created_at, result, new, deleted, email, device_type, device, os, browser, ip, start, utm, version +` + +type InsertAnswersParams struct { + Content sql.NullString `db:"content" json:"content"` + QuizID int64 `db:"quiz_id" json:"quiz_id"` + QuestionID int64 `db:"question_id" json:"question_id"` + Fingerprint sql.NullString `db:"fingerprint" json:"fingerprint"` + Session sql.NullString `db:"session" json:"session"` + Result sql.NullBool `db:"result" json:"result"` + Email string `db:"email" json:"email"` + DeviceType string `db:"device_type" json:"device_type"` + Device string `db:"device" json:"device"` + Os string `db:"os" json:"os"` + Browser string `db:"browser" json:"browser"` + Ip string `db:"ip" json:"ip"` + Start bool `db:"start" json:"start"` + Utm json.RawMessage `db:"utm" json:"utm"` + Version int32 `db:"version" json:"version"` +} + +func (q *Queries) InsertAnswers(ctx context.Context, arg InsertAnswersParams) (Answer, error) { + row := q.db.QueryRowContext(ctx, insertAnswers, + arg.Content, + arg.QuizID, + arg.QuestionID, + arg.Fingerprint, + arg.Session, + arg.Result, + arg.Email, + arg.DeviceType, + arg.Device, + arg.Os, + arg.Browser, + arg.Ip, + arg.Start, + arg.Utm, + arg.Version, + ) + var i Answer + err := row.Scan( + &i.ID, + &i.Content, + &i.QuizID, + &i.QuestionID, + &i.Fingerprint, + &i.Session, + &i.CreatedAt, + &i.Result, + &i.New, + &i.Deleted, + &i.Email, + &i.DeviceType, + &i.Device, + &i.Os, + &i.Browser, + &i.Ip, + &i.Start, + &i.Utm, + &i.Version, + ) + return i, err +} + +const insertQuestion = `-- name: InsertQuestion :one +INSERT INTO question ( + quiz_id, + title, + description, + questiontype, + required, + page, + content, + parent_ids, + updated_at, + session, + auditory +) +VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11) + RETURNING id, created_at, updated_at +` + +type InsertQuestionParams struct { + QuizID int64 `db:"quiz_id" json:"quiz_id"` + Title string `db:"title" json:"title"` + Description sql.NullString `db:"description" json:"description"` + Questiontype interface{} `db:"questiontype" json:"questiontype"` + Required sql.NullBool `db:"required" json:"required"` + Page sql.NullInt16 `db:"page" json:"page"` + Content sql.NullString `db:"content" json:"content"` + ParentIds []int32 `db:"parent_ids" json:"parent_ids"` + UpdatedAt sql.NullTime `db:"updated_at" json:"updated_at"` + Session string `db:"session" json:"session"` + Auditory int64 `db:"auditory" json:"auditory"` +} + +type InsertQuestionRow struct { + ID int64 `db:"id" json:"id"` + CreatedAt sql.NullTime `db:"created_at" json:"created_at"` + UpdatedAt sql.NullTime `db:"updated_at" json:"updated_at"` +} + +func (q *Queries) InsertQuestion(ctx context.Context, arg InsertQuestionParams) (InsertQuestionRow, error) { + row := q.db.QueryRowContext(ctx, insertQuestion, + arg.QuizID, + arg.Title, + arg.Description, + arg.Questiontype, + arg.Required, + arg.Page, + arg.Content, + pq.Array(arg.ParentIds), + arg.UpdatedAt, + arg.Session, + arg.Auditory, + ) + var i InsertQuestionRow + err := row.Scan(&i.ID, &i.CreatedAt, &i.UpdatedAt) + return i, err +} diff --git a/go.mod b/go.mod index c92debb..f0f45a0 100644 --- a/go.mod +++ b/go.mod @@ -5,50 +5,27 @@ go 1.23.2 toolchain go1.23.4 require ( - gitea.pena/PenaSide/common v0.0.0-20250103085335-91ea31fee517 - gitea.pena/PenaSide/hlog v0.0.0-20241125221102-a54c29c002a9 - gitea.pena/PenaSide/trashlog v0.0.0-20250224122049-ddb4d72e9d07 - gitea.pena/SQuiz/common v0.0.0-20250610213406-ac5c9d667dc7 - github.com/go-redis/redis/v8 v8.11.5 + github.com/ClickHouse/clickhouse-go v1.5.4 github.com/gofiber/fiber/v2 v2.52.5 - github.com/minio/minio-go/v7 v7.0.81 + github.com/google/uuid v1.6.0 + github.com/lib/pq v1.10.9 github.com/rs/xid v1.6.0 github.com/skeris/appInit v1.0.2 go.uber.org/zap v1.27.0 ) require ( - gitea.pena/PenaSide/linters-golang v0.0.0-20241207122018-933207374735 // indirect - github.com/ClickHouse/clickhouse-go v1.5.4 // indirect github.com/andybalholm/brotli v1.1.0 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 // indirect - github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/dustin/go-humanize v1.0.1 // indirect - github.com/go-ini/ini v1.67.0 // indirect - github.com/goccy/go-json v0.10.3 // indirect - github.com/golang-jwt/jwt/v5 v5.2.1 // indirect - github.com/golang/protobuf v1.5.4 // indirect - github.com/google/uuid v1.6.0 // indirect github.com/klauspost/compress v1.17.11 // indirect - github.com/klauspost/cpuid/v2 v2.2.9 // indirect - github.com/lib/pq v1.10.9 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect - github.com/minio/md5-simd v1.1.2 // indirect github.com/rivo/uniseg v0.2.0 // indirect - github.com/tealeg/xlsx v1.0.5 // indirect + github.com/stretchr/testify v1.9.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.etcd.io/bbolt v1.3.10 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.28.0 // indirect - golang.org/x/net v0.30.0 // indirect golang.org/x/sys v0.28.0 // indirect - golang.org/x/text v0.19.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 // indirect - google.golang.org/grpc v1.64.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect ) diff --git a/go.sum b/go.sum index 108168a..936e917 100644 --- a/go.sum +++ b/go.sum @@ -1,17 +1,3 @@ -gitea.pena/PenaSide/common v0.0.0-20250103085335-91ea31fee517 h1:EgBe8VcdPwmxbSzYLndncP+NmR73uYuXxkTeDlEttEE= -gitea.pena/PenaSide/common v0.0.0-20250103085335-91ea31fee517/go.mod h1:91EuBCgcqgJ6mG36n2pds8sPwwfaJytLWOzY3h2YFKU= -gitea.pena/PenaSide/hlog v0.0.0-20241125221102-a54c29c002a9 h1:tBkXWNIt8icmkMMnq8MA421RWkUy4OZh5P7C3q8uCu4= -gitea.pena/PenaSide/hlog v0.0.0-20241125221102-a54c29c002a9/go.mod h1:sanhSL8aEsfcq21P+eItYiAnKAre+B67nGJmDfk2cf0= -gitea.pena/PenaSide/linters-golang v0.0.0-20241207122018-933207374735 h1:jDVeUhGBTXBibmW5dmtJg2m2+z5z2Rf6J4G0LpjVoJ0= -gitea.pena/PenaSide/linters-golang v0.0.0-20241207122018-933207374735/go.mod h1:gdd+vOT6up9STkEbxa2qESLIMZFjCmRbkcheFQCVgZU= -gitea.pena/PenaSide/trashlog v0.0.0-20250224122049-ddb4d72e9d07 h1:bUIUgzXQt16aBqSccI//BaODpRCTIaqlddSepM98QSc= -gitea.pena/PenaSide/trashlog v0.0.0-20250224122049-ddb4d72e9d07/go.mod h1:GRfWJerTUlgy82CiYAxE4tVYSVV54zEJJQy17Fx46E4= -gitea.pena/SQuiz/common v0.0.0-20250514124515-870e52266ca5 h1:C+iCsGMSUJonOTNNk8wWYOfzZ0Jjw+2IQ5FaEGwRVT0= -gitea.pena/SQuiz/common v0.0.0-20250514124515-870e52266ca5/go.mod h1:zCrUwDh0APpztKk6NUqTZv+zhjVbWpGBJiJ5z9dAH0U= -gitea.pena/SQuiz/common v0.0.0-20250610192732-9f01faa53ea9 h1:y97VEguCy1Qf/hl/XT9IfX3EgwLgAkn2+1sBDuxjo7Q= -gitea.pena/SQuiz/common v0.0.0-20250610192732-9f01faa53ea9/go.mod h1:zCrUwDh0APpztKk6NUqTZv+zhjVbWpGBJiJ5z9dAH0U= -gitea.pena/SQuiz/common v0.0.0-20250610213406-ac5c9d667dc7 h1:LdL8G958qTeDNNsLbqhnlnFbbiSMoqqULnNqxIR4c/w= -gitea.pena/SQuiz/common v0.0.0-20250610213406-ac5c9d667dc7/go.mod h1:zCrUwDh0APpztKk6NUqTZv+zhjVbWpGBJiJ5z9dAH0U= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/ClickHouse/clickhouse-go v1.5.4 h1:cKjXeYLNWVJIx2J1K6H2CqyRmfwVJVY1OV1coaaFcI0= github.com/ClickHouse/clickhouse-go v1.5.4/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI= @@ -19,38 +5,14 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1 github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/bkaradzic/go-lz4 v1.0.0 h1:RXc4wYsyz985CkXXeX04y4VnZFGG8Rd43pRaHsOXAKk= github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4= -github.com/caarlos0/env/v8 v8.0.0 h1:POhxHhSpuxrLMIdvTGARuZqR4Jjm8AYmoi/JKlcScs0= -github.com/caarlos0/env/v8 v8.0.0/go.mod h1:7K4wMY9bH0esiXSSHlfHLX5xKGQMnkH5Fk4TDSSSzfo= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 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/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -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/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= -github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= -github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= -github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= -github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo= github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= -github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= -github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 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= @@ -58,15 +20,9 @@ github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhB github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= -github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= -github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 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= @@ -78,23 +34,9 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/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.81 h1:SzhMN0TQ6T/xSBu6Nvw3M5M8voM+Ht8RH3hE8S7zxaA= -github.com/minio/minio-go/v7 v7.0.81/go.mod h1:84gmIilaX4zcvAWWzJ5Z1WI5axN+hAbM5w25xf8xvC0= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= -github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= 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/pioz/faker v1.7.3 h1:Tez8Emuq0UN+/d6mo3a9m/9ZZ/zdfJk0c5RtRatrceM= -github.com/pioz/faker v1.7.3/go.mod h1:xSpay5w/oz1a6+ww0M3vfpe40pSIykeUPeWEc3TvVlc= 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= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= @@ -109,18 +51,12 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/tealeg/xlsx v1.0.5 h1:+f8oFmvY8Gw1iUXzPk+kz+4GpbDZPK1FhPiQRd+ypgE= -github.com/tealeg/xlsx v1.0.5/go.mod h1:btRS8dz54TDnvKNosuAqxrM1QgN1udgk9O34bDCnORM= -github.com/themakers/bdd v0.0.0-20210316111417-6b1dfe326f33 h1:N9f/Q+2Ssa+yDcbfaoLTYvXmdeyUUxsJKdPUVsjSmiA= -github.com/themakers/bdd v0.0.0-20210316111417-6b1dfe326f33/go.mod h1:rpcH99JknBh8seZmlOlUg51gasZH6QH34oXNsIwYT6E= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= -go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0= -go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= @@ -133,18 +69,12 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= -golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -152,31 +82,15 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 h1:9Xyg6I9IWQZhRVfCWjKK+l6kI0jHcPesVlMnT//aHNo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= -google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= -google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/tucnak/telebot.v2 v2.5.0 h1:i+NynLo443Vp+Zn3Gv9JBjh3Z/PaiKAQwcnhNI7y6Po= -gopkg.in/tucnak/telebot.v2 v2.5.0/go.mod h1:BgaIIx50PSRS9pG59JH+geT82cfvoJU/IaI5TJdN3v8= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= diff --git a/healthchecks/healthchecks.go b/healthchecks/healthchecks.go new file mode 100644 index 0000000..ad0a945 --- /dev/null +++ b/healthchecks/healthchecks.go @@ -0,0 +1,18 @@ +package healthchecks + +import ( + "github.com/gofiber/fiber/v2" +) + +func Liveness(c *fiber.Ctx) error { + return c.SendStatus(fiber.StatusOK) +} + +func Readiness(err *error) fiber.Handler { + return func(c *fiber.Ctx) error { + if *err != nil { + return c.SendString((*err).Error()) + } + return c.SendStatus(fiber.StatusOK) + } +} diff --git a/middleware/middleware.go b/middleware/middleware.go new file mode 100644 index 0000000..6903804 --- /dev/null +++ b/middleware/middleware.go @@ -0,0 +1,28 @@ +package middleware + +import ( + "github.com/gofiber/fiber/v2" + "github.com/rs/xid" +) + +type ContextKey string + +const ( + SessionKey = "X-SessionKey" +) + +func AnswererChain() fiber.Handler { + return func(c *fiber.Ctx) error { + session := c.Get(SessionKey) + + if session == "" { + session := xid.New().String() + c.Set(SessionKey, session) + c.Locals(ContextKey(SessionKey), session) + } else { + c.Locals(ContextKey(SessionKey), session) + } + + return c.Next() + } +} diff --git a/model/model.go b/model/model.go new file mode 100644 index 0000000..b8d5b6e --- /dev/null +++ b/model/model.go @@ -0,0 +1,133 @@ +package model + +import "time" + +const TypeText = "text" + +type Quiz struct { + Id uint64 `json:"id"` + Qid string `json:"qid"` // uuid for secure data get and post + AccountId string `json:"accountid"` // account that created the quiz + + Deleted bool `json:"deleted"` // fake delete field + Archived bool `json:"archived"` // field for archiving quiz + + Fingerprinting bool `json:"fingerprinting"` // field that need for storing device id + Repeatable bool `json:"repeatable"` // make it true for allow more than one quiz checkouting + NotePrevented bool `json:"note_prevented"` // note answers even if the quiz was aborted + MailNotifications bool `json:"mail_notifications"` // set true if you want get an email with every quiz passing + UniqueAnswers bool `json:"unique_answers"` // set true if we you mention only last quiz passing + + Name string `json:"name"` + Description string `json:"description"` + Config string `json:"config"` // serialize json with config for page rules + Status string `json:"status"` // status of quiz as enum. see Status const higher + Limit uint64 `json:"limit"` // max count of quiz passing + DueTo uint64 `json:"due_to"` // time when quiz is end + + TimeOfPassing uint64 `json:"time_of_passing"` // amount of seconds for give all appropriate answers for quiz + Pausable bool `json:"pausable"` // true allows to pause the quiz taking + + Version int `json:"version"` + VersionComment string `json:"version_comment"` + ParentIds []int32 `json:"parent_ids"` + + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + + QuestionsCount uint64 `json:"questions_count"` + SessionCount uint64 `json:"session_count"` + PassedCount uint64 `json:"passed_count"` + AverageTime uint64 `json:"average_time"` + + Super bool `json:"super"` + GroupId uint64 `json:"group_id"` + + GigaChat bool `json:"giga_chat"` +} + +type Question struct { + Id uint64 `json:"id"` + QuizId uint64 `json:"quiz_id"` // relation to quiz table + + Title string `json:"title"` // title of question + Description string `json:"description"` // html\text representation of question and question description for answerer + Type string `json:"type"` // type field. enum with constants from consts higher + + Required bool `json:"required"` // answerer must answer this question + Deleted bool `json:"deleted"` // fake deleting field + + Page int `json:"page"` // set page number for question + //serialized json. caption for button type, array of key-value pairs for checkbox and select types, + // placeholder and input title for text and file types + Content string `json:"content"` + + Version int `json:"version"` + //todo check the best choice: question duplication and no statistics about the most unstable question or + //low performance of high complexity join-on-array query + ParentIds []int32 `json:"parent_ids"` + + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + + Session string `json:"session"` + Auditory int64 `json:"auditory"` +} + +type Answer struct { + Id uint64 + + Content string `json:"content"` //serialized json. empty for buttons + + QuestionId uint64 `json:"question_id"` // relation for quiz + QuizId uint64 // relation for quiz + + Fingerprint string // device Id + Session string // xid of session + + Result bool + CreatedAt time.Time + New bool `json:"new"` + Deleted bool + Email string + + DeviceType string + Device string + Browser string + IP string + OS string + Start bool + Utm UTMSavingMap + Version int32 +} + +type UTMSavingMap map[string]string + +type ResultContent struct { + Text string `json:"text"` + Name string `json:"name"` + Email string `json:"email"` + Phone string `json:"phone"` + Address string `json:"address"` + Telegram string `json:"telegram"` + Wechat string `json:"wechat"` + Viber string `json:"viber"` + Vk string `json:"vk"` + Skype string `json:"skype"` + Whatsup string `json:"whatsup"` + Messenger string `json:"messenger"` + Custom map[string]string `json:"customs"` + Start bool `json:"start"` + //IMGContent ImageContent `json:"imagecontent"` +} + +type QuizConfig struct { + Mailing ResultInfo `json:"resultInfo"` +} + +type ResultInfo struct { + When string `json:"when"` // before|after|email + Theme string `json:"theme"` // тема письма + Reply string `json:"reply"` // email для ответов, указывается в создании письма + ReplName string `json:"repl_name"` // имя отправителя +} diff --git a/service/service.go b/service/service.go index ac2c510..7585296 100644 --- a/service/service.go +++ b/service/service.go @@ -4,9 +4,9 @@ import ( "encoding/json" "fmt" "gitea.pena/SQuiz/answerer/clients" - quizdal "gitea.pena/SQuiz/common/dal" - "gitea.pena/SQuiz/common/middleware" - "gitea.pena/SQuiz/common/model" + quizdal "gitea.pena/SQuiz/answerer/dal" + "gitea.pena/SQuiz/answerer/middleware" + "gitea.pena/SQuiz/answerer/model" "github.com/gofiber/fiber/v2" "strconv" "strings" @@ -147,7 +147,7 @@ func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error { fp = cfp } - quiz, err := s.dal.QuizRepo.GetQuizByQid(c.Context(), quizID[0]) + quiz, err := s.dal.GetQuizByQid(c.Context(), quizID[0]) if err != nil { return c.Status(fiber.StatusInternalServerError).SendString("can not get quiz") } @@ -164,7 +164,7 @@ func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error { final = parsedFinal } - question, err := s.dal.QuestionRepo.GetQuestionListByIDs(c.Context(), []int32{int32(ans.QuestionId)}) + question, err := s.dal.GetQuestionListByIDs(c.Context(), []int32{int32(ans.QuestionId)}) if err != nil { return c.Status(fiber.StatusInternalServerError).SendString("can not get questions") } @@ -178,7 +178,7 @@ func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error { return c.Status(fiber.StatusInternalServerError).SendString(fmt.Sprintf("failed send answer to ai, err: %s", err.Error())) } - _, err = s.dal.QuestionRepo.CreateQuestion(c.Context(), &model.Question{ + _, err = s.dal.CreateQuestion(c.Context(), &model.Question{ QuizId: quiz.Id, Title: truncateUTF8(questionText, 512), Type: model.TypeText, @@ -234,7 +234,7 @@ func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error { return c.Status(fiber.StatusInternalServerError).SendString("can not unmarshal quiz config") } - stored, ers := s.dal.AnswerRepo.CreateAnswers(c.Context(), answers, cs, fp, quiz.Id) + stored, ers := s.dal.CreateAnswers(c.Context(), answers, cs, fp, quiz.Id) if len(ers) != 0 { for _, err := range ers { if strings.Contains(err.Error(), "duplicate key value") { diff --git a/sqlc.yaml b/sqlc.yaml new file mode 100644 index 0000000..103e173 --- /dev/null +++ b/sqlc.yaml @@ -0,0 +1,64 @@ +version: "1" +packages: + - name: "sqlcgen" + path: "./dal/sqlcgen" + queries: "./dal/db_query/queries.sql" + schema: + - "./dal/schema/000001_init.up.sql" + - "./dal/schema/000001_init.down.sql" + - "./dal/schema/000002_init.up.sql" + - "./dal/schema/000002_init.down.sql" + - "./dal/schema/000003_init.up.sql" + - "./dal/schema/000003_init.down.sql" + - "./dal/schema/000004_init.up.sql" + - "./dal/schema/000004_init.down.sql" + - "./dal/schema/000005_init.up.sql" + - "./dal/schema/000005_init.down.sql" + - "./dal/schema/000006_init.up.sql" + - "./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" + - "./dal/schema/000011_init.up.sql" + - "./dal/schema/000011_init.down.sql" + - "./dal/schema/000012_init.up.sql" + - "./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" + - "./dal/schema/000018_init.up.sql" + - "./dal/schema/000018_init.down.sql" + - "./dal/schema/000019_init.up.sql" + - "./dal/schema/000019_init.down.sql" + - "./dal/schema/000020_init.up.sql" + - "./dal/schema/000020_init.down.sql" + - "./dal/schema/000021_init.up.sql" + - "./dal/schema/000021_init.down.sql" + - "./dal/schema/000022_init.up.sql" + - "./dal/schema/000022_init.down.sql" + - "./dal/schema/000023_init.up.sql" + - "./dal/schema/000023_init.down.sql" + - "./dal/schema/000024_init.up.sql" + - "./dal/schema/000024_init.down.sql" + - "./dal/schema/000025_init.up.sql" + - "./dal/schema/000025_init.down.sql" + - "./dal/schema/000026_init.up.sql" + - "./dal/schema/000026_init.down.sql" + engine: "postgresql" + emit_json_tags: true + emit_db_tags: true + emit_prepared_queries: false + emit_interface: false + emit_exact_table_names: false + emit_empty_slices: false + sql_package: "database/sql"