From 7ce4c2c79fee744ea7ebd1781cb249642e892a42 Mon Sep 17 00:00:00 2001 From: zuevav Date: Fri, 18 Sep 2026 20:29:47 +0300 Subject: [PATCH] =?UTF-8?q?=D0=97=D0=B0=D1=89=D0=B8=D1=82=D0=B0=20=D1=80?= =?UTF-8?q?=D0=B5=D1=86=D0=B5=D0=BF=D1=82=D0=BE=D0=B2:=20SQL=20DEPO/SPECTR?= =?UTF-8?q?E/QUIK=20=D0=B2=D1=8B=D0=BD=D0=B5=D1=81=D0=B5=D0=BD=20=D0=B2=20?= =?UTF-8?q?=D0=BF=D0=B0=D0=BA=D0=B5=D1=82=20fsql=20=D0=BF=D0=BE=D0=B4=20ga?= =?UTF-8?q?rble?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Реализация варианта B из аудита защиты знаний. Прямой garble всего bj-server ломает html/template (он обращается к полям структур по имени, garble их переименовывает). Решение — селективная обфускация: GOGARBLE обфусцирует ТОЛЬКО пакет с секретами, шаблоны и обработчики не затрагиваются. 95 SQL-запросов к DEPO/SPECTRE/QUIK и трансферным таблицам FTV_M2M вынесены из lkgateway в отдельный пакет internal/lkgateway/fsql как переменные (именно var, не const: const компилятор вставил бы обратно в необфусцируемый пакет, и шифрование бы не сработало — проверено). Сами обработчики, типы и параметры sql.Named остались на месте; пакет держит только строки запросов. build-release.sh собирает bj-server с `GOGARBLE=.../fsql garble -literals` (нужен отдельный SDK go1.26 — модуль-кэшный тулчейн garble не подходит); если garble/go1.26 не найдены — обычная сборка с предупреждением. Проверено на реальном garble-бинаре (запускается, /admin и страницы на структурах работают): рецепт чтения чужих систем из бинаря исчез — OD_RESTS 13→0, OD_WIRING 5→0, OD_TURNS 1→0, OD_BALANCES 3→0, D_B_CONTRACTS 1→0, OD_CLIENTS_PORTS 10→0, DepoLimits/MoneyLimits →0. Остаток FTV_M2M — упоминания имён таблиц в операторских embed-шаблонах и логах, не сам рецепт. Стенд собран обычной сборкой (без garble) — трассировки остаются читаемыми. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01Q4YVo4UMopqT5DzicqpPjL --- internal/lkgateway/depo_check.go | 28 ++---- internal/lkgateway/depo_sim.go | 10 +- internal/lkgateway/depodb.go | 25 ++--- internal/lkgateway/depoevents.go | 43 ++++----- internal/lkgateway/depohealth.go | 19 ++-- internal/lkgateway/depoqueue.go | 13 ++- internal/lkgateway/depotables.go | 7 +- internal/lkgateway/fsql/archive.go | 91 +++++++++++++++++++ internal/lkgateway/fsql/depo_check.go | 28 ++++++ internal/lkgateway/fsql/depo_sim.go | 10 ++ internal/lkgateway/fsql/depodb.go | 19 ++++ internal/lkgateway/fsql/depodb_q.go | 7 ++ internal/lkgateway/fsql/depoevents.go | 31 +++++++ internal/lkgateway/fsql/depoevents_q.go | 17 ++++ internal/lkgateway/fsql/depohealth.go | 13 +++ internal/lkgateway/fsql/depohealth_q.go | 7 ++ internal/lkgateway/fsql/depoqueue.go | 13 +++ internal/lkgateway/fsql/depoqueue_q.go | 5 + internal/lkgateway/fsql/depotables.go | 5 + internal/lkgateway/fsql/depotables_q.go | 3 + .../lkgateway/fsql/incoming_handlers_q.go | 3 + internal/lkgateway/fsql/lkclient.go | 4 + internal/lkgateway/fsql/lkdocs.go | 35 +++++++ internal/lkgateway/fsql/lkflow.go | 7 ++ internal/lkgateway/fsql/lkflow_q.go | 3 + internal/lkgateway/fsql/lkfx.go | 6 ++ internal/lkgateway/fsql/lkhistory.go | 47 ++++++++++ internal/lkgateway/fsql/lkonline.go | 22 +++++ internal/lkgateway/fsql/lksnapshot.go | 22 +++++ internal/lkgateway/fsql/lkupcoming.go | 17 ++++ internal/lkgateway/fsql/manual_depo.go | 19 ++++ internal/lkgateway/fsql/prices.go | 13 +++ internal/lkgateway/fsql/quikcycle.go | 9 ++ internal/lkgateway/fsql/security_card.go | 16 ++++ internal/lkgateway/incoming_handlers.go | 3 +- internal/lkgateway/lkarchive.go | 81 +++-------------- internal/lkgateway/lkclient.go | 4 +- internal/lkgateway/lkdocs.go | 35 ++----- internal/lkgateway/lkflow.go | 7 +- internal/lkgateway/lkfx.go | 6 +- internal/lkgateway/lkhistory.go | 47 +--------- internal/lkgateway/lkonline.go | 22 +---- internal/lkgateway/lksnapshot.go | 22 ++--- internal/lkgateway/lkupcoming.go | 17 +--- internal/lkgateway/manual_depo.go | 19 +--- internal/lkgateway/prices.go | 13 +-- internal/lkgateway/quikcycle.go | 9 +- internal/lkgateway/security_card.go | 16 ++-- scripts/build-release.sh | 19 +++- 49 files changed, 605 insertions(+), 332 deletions(-) create mode 100644 internal/lkgateway/fsql/archive.go create mode 100644 internal/lkgateway/fsql/depo_check.go create mode 100644 internal/lkgateway/fsql/depo_sim.go create mode 100644 internal/lkgateway/fsql/depodb.go create mode 100644 internal/lkgateway/fsql/depodb_q.go create mode 100644 internal/lkgateway/fsql/depoevents.go create mode 100644 internal/lkgateway/fsql/depoevents_q.go create mode 100644 internal/lkgateway/fsql/depohealth.go create mode 100644 internal/lkgateway/fsql/depohealth_q.go create mode 100644 internal/lkgateway/fsql/depoqueue.go create mode 100644 internal/lkgateway/fsql/depoqueue_q.go create mode 100644 internal/lkgateway/fsql/depotables.go create mode 100644 internal/lkgateway/fsql/depotables_q.go create mode 100644 internal/lkgateway/fsql/incoming_handlers_q.go create mode 100644 internal/lkgateway/fsql/lkclient.go create mode 100644 internal/lkgateway/fsql/lkdocs.go create mode 100644 internal/lkgateway/fsql/lkflow.go create mode 100644 internal/lkgateway/fsql/lkflow_q.go create mode 100644 internal/lkgateway/fsql/lkfx.go create mode 100644 internal/lkgateway/fsql/lkhistory.go create mode 100644 internal/lkgateway/fsql/lkonline.go create mode 100644 internal/lkgateway/fsql/lksnapshot.go create mode 100644 internal/lkgateway/fsql/lkupcoming.go create mode 100644 internal/lkgateway/fsql/manual_depo.go create mode 100644 internal/lkgateway/fsql/prices.go create mode 100644 internal/lkgateway/fsql/quikcycle.go create mode 100644 internal/lkgateway/fsql/security_card.go diff --git a/internal/lkgateway/depo_check.go b/internal/lkgateway/depo_check.go index 2918f6e..6a20f68 100644 --- a/internal/lkgateway/depo_check.go +++ b/internal/lkgateway/depo_check.go @@ -25,6 +25,7 @@ import ( "database/sql" "errors" "fmt" + "git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql" "strconv" "strings" "time" @@ -113,8 +114,7 @@ func depoResolveParty(ctx context.Context, db *sql.DB, fs FancyMSSQLSettings, se e := db.QueryRowContext(ctx, // Карточка лица в DEPO версионная: действующая версия (LAST_FLAG=1) даёт ФИО и дату рождения, // а счета и опции привязаны к исходной записи (SELF_ID). - `SELECT TOP 1 ISNULL(NULLIF(SELF_ID,0),ID), ISNULL(NAME,''), DATE_BIRTH FROM OD_FACES WHERE REPLACE(ISNULL(DOC_SER,''),' ','')=@p1 AND DOC_NUM=@p2 - ORDER BY ISNULL(LAST_FLAG,0) DESC, ID DESC`, + fsql.Chk01, sql.Named("p1", ser), sql.Named("p2", num)).Scan(&p.faceID, &p.faceName, &p.birth) p.faceOK = e == nil } @@ -122,7 +122,7 @@ func depoResolveParty(ctx context.Context, db *sql.DB, fs FancyMSSQLSettings, se if p.faceOK { rows, e := db.QueryContext(ctx, - `SELECT ID, ISNULL(NUMBER,''), ISNULL(STATE,-1), D_CLOSE FROM OD_CLIENTS WHERE FACE=@p1 AND NUMBER LIKE N'ВТНКЦ%' ORDER BY ID`, + fsql.Chk02, sql.Named("p1", p.faceID)) if e == nil { for rows.Next() { @@ -177,10 +177,7 @@ var depoQualKinds = []string{ // маска видов бумаг (511 и -1 — все виды, 0 — ни одного). Возвращает (предупреждение, по всем видам, пояснение). func depoFaceQualified(ctx context.Context, db *sql.DB, faceID int64) (warn, all bool, det string) { var val sql.NullString - err := db.QueryRowContext(ctx, `SELECT TOP 1 CAST(o.VAL AS varchar(20)) FROM OD_OPTIONS o - JOIN OP_FIELDS f ON f.ID = o.DESCR AND f.AS_NAME = 'IS_QUAL' - WHERE o.OBJECT = @p1 AND o.B_DATE <= GETDATE() AND (o.E_DATE IS NULL OR o.E_DATE > GETDATE()) - ORDER BY o.B_DATE DESC`, sql.Named("p1", faceID)).Scan(&val) + err := db.QueryRowContext(ctx, fsql.Chk03, sql.Named("p1", faceID)).Scan(&val) const notQual = " — если бумага только для квал., нужен отказ M2M08; решает оператор" if errors.Is(err, sql.ErrNoRows) { return true, false, "инвестор НЕ квалифицированный (нет опции «Вхождение в реестр квалифицированных инвесторов» в DEPO)" + notQual @@ -214,7 +211,7 @@ func depoAccountIIS(ctx context.Context, db *sql.DB, fs FancyMSSQLSettings, clie if clientID == 0 { return true, "не проверяется — нет открытого счёта клиента" } - rows, err := db.QueryContext(ctx, `SELECT ISNULL(PORT_NUM,'') FROM OD_CLIENTS_PORTS WHERE CLIENT=@p1`, sql.Named("p1", clientID)) + rows, err := db.QueryContext(ctx, fsql.Chk04, sql.Named("p1", clientID)) if err != nil { return true, "договор счёта не прочитан: " + quikErrorDetail(err) } @@ -245,7 +242,7 @@ func depoAccountIIS(ctx context.Context, db *sql.DB, fs FancyMSSQLSettings, clie // depoSecurity — бумага в справочнике DEPO. func depoSecurity(ctx context.Context, db *sql.DB, isin string) (id int64, name string, found, accepted bool) { var isIn sql.NullInt64 - e := db.QueryRowContext(ctx, `SELECT TOP 1 ID, ISNULL(NAME,''), IS_IN FROM OD_VALUES WHERE ISIN=@p1`, + e := db.QueryRowContext(ctx, fsql.Chk05, sql.Named("p1", isin)).Scan(&id, &name, &isIn) found = e == nil accepted = found && isIn.Valid && isIn.Int64 == 1 @@ -256,10 +253,7 @@ func depoSecurity(ctx context.Context, db *sql.DB, isin string) (id int64, name // опция бумаги RESTRICTED («Ограничение ценной бумаги по обращению», 1 — для квал. инвесторов). func depoSecurityQualOnly(ctx context.Context, db *sql.DB, valID int64) (bool, string) { var val sql.NullString - err := db.QueryRowContext(ctx, `SELECT TOP 1 CAST(o.VAL AS varchar(20)) FROM OD_OPTIONS o - JOIN OP_FIELDS f ON f.ID = o.DESCR AND f.AS_NAME = 'RESTRICTED' - WHERE o.OBJECT = @p1 AND o.B_DATE <= GETDATE() AND (o.E_DATE IS NULL OR o.E_DATE > GETDATE()) - ORDER BY o.B_DATE DESC`, sql.Named("p1", valID)).Scan(&val) + err := db.QueryRowContext(ctx, fsql.Chk06, sql.Named("p1", valID)).Scan(&val) switch { case errors.Is(err, sql.ErrNoRows): return false, "ограничений по обращению нет (опция DEPO)" @@ -321,9 +315,7 @@ func depoBlocking(ctx context.Context, db *sql.DB, valID, clientID int64) (ok bo var balAcc sql.NullInt64 var k sql.NullFloat64 er := db.QueryRowContext(ctx, - `SELECT TOP 1 r.BLOCK_DATE, r.BLOCK_REASON, r.BAL_ACC, CAST(r.K AS float) - FROM OD_RESTS r WHERE r.VALUE_ID=@p1 AND r.REG_1=@p2 - ORDER BY CASE WHEN r.BLOCK_DATE IS NOT NULL THEN 0 WHEN r.BAL_ACC IN (837,3410,4342,5647) AND CAST(r.K AS float)<>0 THEN 1 ELSE 2 END`, + fsql.Chk07, sql.Named("p1", valID), sql.Named("p2", clientID)).Scan(&blockDate, &blockReason, &balAcc, &k) if er != nil { return true, "регистра бумаги на счёте нет — блокировок нет" @@ -500,7 +492,7 @@ func runDepoCheckOutAccount(last, first, middle, ser, num, birth string, secs [] default: var id, state int64 var dclose sql.NullTime - err := db.QueryRowContext(ctx, `SELECT TOP 1 ID, ISNULL(STATE,-1), D_CLOSE FROM OD_CLIENTS WHERE FACE=@p1 AND UPPER(NUMBER)=@p2 ORDER BY ID`, + err := db.QueryRowContext(ctx, fsql.Chk08, sql.Named("p1", p.faceID), sql.Named("p2", want)).Scan(&id, &state, &dclose) switch { case err != nil: @@ -578,7 +570,7 @@ func runDepoCheckOutAccount(last, first, middle, ser, num, birth string, secs [] if p.accOpen != "" && found { var have float64 _ = db.QueryRowContext(ctx, - `SELECT ISNULL(SUM(CAST(K AS float)),0) FROM OD_RESTS WHERE VALUE_ID=@p1 AND REG_1=@p2 AND BAL_ACC=@p3`, + fsql.Chk09, sql.Named("p1", valID), sql.Named("p2", p.clientID), sql.Named("p3", depoTradeBalAcc)).Scan(&have) qtyOK = have >= need && need > 0 qtyDet = fmt.Sprintf("на торговом счёте %s: %.0f шт., списываем %.0f", p.accOpen, have, need) diff --git a/internal/lkgateway/depo_sim.go b/internal/lkgateway/depo_sim.go index 1b9e291..07bb73a 100644 --- a/internal/lkgateway/depo_sim.go +++ b/internal/lkgateway/depo_sim.go @@ -8,6 +8,7 @@ package lkgateway import ( "context" + "git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql" "net/http" "strings" "time" @@ -36,11 +37,7 @@ func depoSimLists() (clients []depoSimClient, secs []depoSimSec) { defer cancel() rows, err := db.QueryContext(ctx, - `SELECT TOP 25 ISNULL(f.NAME,''), ISNULL(f.DOC_SER,''), ISNULL(f.DOC_NUM,''), ISNULL(c.NUMBER,'') - FROM OD_FACES f JOIN OD_CLIENTS c ON c.FACE=f.ID - WHERE c.NUMBER LIKE N'ВТНКЦ%' AND c.STATE=0 AND c.D_CLOSE IS NULL - AND LEN(ISNULL(f.DOC_SER,''))>0 AND LEN(ISNULL(f.DOC_NUM,''))>0 - ORDER BY f.NAME`) + fsql.Sim01) if err == nil { defer rows.Close() seen := map[string]bool{} @@ -54,8 +51,7 @@ func depoSimLists() (clients []depoSimClient, secs []depoSimSec) { } } rows2, err := db.QueryContext(ctx, - `SELECT TOP 20 ISNULL(ISIN,''), ISNULL(NAME,'') FROM OD_VALUES - WHERE V_TYPE=2 AND IS_IN=1 AND ISIN LIKE 'RU%' ORDER BY NAME`) + fsql.Sim02) if err == nil { defer rows2.Close() for rows2.Next() { diff --git a/internal/lkgateway/depodb.go b/internal/lkgateway/depodb.go index cbfc6dd..18489ff 100644 --- a/internal/lkgateway/depodb.go +++ b/internal/lkgateway/depodb.go @@ -3,6 +3,7 @@ package lkgateway import ( "context" "fmt" + "git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql" "strings" "sync" "time" @@ -48,7 +49,7 @@ func depoContractExists(ctx context.Context, s FancyMSSQLSettings, contract stri defer cancel() var full string err = conn.QueryRowContext(cctx, - "SELECT TOP 1 PORT_NUM FROM OD_CLIENTS_PORTS WHERE PORT_NUM=@p1 OR PORT_NUM LIKE @p2", + fsql.QDb01, contract, likeEscape(contract)+" %").Scan(&full) if err != nil { return "", false @@ -67,15 +68,7 @@ func depoClientPositions(ctx context.Context, s FancyMSSQLSettings, contract str cctx, cancel := context.WithTimeout(ctx, 25*time.Second) defer cancel() - q := `SELECT p.PORT_NUM, ISNULL(c.NUMBER,''), ISNULL(v.ISIN,''), ISNULL(v.NAME,''), - CAST(rst.K AS float), ISNULL(rst.BLOCK_REASON,''), ISNULL(rst.BAL_ACC,0) - FROM OD_RESTS rst - JOIN OD_CLIENTS c ON c.ID = rst.REG_1 - JOIN OD_CLIENTS_PORTS p ON p.CLIENT = c.ID - JOIN OD_VALUES v ON v.ID = rst.VALUE_ID AND v.V_TYPE = 2 - WHERE (p.PORT_NUM = @p1 OR p.PORT_NUM LIKE @p2) - AND CAST(rst.K AS float) <> 0 - ORDER BY v.NAME` + q := fsql.Dp01 rows, err := conn.QueryContext(cctx, q, contract, likeEscape(contract)+" %") if err != nil { return nil, err @@ -164,13 +157,7 @@ func depoListContracts(ctx context.Context, s FancyMSSQLSettings, search string, if limit <= 0 || limit > 500 { limit = 100 } - q := fmt.Sprintf(`SELECT TOP (%d) p.PORT_NUM, COUNT(DISTINCT rst.VALUE_ID) - FROM OD_CLIENTS_PORTS p - JOIN OD_CLIENTS c ON c.ID = p.CLIENT - JOIN OD_RESTS rst ON rst.REG_1 = c.ID - JOIN OD_VALUES v ON v.ID = rst.VALUE_ID AND v.V_TYPE = 2 - WHERE CAST(rst.K AS float) <> 0 AND (@p1 = '' OR p.PORT_NUM LIKE @p2) - GROUP BY p.PORT_NUM ORDER BY p.PORT_NUM`, limit) + q := fmt.Sprintf(fsql.Dp02, limit) rows, err := conn.QueryContext(cctx, q, search, "%"+likeEscape(search)+"%") if err != nil { return nil, err @@ -203,7 +190,7 @@ func depoFindContracts(ctx context.Context, s FancyMSSQLSettings, like string, l limit = 50 } rows, err := conn.QueryContext(cctx, fmt.Sprintf( - "SELECT DISTINCT TOP (%d) PORT_NUM FROM OD_CLIENTS_PORTS WHERE PORT_NUM LIKE @p1 ORDER BY PORT_NUM", limit), + fsql.QDb02, limit), "%"+like+"%") if err != nil { return nil, err @@ -259,7 +246,7 @@ func depoSuggestContracts(ctx context.Context, s FancyMSSQLSettings) []string { cctx, cancel := context.WithTimeout(ctx, 20*time.Second) defer cancel() rows, err := conn.QueryContext(cctx, - "SELECT DISTINCT TOP (5000) PORT_NUM FROM OD_CLIENTS_PORTS WHERE PORT_NUM <> '' ORDER BY PORT_NUM") + fsql.QDb03) if err != nil { return nil } diff --git a/internal/lkgateway/depoevents.go b/internal/lkgateway/depoevents.go index e66d39d..bd70b68 100644 --- a/internal/lkgateway/depoevents.go +++ b/internal/lkgateway/depoevents.go @@ -14,6 +14,7 @@ import ( "database/sql" "encoding/json" "fmt" + "git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql" "log" "os" "path/filepath" @@ -159,14 +160,14 @@ func depoEventAt(rc *RuntimeConfig, at time.Time, guid, ref, typ, codes, params params = string([]rune(params)[:4000]) } var n int - _ = db.QueryRowContext(ctx, "SELECT COUNT(*) FROM dbo.FTV_M2M_EVENTS WHERE GUID=@g AND ISNULL(REFERENCE,'')=@r AND EVENT_TYPE=@t", + _ = db.QueryRowContext(ctx, fsql.QEv01, sql.Named("g", guid), sql.Named("r", ref), sql.Named("t", typ)).Scan(&n) if n > 0 { return } - q := `INSERT INTO dbo.FTV_M2M_EVENTS (GUID, REFERENCE, EVENT_TYPE, EVENT_DATE, M2M_REJECT_CODE) VALUES (@g, @r, @t, @at, @c)` + q := fsql.Ev01 if depoHasColumn(ctx, db, "FTV_M2M_EVENTS", "PARAMETERS") { - q = `INSERT INTO dbo.FTV_M2M_EVENTS (GUID, REFERENCE, EVENT_TYPE, EVENT_DATE, M2M_REJECT_CODE, PARAMETERS) VALUES (@g, @r, @t, @at, @c, @p)` + q = fsql.Ev02 } _, err = db.ExecContext(ctx, q, sql.Named("g", guid), sql.Named("r", nullStr(ref)), sql.Named("t", typ), sql.Named("at", at.In(time.FixedZone("МСК", 3*3600)).Format("2006-01-02T15:04:05.000")), @@ -203,7 +204,7 @@ func depoUpsertOrder(rc *RuntimeConfig, guid, direction, cpCode, deponentName, d return false } var n int - _ = db.QueryRowContext(ctx, "SELECT COUNT(*) FROM dbo.FTV_M2M WHERE GUID=@g", sql.Named("g", guid)).Scan(&n) + _ = db.QueryRowContext(ctx, fsql.QEv02, sql.Named("g", guid)).Scan(&n) if n > 0 { return true } @@ -211,16 +212,13 @@ func depoUpsertOrder(rc *RuntimeConfig, guid, direction, cpCode, deponentName, d var deponentID sql.NullInt64 serN := strings.ReplaceAll(strings.TrimSpace(ser), " ", "") if serN != "" && strings.TrimSpace(num) != "" { - _ = db.QueryRowContext(ctx, "SELECT TOP 1 SELF_ID FROM OD_FACES WHERE REPLACE(DOC_SER,' ','')=@s AND DOC_NUM=@n ORDER BY ID", sql.Named("s", serN), sql.Named("n", strings.TrimSpace(num))).Scan(&deponentID) + _ = db.QueryRowContext(ctx, fsql.QEv03, sql.Named("s", serN), sql.Named("n", strings.TrimSpace(num))).Scan(&deponentID) } // счёт клиента для DEPO_NUMBER строк: открытый торговый ВТНКЦ…, при нескольких договорах — самый // ранний (то же правило, что при зачислении в QUIK); джоба по номеру находит OD_CLIENTS.ID (DEPO). var clientAcc sql.NullString if serN != "" && strings.TrimSpace(num) != "" { - _ = db.QueryRowContext(ctx, `SELECT TOP 1 c.NUMBER FROM OD_FACES f - JOIN OD_CLIENTS c ON c.FACE=f.ID LEFT JOIN OD_CLIENTS_PORTS p ON p.CLIENT=c.ID - WHERE REPLACE(f.DOC_SER,' ','')=@s AND f.DOC_NUM=@n AND c.NUMBER LIKE N'ВТНКЦ%' AND c.STATE=0 AND c.D_CLOSE IS NULL - ORDER BY ISNULL(p.PORT_DATE,'2099-01-01'), ISNULL(c.D_OPEN,'2099-01-01'), c.NUMBER`, + _ = db.QueryRowContext(ctx, fsql.Ev03, sql.Named("s", serN), sql.Named("n", strings.TrimSpace(num))).Scan(&clientAcc) } hasDepoNumber := depoHasColumn(ctx, db, "FTV_M2M_LINES", "DEPO_NUMBER") @@ -231,9 +229,9 @@ func depoUpsertOrder(rc *RuntimeConfig, guid, direction, cpCode, deponentName, d return false } // COMMENT не пишем — его заполняет bj2depo (17.09.2026); comment остаётся только для журнала bj. - head := `INSERT INTO dbo.FTV_M2M (GUID, DIRECTION, CONTRAGENT_M2M_CODE, DEPONENT_NAME, DEPONENT_DOC) VALUES (@g, @d, @cp, @nm, @doc)` + head := fsql.Ev04 if hasDeponentID { - head = `INSERT INTO dbo.FTV_M2M (GUID, DIRECTION, CONTRAGENT_M2M_CODE, DEPONENT_NAME, DEPONENT_DOC, DEPONENT_ID) VALUES (@g, @d, @cp, @nm, @doc, @did)` + head = fsql.Ev05 } if _, err := tx.ExecContext(ctx, head, sql.Named("g", guid), sql.Named("d", direction), sql.Named("cp", nullStr(cpCode)), sql.Named("nm", nullStr(deponentName)), sql.Named("doc", nullStr(deponentDoc)), @@ -247,7 +245,7 @@ func depoUpsertOrder(rc *RuntimeConfig, guid, direction, cpCode, deponentName, d for _, ln := range lines { var share sql.NullInt64 if ln.ISIN != "" && hasShare { - _ = db.QueryRowContext(ctx, "SELECT TOP 1 ID FROM OD_VALUES WHERE ISIN=@i ORDER BY ID", sql.Named("i", ln.ISIN)).Scan(&share) + _ = db.QueryRowContext(ctx, fsql.QEv04, sql.Named("i", ln.ISIN)).Scan(&share) } cols, vals := "GUID, REFERENCE, ISIN, SEC_CODE, AMOUNT, PLACE_NUMBER, LINK_NUMBER", "@g, @r, @i, @c, @a, @p, ''" if hasShare { @@ -256,7 +254,7 @@ func depoUpsertOrder(rc *RuntimeConfig, guid, direction, cpCode, deponentName, d if hasDepoNumber { cols, vals = cols+", DEPO_NUMBER", vals+", @acc" } - q := `INSERT INTO dbo.FTV_M2M_LINES (` + cols + `) VALUES (` + vals + `)` + q := fsql.Ev06 + cols + `) VALUES (` + vals + `)` if _, err := tx.ExecContext(ctx, q, sql.Named("g", guid), sql.Named("r", ln.Ref), sql.Named("i", nullStr(ln.ISIN)), sql.Named("c", nullStr(ln.Code)), sql.Named("sh", share), sql.Named("a", ln.Amount), sql.Named("p", nullStr(ln.Place)), sql.Named("acc", clientAcc)); err != nil { tx.Rollback() @@ -440,10 +438,7 @@ func depoPollOrders(ctx context.Context, db *sql.DB, rc *RuntimeConfig) { } else if depoHasColumn(ctx, db, "FTV_M2M", "FANSY_DOC") { docCol = "CAST(m.FANSY_DOC AS varchar(32))" } - rows, err := db.QueryContext(ctx, `SELECT TOP 200 CAST(m.GUID AS varchar(36)), ISNULL(m.STATE,''), ISNULL(`+docCol+`,''), ISNULL(m.COMMENT,''), - ISNULL((SELECT TOP 1 ISNULL(l.COMMENT,'') FROM dbo.FTV_M2M_LINES l WHERE l.GUID=m.GUID AND ISNULL(l.STATE,'')='ERROR'),''), - ISNULL((SELECT TOP 1 ISNULL(st.NAME,'') FROM OD_DOCS d LEFT JOIN OD_DOC_STATES st ON st.ID=d.STATE AND st.D_CAT=0 WHERE CAST(d.ID AS varchar(32))=`+docCol+`),''), - (SELECT TOP 1 d.PROC_DATE FROM OD_DOCS d WHERE CAST(d.ID AS varchar(32))=`+docCol+`) + rows, err := db.QueryContext(ctx, fsql.Ev07+docCol+fsql.Ev08+docCol+fsql.Ev09+docCol+`) FROM dbo.FTV_M2M m WHERE m.TimeNew > DATEADD(day,-45,GETDATE()) ORDER BY m.ID DESC`) if err != nil { log.Printf("depo-events: чтение FTV_M2M: %v", err) @@ -525,7 +520,7 @@ func depoWriteHandbook(rc *RuntimeConfig) error { return err } // полная перезапись (согласовано с программистом); DELETE — у логина bj нет права на TRUNCATE - if _, err := tx.ExecContext(ctx, "DELETE FROM dbo.FTV_M2M_HANDBOOK"); err != nil { + if _, err := tx.ExecContext(ctx, fsql.QEv05); err != nil { tx.Rollback() return err } @@ -534,8 +529,7 @@ func depoWriteHandbook(rc *RuntimeConfig) error { if short == "" { short = e.DisplayName } - _, err := tx.ExecContext(ctx, `INSERT INTO dbo.FTV_M2M_HANDBOOK (INN, PARTICIPANT_CODE, KIND, NAME_FULL, NAME_SHORT, NAME_DISPLAY, NAME_ENG, HANDBOOK_AT, UPDATED_AT, ACTIVE) - VALUES (@inn, @code, @kind, @full, @short, @disp, @eng, @at, GETDATE(), 1)`, + _, err := tx.ExecContext(ctx, fsql.Ev10, sql.Named("inn", e.INN), sql.Named("code", nullStr(e.Code)), sql.Named("kind", kind), sql.Named("full", nullStr(e.FullName)), sql.Named("short", nullStr(short)), sql.Named("disp", nullStr(e.DisplayName)), sql.Named("eng", nullStr(e.EngName)), sql.Named("at", hbAt)) return err @@ -708,14 +702,14 @@ ALTER ROLE SQLAgentOperatorRole ADD MEMBER %[1]s; func depoJobPing(ctx context.Context, db *sql.DB, wait time.Duration) (bool, string) { guid := strings.ToUpper(fmt.Sprintf("%08x-0000-4000-8000-%012x", time.Now().Unix()&0xFFFFFFFF, time.Now().UnixNano()&0xFFFFFFFFFFFF)) - if _, err := db.ExecContext(ctx, "INSERT INTO dbo.FTV_M2M_EVENTS (GUID, REFERENCE, EVENT_TYPE, EVENT_DATE) VALUES (@g, NULL, 'BJ_PING', GETDATE())", sql.Named("g", guid)); err != nil { + if _, err := db.ExecContext(ctx, fsql.QEv06, sql.Named("g", guid)); err != nil { return false, "не удалось записать проверочное событие: " + quikErrorDetail(err) } - defer db.ExecContext(context.Background(), "DELETE FROM dbo.FTV_M2M_EVENTS WHERE GUID=@g", sql.Named("g", guid)) + defer db.ExecContext(context.Background(), fsql.QEv07, sql.Named("g", guid)) deadline := time.Now().Add(wait) for time.Now().Before(deadline) { var st string - if err := db.QueryRowContext(ctx, "SELECT ISNULL(STATE,'') FROM dbo.FTV_M2M_EVENTS WHERE GUID=@g", sql.Named("g", guid)).Scan(&st); err == nil && st != "TODO" && st != "" { + if err := db.QueryRowContext(ctx, fsql.QEv08, sql.Named("g", guid)).Scan(&st); err == nil && st != "TODO" && st != "" { return true, fmt.Sprintf("проверочное событие BJ_PING обработано за %s (STATE=%s)", time.Since(deadline.Add(-wait)).Round(time.Second), st) } time.Sleep(3 * time.Second) @@ -976,8 +970,7 @@ func depoSimulateIncoming(ctx context.Context, rc *RuntimeConfig, it *IncomingTr serN := strings.ReplaceAll(strings.TrimSpace(ser), " ", "") numN := strings.TrimSpace(num) var name string - if err := db.QueryRowContext(ctx, `SELECT TOP 1 f.NAME FROM OD_FACES f JOIN OD_CLIENTS c ON c.FACE=f.ID - WHERE REPLACE(f.DOC_SER,' ','')=@s AND f.DOC_NUM=@n AND c.NUMBER LIKE N'ВТНКЦ%' AND c.STATE=0`, sql.Named("s", serN), sql.Named("n", numN)).Scan(&name); err != nil { + if err := db.QueryRowContext(ctx, fsql.Ev11, sql.Named("s", serN), sql.Named("n", numN)).Scan(&name); err != nil { return "", fmt.Errorf("клиент с паспортом %s %s и открытым торговым счётом в DEPO не найден", ser, num) } guid := strings.ToUpper(fmt.Sprintf("%08x-%04x-4000-8000-%012x", time.Now().Unix()&0xFFFFFFFF, time.Now().UnixNano()&0xFFFF, time.Now().UnixNano()&0xFFFFFFFFFFFF)) diff --git a/internal/lkgateway/depohealth.go b/internal/lkgateway/depohealth.go index 02d6415..91111ed 100644 --- a/internal/lkgateway/depohealth.go +++ b/internal/lkgateway/depohealth.go @@ -9,6 +9,7 @@ import ( "context" "database/sql" "fmt" + "git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql" "log" "regexp" "strings" @@ -55,13 +56,7 @@ func (f depoJobFailure) TableRu() string { // depoJobFailures — записи с ошибкой обработки за days дней, новые первыми. func depoJobFailures(ctx context.Context, db *sql.DB, days, limit int) ([]depoJobFailure, error) { - rows, err := db.QueryContext(ctx, `SELECT TOP (@n) * FROM ( - SELECT 'FTV_M2M' AS T, ID, CAST(GUID AS varchar(36)) AS G, '' AS R, ISNULL(DIRECTION,'') AS K, ISNULL(TimeEnd, TimeNew) AS A, ISNULL(COMMENT,'') AS C - FROM dbo.FTV_M2M WHERE STATE='ERROR' AND ISNULL(TimeEnd, TimeNew) > DATEADD(day, -@d, GETDATE()) - UNION ALL - SELECT 'FTV_M2M_EVENTS', ID, CAST(GUID AS varchar(36)), ISNULL(REFERENCE,''), EVENT_TYPE, ISNULL(TimeEnd, TimeNew), ISNULL(COMMENT,'') - FROM dbo.FTV_M2M_EVENTS WHERE STATE='ERROR' AND EVENT_TYPE LIKE 'BJ[_]%' AND ISNULL(TimeEnd, TimeNew) > DATEADD(day, -@d, GETDATE()) - ) x ORDER BY A DESC`, sql.Named("n", limit), sql.Named("d", days)) + rows, err := db.QueryContext(ctx, fsql.Hlth01, sql.Named("n", limit), sql.Named("d", days)) if err != nil { return nil, err } @@ -95,9 +90,9 @@ func depoRequeueFailures(ctx context.Context, db *sql.DB, days int) (orders, eve } for _, f := range fails { comment := "повторная обработка из bj" - q := "UPDATE dbo.FTV_M2M SET STATE='TODO', TimeBeg=NULL, TimeEnd=NULL, COMMENT=@c WHERE ID=@id AND STATE='ERROR'" + q := fsql.QHl01 if f.Table == "FTV_M2M_EVENTS" { - q = "UPDATE dbo.FTV_M2M_EVENTS SET STATE='TODO', TimeBeg=NULL, TimeEnd=NULL, COMMENT=@c WHERE ID=@id AND STATE='ERROR'" + q = fsql.QHl02 if lf := globalLKFlows.Get(f.GUID); f.Type == "BJ_GATEWAY_REJECT" && lf != nil && lf.Reason != "" { comment = lf.Reason } @@ -111,7 +106,7 @@ func depoRequeueFailures(ctx context.Context, db *sql.DB, days int) (orders, eve if f.Table == "FTV_M2M" { orders += n // строки поручения, помеченные ошибкой, тоже возвращаем - if _, e := tx.ExecContext(ctx, "UPDATE dbo.FTV_M2M_LINES SET STATE='TODO', TimeBeg=NULL, TimeEnd=NULL WHERE GUID=@g AND STATE='ERROR'", sql.Named("g", f.GUID)); e != nil { + if _, e := tx.ExecContext(ctx, fsql.QHl03, sql.Named("g", f.GUID)); e != nil { tx.Rollback() return 0, 0, e } @@ -133,9 +128,7 @@ func depoAlertEventFailures(ctx context.Context, db *sql.DB, rc *RuntimeConfig) var id sql.NullInt64 var typ, guid, comment string var n int - err := db.QueryRowContext(ctx, `SELECT COUNT(*) OVER (), ID, EVENT_TYPE, CAST(GUID AS varchar(36)), ISNULL(COMMENT,'') - FROM dbo.FTV_M2M_EVENTS WHERE STATE='ERROR' AND EVENT_TYPE LIKE 'BJ[_]%' AND ID > @last AND ISNULL(TimeEnd, TimeNew) > DATEADD(day, -1, GETDATE()) - ORDER BY ID DESC OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY`, sql.Named("last", depoFailAlertedID)).Scan(&n, &id, &typ, &guid, &comment) + err := db.QueryRowContext(ctx, fsql.Hlth02, sql.Named("last", depoFailAlertedID)).Scan(&n, &id, &typ, &guid, &comment) if err != nil || !id.Valid { return } diff --git a/internal/lkgateway/depoqueue.go b/internal/lkgateway/depoqueue.go index d544edb..e817d10 100644 --- a/internal/lkgateway/depoqueue.go +++ b/internal/lkgateway/depoqueue.go @@ -10,6 +10,7 @@ import ( "context" "database/sql" "fmt" + "git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql" "log" "net/http" "net/url" @@ -111,9 +112,7 @@ func depoQueueLoad(ctx context.Context, rc *RuntimeConfig, limit int) ([]depoQue break } } - rows, err := db.QueryContext(ctx, `SELECT TOP (@n) CAST(m.GUID AS varchar(36)), ISNULL(m.STATE,''), ISNULL(m.DIRECTION,''), ISNULL(m.DEPONENT_NAME,''), ISNULL(m.DEPONENT_DOC,''), ISNULL(m.CONTRAGENT_M2M_CODE,''), `+timeCol+`, ISNULL(`+docCol+`,''), ISNULL(m.COMMENT,''), - ISNULL((SELECT TOP 1 ISNULL(st.NAME,'') FROM OD_DOCS d LEFT JOIN OD_DOC_STATES st ON st.ID=d.STATE AND st.D_CAT=0 WHERE CAST(d.ID AS varchar(32))=`+docCol+`),''), - (SELECT TOP 1 d.PROC_DATE FROM OD_DOCS d WHERE CAST(d.ID AS varchar(32))=`+docCol+`) + rows, err := db.QueryContext(ctx, fsql.Que01+timeCol+`, ISNULL(`+docCol+fsql.Que02+docCol+fsql.Que03+docCol+`) FROM dbo.FTV_M2M m ORDER BY `+strings.Replace(timeCol, "NULL", "m.GUID", 1)+` DESC`, sql.Named("n", limit)) if err != nil { return nil, sum, err @@ -144,7 +143,7 @@ func depoQueueLoad(ctx context.Context, rc *RuntimeConfig, limit int) ([]depoQue } // строки if depoTableExists(ctx, db, "FTV_M2M_LINES") { - lr, err := db.QueryContext(ctx, `SELECT CAST(l.GUID AS varchar(36)), ISNULL(l.ISIN,''), ISNULL(l.SEC_CODE,''), ISNULL(CAST(l.AMOUNT AS varchar(40)),''), ISNULL(l.STATE,''), ISNULL(l.COMMENT,'') FROM dbo.FTV_M2M_LINES l WHERE l.GUID IN (SELECT TOP (@n) GUID FROM dbo.FTV_M2M ORDER BY `+strings.Replace(strings.TrimPrefix(timeCol, "m."), "NULL", "GUID", 1)+` DESC)`, sql.Named("n", limit)) + lr, err := db.QueryContext(ctx, fsql.Que04+strings.Replace(strings.TrimPrefix(timeCol, "m."), "NULL", "GUID", 1)+` DESC)`, sql.Named("n", limit)) if err == nil { for lr.Next() { var g string @@ -164,7 +163,7 @@ func depoQueueLoad(ctx context.Context, rc *RuntimeConfig, limit int) ([]depoQue } // события if depoTableExists(ctx, db, "FTV_M2M_EVENTS") { - er, err := db.QueryContext(ctx, `SELECT CAST(e.GUID AS varchar(36)), ISNULL(e.REFERENCE,''), ISNULL(e.STATE,''), ISNULL(e.EVENT_TYPE,''), e.EVENT_DATE, ISNULL(e.M2M_REJECT_CODE,''), ISNULL(e.COMMENT,'') FROM dbo.FTV_M2M_EVENTS e WHERE e.GUID IN (SELECT TOP (@n) GUID FROM dbo.FTV_M2M ORDER BY `+strings.Replace(strings.TrimPrefix(timeCol, "m."), "NULL", "GUID", 1)+` DESC) ORDER BY e.EVENT_DATE`, sql.Named("n", limit)) + er, err := db.QueryContext(ctx, fsql.Que05+strings.Replace(strings.TrimPrefix(timeCol, "m."), "NULL", "GUID", 1)+` DESC) ORDER BY e.EVENT_DATE`, sql.Named("n", limit)) if err == nil { for er.Next() { var g string @@ -196,10 +195,10 @@ func depoQueueLoad(ctx context.Context, rc *RuntimeConfig, limit int) ([]depoQue } if depoTableExists(ctx, db, "FTV_M2M_HANDBOOK") { sum.HasHandbook = true - _ = db.QueryRowContext(ctx, "SELECT count(*) FROM dbo.FTV_M2M_HANDBOOK").Scan(&sum.Handbook) + _ = db.QueryRowContext(ctx, fsql.QQu01).Scan(&sum.Handbook) if depoHasColumn(ctx, db, "FTV_M2M_HANDBOOK", "HANDBOOK_AT") { var t sql.NullTime - if db.QueryRowContext(ctx, "SELECT max(HANDBOOK_AT) FROM dbo.FTV_M2M_HANDBOOK").Scan(&t) == nil && t.Valid { + if db.QueryRowContext(ctx, fsql.QQu02).Scan(&t) == nil && t.Valid { sum.HandbookAt = t.Time } } diff --git a/internal/lkgateway/depotables.go b/internal/lkgateway/depotables.go index dfeeeb5..3de20c7 100644 --- a/internal/lkgateway/depotables.go +++ b/internal/lkgateway/depotables.go @@ -12,6 +12,7 @@ import ( "context" "database/sql" "fmt" + "git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql" "regexp" "strings" ) @@ -225,8 +226,8 @@ func depoTablesGrantSQL(pg bool, user string) []string { if pg { return []string{ fmt.Sprintf(`GRANT USAGE ON SCHEMA public TO "%s"`, user), - fmt.Sprintf(`GRANT SELECT ON ALL TABLES IN SCHEMA public TO "%s"`, user), // учётные таблицы DEPO (сверка) - fmt.Sprintf(`GRANT SELECT, INSERT, UPDATE, DELETE ON %s TO "%s"`, strings.Join(depoTableNames(), ", "), user), + fmt.Sprintf(fsql.Tbl01, user), // учётные таблицы DEPO (сверка) + fmt.Sprintf(fsql.Tbl02, strings.Join(depoTableNames(), ", "), user), } } out := []string{ @@ -274,7 +275,7 @@ func depoTablesScript(f FancyMSSQLSettings) string { } else { b.WriteString("-- Права логину bj: выдайте SELECT, INSERT, UPDATE, DELETE на таблицы выше (логин bj в настройках не указан).\n\n") } - b.WriteString("-- Кроме того, логину bj нужно право SELECT на учётные таблицы DEPO (OD_CLIENTS, OD_CLIENTS_PORTS, OD_RESTS, OD_VALUES и др.) для сверки переводов.\n") + b.WriteString(fsql.QTb01) return b.String() } diff --git a/internal/lkgateway/fsql/archive.go b/internal/lkgateway/fsql/archive.go new file mode 100644 index 0000000..685dd41 --- /dev/null +++ b/internal/lkgateway/fsql/archive.go @@ -0,0 +1,91 @@ +package fsql + +// fsql — SQL-запросы к учётным системам DEPO и SPECTRE и к экспорту QUIK, +// вынесенные из lkgateway отдельным пакетом. В релизной сборке этот пакет +// (и только он) обфусцируется garble: строки запросов шифруются и в бинаре +// не читаются `strings`. Именно здесь лежит знание о чужих схемах (какие +// таблицы, связи и столбцы), которое дороже всего восстанавливать. +// +// ВАЖНО: только var, не const — константу компилятор вставил бы в +// вызывающий (необфусцированный) пакет, и шифрование бы не сработало. +// Запросы — data-only: параметры передаются через sql.Named на месте вызова. + +var Arch01 = `SELECT r.ID, b.NUM_S, ISNULL(b.SYS_NAME,''), b.NUM_1, ISNULL(r.REG_2,0), ISNULL(r.VALUE_ID,0), + CAST(ISNULL(r.A,0) AS float), CAST(ISNULL(r.D,0) AS float), CAST(ISNULL(r.EQ_D,0) AS float), + ISNULL(vs.ISIN,''), ISNULL(vs.NAME,''), ISNULL(vc.SYSNAME,''), ISNULL(sh.CLASS,0), CAST(ISNULL(sh.NOMINAL,0) AS float) + FROM OD_RESTS r JOIN OD_BALANCES b ON b.ID = r.BAL_ACC + LEFT JOIN OD_VALUES vs ON vs.ID = r.REG_2 + LEFT JOIN OD_VALUES vc ON vc.ID = r.VALUE_ID + LEFT JOIN OD_SHARES sh ON sh.ID = r.REG_2 + WHERE r.REG_3 = @c AND b.IS_BAL = 1 AND b.PART = 39 AND b.NUM_1 IN (50,51,52,55,58) + ORDER BY b.NUM_1, vs.NAME` + +var Arch02 = `SELECT REST, + CAST(SUM(CASE WHEN TYPE_ = 1 THEN AMOUNT_ ELSE -AMOUNT_ END) AS float), + CAST(SUM(CASE WHEN TYPE_ = 1 THEN VALUE_ ELSE -VALUE_ END) AS float), + CAST(SUM(CASE WHEN TYPE_ = 1 THEN EQ_ ELSE -EQ_ END) AS float) + FROM OD_TURNS WHERE REST IN (%s) AND WIRDATE >= @d AND IS_PLAN = 'F' AND YEAR(WIRDATE) < 9000 + GROUP BY REST` + +var Arch03 = `SELECT MAX(R_DATE) FROM OD_DOCS WHERE YEAR(R_DATE) < 9000` + +var Arch04 = `SELECT TOP 1 OFICDATE, CAST(RATE3 AS float), CAST(PFR_PRICE AS float), CAST(CLOSE_ AS float) + FROM OD_VALUES_RATES WHERE VALUE_ID = @v AND OFICDATE <= @d AND (RATE3 > 0 OR PFR_PRICE > 0 OR CLOSE_ > 0) + ORDER BY OFICDATE DESC` + +var Arch05 = `SELECT TOP 1 CAST(RATE AS float) FROM OD_VALUES_RATES WHERE VALUE_ID = @v AND MARKET = 0 AND OFICDATE <= @d AND RATE > 0 ORDER BY OFICDATE DESC` + +var Arch06 = `SELECT r.ID, b.NUM_1, ISNULL(b.SYS_NAME,'') FROM OD_RESTS r JOIN OD_BALANCES b ON b.ID = r.BAL_ACC + WHERE r.REG_3 = @c AND b.PART = 39 AND ((b.NUM_1 = 58 AND b.SYS_NAME LIKE N'ПОРТФЕЛЬ%') OR b.NUM_1 IN (50,51,52,55))` + +var Arch07 = ` + WITH w AS ( + SELECT wr.WIRDATE, wr.EQ_, wr.D_REST, wr.K_REST, wr.D_SUMMA, wr.K_SUMMA, wr.D_AMOUNT, wr.K_AMOUNT, wr.NAME, + wr.D_REST AS sec_rest, wr.K_REST AS money_rest + FROM OD_WIRING wr + WHERE wr.D_REST IN (%[1]s) AND wr.IS_PLAN = 'F' AND YEAR(wr.WIRDATE) < 9000 + AND wr.WIRDATE >= @f AND wr.WIRDATE < @t AND (wr.D_AMOUNT <> 0 OR wr.K_AMOUNT <> 0) + UNION ALL + SELECT wr.WIRDATE, wr.EQ_, wr.D_REST, wr.K_REST, wr.D_SUMMA, wr.K_SUMMA, wr.D_AMOUNT, wr.K_AMOUNT, wr.NAME, + wr.K_REST, wr.D_REST + FROM OD_WIRING wr + WHERE wr.K_REST IN (%[1]s) AND wr.D_REST NOT IN (%[1]s) AND wr.IS_PLAN = 'F' AND YEAR(wr.WIRDATE) < 9000 + AND wr.WIRDATE >= @f AND wr.WIRDATE < @t AND (wr.D_AMOUNT <> 0 OR wr.K_AMOUNT <> 0)) + SELECT w.WIRDATE, CAST(ISNULL(w.EQ_,0) AS float), w.D_REST, w.K_REST, + CAST(ISNULL(w.D_SUMMA,0) AS float), CAST(ISNULL(w.K_SUMMA,0) AS float), + CAST(ISNULL(w.D_AMOUNT,0) AS float), CAST(ISNULL(w.K_AMOUNT,0) AS float), + ISNULL(w.NAME,''), ISNULL(sv.NAME,''), ISNULL(sv.ISIN,''), ISNULL(mv.SYSNAME,'') + FROM w + LEFT JOIN OD_RESTS sr ON sr.ID = w.sec_rest + LEFT JOIN OD_VALUES sv ON sv.ID = sr.REG_2 + LEFT JOIN OD_RESTS mr ON mr.ID = w.money_rest + LEFT JOIN OD_VALUES mv ON mv.ID = mr.VALUE_ID + ORDER BY w.WIRDATE` + +var Arch08 = `SELECT w.WIRDATE, CAST(ISNULL(w.EQ_,0) AS float), w.D_REST, w.K_REST, + CAST(ISNULL(w.D_SUMMA,0) AS float), CAST(ISNULL(w.K_SUMMA,0) AS float), ISNULL(w.NAME,''), ISNULL(mv.SYSNAME,'') + FROM OD_WIRING w + JOIN OD_RESTS mr ON mr.ID = CASE WHEN w.D_REST IN (%[1]s) THEN w.D_REST ELSE w.K_REST END + LEFT JOIN OD_VALUES mv ON mv.ID = mr.VALUE_ID + WHERE (w.D_REST IN (%[1]s) OR w.K_REST IN (%[1]s)) AND w.IS_PLAN = 'F' AND YEAR(w.WIRDATE) < 9000 + AND w.WIRDATE >= @f AND w.WIRDATE < @t + ORDER BY w.WIRDATE` + +var Arch09 = `SELECT r.ID FROM OD_RESTS r JOIN OD_BALANCES b ON b.ID = r.BAL_ACC WHERE r.REG_3 = @c AND b.SYS_NAME = N'ВЫРУЧКА'` + +var Arch10 = ` + WITH w AS ( + SELECT wr.WIRDATE, wr.EQ_, wr.D_REST, wr.K_REST, wr.D_SUMMA, wr.K_SUMMA, wr.D_REST AS sec_rest + FROM OD_WIRING wr + WHERE wr.K_REST IN (%[1]s) AND wr.IS_PLAN = 'F' AND YEAR(wr.WIRDATE) < 9000 + AND wr.WIRDATE >= @f AND wr.WIRDATE < @t AND wr.EQ_ <> 0 + UNION ALL + SELECT wr.WIRDATE, wr.EQ_, wr.D_REST, wr.K_REST, wr.D_SUMMA, wr.K_SUMMA, wr.K_REST + FROM OD_WIRING wr + WHERE wr.D_REST IN (%[1]s) AND wr.K_REST NOT IN (%[1]s) AND wr.IS_PLAN = 'F' AND YEAR(wr.WIRDATE) < 9000 + AND wr.WIRDATE >= @f AND wr.WIRDATE < @t AND wr.EQ_ <> 0) + SELECT w.WIRDATE, CAST(ISNULL(w.EQ_,0) AS float), w.D_REST, w.K_REST, + CAST(ISNULL(w.D_SUMMA,0) AS float), CAST(ISNULL(w.K_SUMMA,0) AS float), ISNULL(sv.ISIN,'') + FROM w + LEFT JOIN OD_RESTS sr ON sr.ID = w.sec_rest + LEFT JOIN OD_VALUES sv ON sv.ID = sr.REG_2` diff --git a/internal/lkgateway/fsql/depo_check.go b/internal/lkgateway/fsql/depo_check.go new file mode 100644 index 0000000..1691151 --- /dev/null +++ b/internal/lkgateway/fsql/depo_check.go @@ -0,0 +1,28 @@ +package fsql + +var Chk01 = `SELECT TOP 1 ISNULL(NULLIF(SELF_ID,0),ID), ISNULL(NAME,''), DATE_BIRTH FROM OD_FACES WHERE REPLACE(ISNULL(DOC_SER,''),' ','')=@p1 AND DOC_NUM=@p2 + ORDER BY ISNULL(LAST_FLAG,0) DESC, ID DESC` + +var Chk02 = `SELECT ID, ISNULL(NUMBER,''), ISNULL(STATE,-1), D_CLOSE FROM OD_CLIENTS WHERE FACE=@p1 AND NUMBER LIKE N'ВТНКЦ%' ORDER BY ID` + +var Chk03 = `SELECT TOP 1 CAST(o.VAL AS varchar(20)) FROM OD_OPTIONS o + JOIN OP_FIELDS f ON f.ID = o.DESCR AND f.AS_NAME = 'IS_QUAL' + WHERE o.OBJECT = @p1 AND o.B_DATE <= GETDATE() AND (o.E_DATE IS NULL OR o.E_DATE > GETDATE()) + ORDER BY o.B_DATE DESC` + +var Chk04 = `SELECT ISNULL(PORT_NUM,'') FROM OD_CLIENTS_PORTS WHERE CLIENT=@p1` + +var Chk05 = `SELECT TOP 1 ID, ISNULL(NAME,''), IS_IN FROM OD_VALUES WHERE ISIN=@p1` + +var Chk06 = `SELECT TOP 1 CAST(o.VAL AS varchar(20)) FROM OD_OPTIONS o + JOIN OP_FIELDS f ON f.ID = o.DESCR AND f.AS_NAME = 'RESTRICTED' + WHERE o.OBJECT = @p1 AND o.B_DATE <= GETDATE() AND (o.E_DATE IS NULL OR o.E_DATE > GETDATE()) + ORDER BY o.B_DATE DESC` + +var Chk07 = `SELECT TOP 1 r.BLOCK_DATE, r.BLOCK_REASON, r.BAL_ACC, CAST(r.K AS float) + FROM OD_RESTS r WHERE r.VALUE_ID=@p1 AND r.REG_1=@p2 + ORDER BY CASE WHEN r.BLOCK_DATE IS NOT NULL THEN 0 WHEN r.BAL_ACC IN (837,3410,4342,5647) AND CAST(r.K AS float)<>0 THEN 1 ELSE 2 END` + +var Chk08 = `SELECT TOP 1 ID, ISNULL(STATE,-1), D_CLOSE FROM OD_CLIENTS WHERE FACE=@p1 AND UPPER(NUMBER)=@p2 ORDER BY ID` + +var Chk09 = `SELECT ISNULL(SUM(CAST(K AS float)),0) FROM OD_RESTS WHERE VALUE_ID=@p1 AND REG_1=@p2 AND BAL_ACC=@p3` diff --git a/internal/lkgateway/fsql/depo_sim.go b/internal/lkgateway/fsql/depo_sim.go new file mode 100644 index 0000000..a78e6ae --- /dev/null +++ b/internal/lkgateway/fsql/depo_sim.go @@ -0,0 +1,10 @@ +package fsql + +var Sim01 = `SELECT TOP 25 ISNULL(f.NAME,''), ISNULL(f.DOC_SER,''), ISNULL(f.DOC_NUM,''), ISNULL(c.NUMBER,'') + FROM OD_FACES f JOIN OD_CLIENTS c ON c.FACE=f.ID + WHERE c.NUMBER LIKE N'ВТНКЦ%' AND c.STATE=0 AND c.D_CLOSE IS NULL + AND LEN(ISNULL(f.DOC_SER,''))>0 AND LEN(ISNULL(f.DOC_NUM,''))>0 + ORDER BY f.NAME` + +var Sim02 = `SELECT TOP 20 ISNULL(ISIN,''), ISNULL(NAME,'') FROM OD_VALUES + WHERE V_TYPE=2 AND IS_IN=1 AND ISIN LIKE 'RU%' ORDER BY NAME` diff --git a/internal/lkgateway/fsql/depodb.go b/internal/lkgateway/fsql/depodb.go new file mode 100644 index 0000000..27c3d3a --- /dev/null +++ b/internal/lkgateway/fsql/depodb.go @@ -0,0 +1,19 @@ +package fsql + +var Dp01 = `SELECT p.PORT_NUM, ISNULL(c.NUMBER,''), ISNULL(v.ISIN,''), ISNULL(v.NAME,''), + CAST(rst.K AS float), ISNULL(rst.BLOCK_REASON,''), ISNULL(rst.BAL_ACC,0) + FROM OD_RESTS rst + JOIN OD_CLIENTS c ON c.ID = rst.REG_1 + JOIN OD_CLIENTS_PORTS p ON p.CLIENT = c.ID + JOIN OD_VALUES v ON v.ID = rst.VALUE_ID AND v.V_TYPE = 2 + WHERE (p.PORT_NUM = @p1 OR p.PORT_NUM LIKE @p2) + AND CAST(rst.K AS float) <> 0 + ORDER BY v.NAME` + +var Dp02 = `SELECT TOP (%d) p.PORT_NUM, COUNT(DISTINCT rst.VALUE_ID) + FROM OD_CLIENTS_PORTS p + JOIN OD_CLIENTS c ON c.ID = p.CLIENT + JOIN OD_RESTS rst ON rst.REG_1 = c.ID + JOIN OD_VALUES v ON v.ID = rst.VALUE_ID AND v.V_TYPE = 2 + WHERE CAST(rst.K AS float) <> 0 AND (@p1 = '' OR p.PORT_NUM LIKE @p2) + GROUP BY p.PORT_NUM ORDER BY p.PORT_NUM` diff --git a/internal/lkgateway/fsql/depodb_q.go b/internal/lkgateway/fsql/depodb_q.go new file mode 100644 index 0000000..bce0659 --- /dev/null +++ b/internal/lkgateway/fsql/depodb_q.go @@ -0,0 +1,7 @@ +package fsql + +var QDb01 = "SELECT TOP 1 PORT_NUM FROM OD_CLIENTS_PORTS WHERE PORT_NUM=@p1 OR PORT_NUM LIKE @p2" + +var QDb02 = "SELECT DISTINCT TOP (%d) PORT_NUM FROM OD_CLIENTS_PORTS WHERE PORT_NUM LIKE @p1 ORDER BY PORT_NUM" + +var QDb03 = "SELECT DISTINCT TOP (5000) PORT_NUM FROM OD_CLIENTS_PORTS WHERE PORT_NUM <> '' ORDER BY PORT_NUM" diff --git a/internal/lkgateway/fsql/depoevents.go b/internal/lkgateway/fsql/depoevents.go new file mode 100644 index 0000000..58112bd --- /dev/null +++ b/internal/lkgateway/fsql/depoevents.go @@ -0,0 +1,31 @@ +package fsql + +var Ev01 = `INSERT INTO dbo.FTV_M2M_EVENTS (GUID, REFERENCE, EVENT_TYPE, EVENT_DATE, M2M_REJECT_CODE) VALUES (@g, @r, @t, @at, @c)` + +var Ev02 = `INSERT INTO dbo.FTV_M2M_EVENTS (GUID, REFERENCE, EVENT_TYPE, EVENT_DATE, M2M_REJECT_CODE, PARAMETERS) VALUES (@g, @r, @t, @at, @c, @p)` + +var Ev03 = `SELECT TOP 1 c.NUMBER FROM OD_FACES f + JOIN OD_CLIENTS c ON c.FACE=f.ID LEFT JOIN OD_CLIENTS_PORTS p ON p.CLIENT=c.ID + WHERE REPLACE(f.DOC_SER,' ','')=@s AND f.DOC_NUM=@n AND c.NUMBER LIKE N'ВТНКЦ%' AND c.STATE=0 AND c.D_CLOSE IS NULL + ORDER BY ISNULL(p.PORT_DATE,'2099-01-01'), ISNULL(c.D_OPEN,'2099-01-01'), c.NUMBER` + +var Ev04 = `INSERT INTO dbo.FTV_M2M (GUID, DIRECTION, CONTRAGENT_M2M_CODE, DEPONENT_NAME, DEPONENT_DOC) VALUES (@g, @d, @cp, @nm, @doc)` + +var Ev05 = `INSERT INTO dbo.FTV_M2M (GUID, DIRECTION, CONTRAGENT_M2M_CODE, DEPONENT_NAME, DEPONENT_DOC, DEPONENT_ID) VALUES (@g, @d, @cp, @nm, @doc, @did)` + +var Ev06 = `INSERT INTO dbo.FTV_M2M_LINES (` + +var Ev07 = `SELECT TOP 200 CAST(m.GUID AS varchar(36)), ISNULL(m.STATE,''), ISNULL(` + +var Ev08 = `,''), ISNULL(m.COMMENT,''), + ISNULL((SELECT TOP 1 ISNULL(l.COMMENT,'') FROM dbo.FTV_M2M_LINES l WHERE l.GUID=m.GUID AND ISNULL(l.STATE,'')='ERROR'),''), + ISNULL((SELECT TOP 1 ISNULL(st.NAME,'') FROM OD_DOCS d LEFT JOIN OD_DOC_STATES st ON st.ID=d.STATE AND st.D_CAT=0 WHERE CAST(d.ID AS varchar(32))=` + +var Ev09 = `),''), + (SELECT TOP 1 d.PROC_DATE FROM OD_DOCS d WHERE CAST(d.ID AS varchar(32))=` + +var Ev10 = `INSERT INTO dbo.FTV_M2M_HANDBOOK (INN, PARTICIPANT_CODE, KIND, NAME_FULL, NAME_SHORT, NAME_DISPLAY, NAME_ENG, HANDBOOK_AT, UPDATED_AT, ACTIVE) + VALUES (@inn, @code, @kind, @full, @short, @disp, @eng, @at, GETDATE(), 1)` + +var Ev11 = `SELECT TOP 1 f.NAME FROM OD_FACES f JOIN OD_CLIENTS c ON c.FACE=f.ID + WHERE REPLACE(f.DOC_SER,' ','')=@s AND f.DOC_NUM=@n AND c.NUMBER LIKE N'ВТНКЦ%' AND c.STATE=0` diff --git a/internal/lkgateway/fsql/depoevents_q.go b/internal/lkgateway/fsql/depoevents_q.go new file mode 100644 index 0000000..95f4de1 --- /dev/null +++ b/internal/lkgateway/fsql/depoevents_q.go @@ -0,0 +1,17 @@ +package fsql + +var QEv01 = "SELECT COUNT(*) FROM dbo.FTV_M2M_EVENTS WHERE GUID=@g AND ISNULL(REFERENCE,'')=@r AND EVENT_TYPE=@t" + +var QEv02 = "SELECT COUNT(*) FROM dbo.FTV_M2M WHERE GUID=@g" + +var QEv03 = "SELECT TOP 1 SELF_ID FROM OD_FACES WHERE REPLACE(DOC_SER,' ','')=@s AND DOC_NUM=@n ORDER BY ID" + +var QEv04 = "SELECT TOP 1 ID FROM OD_VALUES WHERE ISIN=@i ORDER BY ID" + +var QEv05 = "DELETE FROM dbo.FTV_M2M_HANDBOOK" + +var QEv06 = "INSERT INTO dbo.FTV_M2M_EVENTS (GUID, REFERENCE, EVENT_TYPE, EVENT_DATE) VALUES (@g, NULL, 'BJ_PING', GETDATE())" + +var QEv07 = "DELETE FROM dbo.FTV_M2M_EVENTS WHERE GUID=@g" + +var QEv08 = "SELECT ISNULL(STATE,'') FROM dbo.FTV_M2M_EVENTS WHERE GUID=@g" diff --git a/internal/lkgateway/fsql/depohealth.go b/internal/lkgateway/fsql/depohealth.go new file mode 100644 index 0000000..cb29b56 --- /dev/null +++ b/internal/lkgateway/fsql/depohealth.go @@ -0,0 +1,13 @@ +package fsql + +var Hlth01 = `SELECT TOP (@n) * FROM ( + SELECT 'FTV_M2M' AS T, ID, CAST(GUID AS varchar(36)) AS G, '' AS R, ISNULL(DIRECTION,'') AS K, ISNULL(TimeEnd, TimeNew) AS A, ISNULL(COMMENT,'') AS C + FROM dbo.FTV_M2M WHERE STATE='ERROR' AND ISNULL(TimeEnd, TimeNew) > DATEADD(day, -@d, GETDATE()) + UNION ALL + SELECT 'FTV_M2M_EVENTS', ID, CAST(GUID AS varchar(36)), ISNULL(REFERENCE,''), EVENT_TYPE, ISNULL(TimeEnd, TimeNew), ISNULL(COMMENT,'') + FROM dbo.FTV_M2M_EVENTS WHERE STATE='ERROR' AND EVENT_TYPE LIKE 'BJ[_]%' AND ISNULL(TimeEnd, TimeNew) > DATEADD(day, -@d, GETDATE()) + ) x ORDER BY A DESC` + +var Hlth02 = `SELECT COUNT(*) OVER (), ID, EVENT_TYPE, CAST(GUID AS varchar(36)), ISNULL(COMMENT,'') + FROM dbo.FTV_M2M_EVENTS WHERE STATE='ERROR' AND EVENT_TYPE LIKE 'BJ[_]%' AND ID > @last AND ISNULL(TimeEnd, TimeNew) > DATEADD(day, -1, GETDATE()) + ORDER BY ID DESC OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY` diff --git a/internal/lkgateway/fsql/depohealth_q.go b/internal/lkgateway/fsql/depohealth_q.go new file mode 100644 index 0000000..8998b31 --- /dev/null +++ b/internal/lkgateway/fsql/depohealth_q.go @@ -0,0 +1,7 @@ +package fsql + +var QHl01 = "UPDATE dbo.FTV_M2M SET STATE='TODO', TimeBeg=NULL, TimeEnd=NULL, COMMENT=@c WHERE ID=@id AND STATE='ERROR'" + +var QHl02 = "UPDATE dbo.FTV_M2M_EVENTS SET STATE='TODO', TimeBeg=NULL, TimeEnd=NULL, COMMENT=@c WHERE ID=@id AND STATE='ERROR'" + +var QHl03 = "UPDATE dbo.FTV_M2M_LINES SET STATE='TODO', TimeBeg=NULL, TimeEnd=NULL WHERE GUID=@g AND STATE='ERROR'" diff --git a/internal/lkgateway/fsql/depoqueue.go b/internal/lkgateway/fsql/depoqueue.go new file mode 100644 index 0000000..27bf895 --- /dev/null +++ b/internal/lkgateway/fsql/depoqueue.go @@ -0,0 +1,13 @@ +package fsql + +var Que01 = `SELECT TOP (@n) CAST(m.GUID AS varchar(36)), ISNULL(m.STATE,''), ISNULL(m.DIRECTION,''), ISNULL(m.DEPONENT_NAME,''), ISNULL(m.DEPONENT_DOC,''), ISNULL(m.CONTRAGENT_M2M_CODE,''), ` + +var Que02 = `,''), ISNULL(m.COMMENT,''), + ISNULL((SELECT TOP 1 ISNULL(st.NAME,'') FROM OD_DOCS d LEFT JOIN OD_DOC_STATES st ON st.ID=d.STATE AND st.D_CAT=0 WHERE CAST(d.ID AS varchar(32))=` + +var Que03 = `),''), + (SELECT TOP 1 d.PROC_DATE FROM OD_DOCS d WHERE CAST(d.ID AS varchar(32))=` + +var Que04 = `SELECT CAST(l.GUID AS varchar(36)), ISNULL(l.ISIN,''), ISNULL(l.SEC_CODE,''), ISNULL(CAST(l.AMOUNT AS varchar(40)),''), ISNULL(l.STATE,''), ISNULL(l.COMMENT,'') FROM dbo.FTV_M2M_LINES l WHERE l.GUID IN (SELECT TOP (@n) GUID FROM dbo.FTV_M2M ORDER BY ` + +var Que05 = `SELECT CAST(e.GUID AS varchar(36)), ISNULL(e.REFERENCE,''), ISNULL(e.STATE,''), ISNULL(e.EVENT_TYPE,''), e.EVENT_DATE, ISNULL(e.M2M_REJECT_CODE,''), ISNULL(e.COMMENT,'') FROM dbo.FTV_M2M_EVENTS e WHERE e.GUID IN (SELECT TOP (@n) GUID FROM dbo.FTV_M2M ORDER BY ` diff --git a/internal/lkgateway/fsql/depoqueue_q.go b/internal/lkgateway/fsql/depoqueue_q.go new file mode 100644 index 0000000..81030b0 --- /dev/null +++ b/internal/lkgateway/fsql/depoqueue_q.go @@ -0,0 +1,5 @@ +package fsql + +var QQu01 = "SELECT count(*) FROM dbo.FTV_M2M_HANDBOOK" + +var QQu02 = "SELECT max(HANDBOOK_AT) FROM dbo.FTV_M2M_HANDBOOK" diff --git a/internal/lkgateway/fsql/depotables.go b/internal/lkgateway/fsql/depotables.go new file mode 100644 index 0000000..4278d1b --- /dev/null +++ b/internal/lkgateway/fsql/depotables.go @@ -0,0 +1,5 @@ +package fsql + +var Tbl01 = `GRANT SELECT ON ALL TABLES IN SCHEMA public TO "%s"` + +var Tbl02 = `GRANT SELECT, INSERT, UPDATE, DELETE ON %s TO "%s"` diff --git a/internal/lkgateway/fsql/depotables_q.go b/internal/lkgateway/fsql/depotables_q.go new file mode 100644 index 0000000..cbd5e9b --- /dev/null +++ b/internal/lkgateway/fsql/depotables_q.go @@ -0,0 +1,3 @@ +package fsql + +var QTb01 = "-- Кроме того, логину bj нужно право SELECT на учётные таблицы DEPO (OD_CLIENTS, OD_CLIENTS_PORTS, OD_RESTS, OD_VALUES и др.) для сверки переводов.\n" diff --git a/internal/lkgateway/fsql/incoming_handlers_q.go b/internal/lkgateway/fsql/incoming_handlers_q.go new file mode 100644 index 0000000..7aceb72 --- /dev/null +++ b/internal/lkgateway/fsql/incoming_handlers_q.go @@ -0,0 +1,3 @@ +package fsql + +var QIn01 = "SELECT CAST(DOC AS varchar(32)) FROM dbo.FTV_M2M WHERE GUID=@g" diff --git a/internal/lkgateway/fsql/lkclient.go b/internal/lkgateway/fsql/lkclient.go new file mode 100644 index 0000000..977dc83 --- /dev/null +++ b/internal/lkgateway/fsql/lkclient.go @@ -0,0 +1,4 @@ +package fsql + +var Cl01 = `SELECT TOP 1 DOC, INVESTOR, NAME, C_OPEN, C_CLOSE, C_STATE FROM D_B_CONTRACTS + WHERE UPPER(NAME) = @n OR UPPER(NAME) LIKE @n + ' %' ORDER BY C_STATE DESC, DOC DESC` diff --git a/internal/lkgateway/fsql/lkdocs.go b/internal/lkgateway/fsql/lkdocs.go new file mode 100644 index 0000000..d518aff --- /dev/null +++ b/internal/lkgateway/fsql/lkdocs.go @@ -0,0 +1,35 @@ +package fsql + +var Doc01 = `SELECT TOP 40 ISNULL(a.KD_TYPE,''), ISNULL(v.ISIN,''), ISNULL(v.NAME,''), a.FIX_DATE, a.OPER_DATE, ISNULL(a.OFFER_NAME,'') + FROM U_CA_ACTS a JOIN OD_VALUES v ON v.ID = a.SHARE + WHERE UPPER(v.ISIN) IN (%s) + AND ISNULL(a.FIX_DATE, a.OPER_DATE) >= DATEADD(month, -1, GETDATE()) + ORDER BY ISNULL(a.FIX_DATE, a.OPER_DATE)` + +var Doc02 = ` +CREATE TABLE IF NOT EXISTS lk.doc_requests ( + id bigserial PRIMARY KEY, + created_at timestamptz NOT NULL DEFAULT now(), + contract_number text NOT NULL, + client_name text, + kind text NOT NULL, + period_from date, + period_to date, + account text, + comment text, + status text NOT NULL DEFAULT 'new', + staff_note text, + done_at timestamptz +); +CREATE INDEX IF NOT EXISTS ix_lk_doc_requests_contract ON lk.doc_requests (contract_number, created_at DESC); +` + +var Doc03 = `INSERT INTO lk.doc_requests (contract_number, client_name, kind, period_from, period_to, account, comment) + VALUES ($1,$2,$3,$4,$5,$6,$7)` + +var Doc04 = `SELECT id, created_at, kind, period_from, period_to, COALESCE(account,''), status, COALESCE(staff_note,'') + FROM lk.doc_requests WHERE contract_number=$1 ORDER BY created_at DESC LIMIT 50` + +var Doc05 = `SELECT TOP 30 d.R_DATE, ISNULL(d.NUM,''), e.B_DATE, e.E_DATE, e.DEPO, d.STATE, ISNULL(e.IS_TURNS,0) + FROM U_OP_INF_EXTRACT e JOIN OD_DOCS d ON d.ID = e.DOC + WHERE e.DEPO IN (%s) ORDER BY d.R_DATE DESC` diff --git a/internal/lkgateway/fsql/lkflow.go b/internal/lkgateway/fsql/lkflow.go new file mode 100644 index 0000000..ba3a7cf --- /dev/null +++ b/internal/lkgateway/fsql/lkflow.go @@ -0,0 +1,7 @@ +package fsql + +var Flow01 = `SELECT ISNULL(` + +var Flow02 = `,''), ISNULL(m.STATE,''), ISNULL(m.COMMENT,''), + ISNULL((SELECT TOP 1 ISNULL(l.COMMENT,'') FROM dbo.FTV_M2M_LINES l WHERE l.GUID=m.GUID AND ISNULL(l.STATE,'')='ERROR'),'') + FROM dbo.FTV_M2M m WHERE m.GUID=@g` diff --git a/internal/lkgateway/fsql/lkflow_q.go b/internal/lkgateway/fsql/lkflow_q.go new file mode 100644 index 0000000..03d5503 --- /dev/null +++ b/internal/lkgateway/fsql/lkflow_q.go @@ -0,0 +1,3 @@ +package fsql + +var QFl01 = "SELECT ISNULL(STATE,''), ISNULL(COMMENT,'') FROM dbo.FTV_M2M_EVENTS WHERE GUID=@g AND EVENT_TYPE='BJ_GATEWAY_REJECT'" diff --git a/internal/lkgateway/fsql/lkfx.go b/internal/lkgateway/fsql/lkfx.go new file mode 100644 index 0000000..89dacf2 --- /dev/null +++ b/internal/lkgateway/fsql/lkfx.go @@ -0,0 +1,6 @@ +package fsql + +var Fx01 = `SELECT UPPER(LTRIM(RTRIM(v.SYSNAME))), r.OFICDATE, CAST(r.RATE AS float) + FROM OD_VALUES_RATES r JOIN OD_VALUES v ON v.ID = r.VALUE_ID + WHERE v.V_TYPE = 1 AND r.MARKET = 0 AND r.RATE > 0 + AND r.OFICDATE = (SELECT MAX(x.OFICDATE) FROM OD_VALUES_RATES x WHERE x.VALUE_ID = r.VALUE_ID AND x.MARKET = 0 AND x.RATE > 0)` diff --git a/internal/lkgateway/fsql/lkhistory.go b/internal/lkgateway/fsql/lkhistory.go new file mode 100644 index 0000000..0419256 --- /dev/null +++ b/internal/lkgateway/fsql/lkhistory.go @@ -0,0 +1,47 @@ +package fsql + +var Hist01 = `SELECT DISTINCT c.ID, ISNULL(c.NUMBER,''), ISNULL(c.FACE,0) + FROM OD_CLIENTS c JOIN OD_CLIENTS_PORTS p ON p.CLIENT = c.ID + WHERE p.PORT_NUM = @p1 OR p.PORT_NUM LIKE @p2` + +var Hist02 = `SELECT TOP %d * FROM ( + SELECT d.R_DATE AS RD, d.PROC_DATE AS PD, ISNULL(d.NUM,'') AS NUM, d.STATE AS ST, N'зачисление' AS KIND, + z.DEPO AS ACC, ISNULL(v.ISIN,'') AS ISIN, ISNULL(v.NAME,'') AS SEC, CAST(ISNULL(z.AMOUNT,0) AS float) AS QTY, + LTRIM(RTRIM(ISNULL(z.COMMENT,'') + ' ' + ISNULL(z.REJECT_REASON,''))) AS NOTE + FROM U_OP_INV_ZACH z JOIN OD_DOCS d ON d.ID = z.DOC LEFT JOIN OD_VALUES v ON v.ID = z.SHARE + WHERE z.DEPO IN (%s) + UNION ALL + SELECT d.R_DATE, d.PROC_DATE, ISNULL(d.NUM,''), d.STATE, N'списание', + p.DEPO, ISNULL(v.ISIN,''), ISNULL(v.NAME,''), -CAST(ISNULL(p.AMOUNT,0) AS float), + LTRIM(RTRIM(ISNULL(p.COMMENT,'') + ' ' + ISNULL(p.REJECT_REASON,''))) + FROM U_OP_INV_SPIS p JOIN OD_DOCS d ON d.ID = p.DOC LEFT JOIN OD_VALUES v ON v.ID = p.SHARE + WHERE p.DEPO IN (%s) + UNION ALL + SELECT d.R_DATE, d.PROC_DATE, ISNULL(d.NUM,''), d.STATE, N'перевод: списание', + t.D_DEPO, ISNULL(v.ISIN,''), ISNULL(v.NAME,''), -CAST(ISNULL(t.AMOUNT,0) AS float), + LTRIM(RTRIM(ISNULL(t.COMMENT,'') + ' ' + ISNULL(t.REJECT_REASON,''))) + FROM U_OP_INV_PEREVOD t JOIN OD_DOCS d ON d.ID = t.DOC LEFT JOIN OD_VALUES v ON v.ID = t.SHARE + WHERE t.D_DEPO IN (%s) + UNION ALL + SELECT d.R_DATE, d.PROC_DATE, ISNULL(d.NUM,''), d.STATE, N'перевод: зачисление', + t.K_DEPO, ISNULL(v.ISIN,''), ISNULL(v.NAME,''), CAST(ISNULL(t.AMOUNT,0) AS float), + LTRIM(RTRIM(ISNULL(t.COMMENT,'') + ' ' + ISNULL(t.REJECT_REASON,''))) + FROM U_OP_INV_PEREVOD t JOIN OD_DOCS d ON d.ID = t.DOC LEFT JOIN OD_VALUES v ON v.ID = t.SHARE + WHERE t.K_DEPO IN (%s)) x + WHERE x.RD >= @p1 AND x.RD < DATEADD(day, 1, @p2) + ORDER BY x.RD DESC, x.NUM DESC` + +var Hist03 = `SELECT TOP 200 h.P_DATE, h.INCOME_KIND, ISNULL(v.ISIN,''), ISNULL(v.NAME,''), + CAST(ISNULL(l.AMOUNT,0) AS float), CAST(ISNULL(l.SUMMA_TOTAL,0) AS float), + CAST(ISNULL(l.TAX_HOLDED,0) AS float), CAST(ISNULL(l.TOTAL_TO_PAY,0) AS float), + ISNULL(l.STATUS,''), ISNULL(l.DEPO,0) + FROM U_OP_INF_INCOME_DOL l + JOIN OD_DOLS o ON o.ID = l.DOL + JOIN U_OP_INF_INCOME h ON h.DOC = o.DOC + LEFT JOIN OD_VALUES v ON v.ID = h.SHARE + WHERE (l.DEPO IN (%s) OR l.INCOME_FACE = @p3) + AND h.P_DATE >= @p1 AND h.P_DATE < DATEADD(day, 1, @p2) + ORDER BY h.P_DATE DESC` + +var Hist04 = `SELECT CAST(ISNULL(SUM(CAST(SUMMA AS float)),0) AS float) FROM OD_NDFL + WHERE FACE = @p1 AND R_DATE >= @p2 AND R_DATE < DATEADD(day, 1, @p3)` diff --git a/internal/lkgateway/fsql/lkonline.go b/internal/lkgateway/fsql/lkonline.go new file mode 100644 index 0000000..dfa9ed7 --- /dev/null +++ b/internal/lkgateway/fsql/lkonline.go @@ -0,0 +1,22 @@ +package fsql + +var On01 = ` + WITH d AS ( + SELECT DISTINCT ON ("ClientCode","SecCode","Trdacc") "SecCode","CurrentBal","AwgPositionPrice","LoadDate" + FROM "DepoLimits" WHERE "ClientCode" = ANY($1) AND "LimitKind"='T2' + ORDER BY "ClientCode","SecCode","Trdacc","LoadDate" DESC) + SELECT "SecCode", SUM("CurrentBal")::float8, + COALESCE(SUM("CurrentBal"*"AwgPositionPrice")/NULLIF(SUM("CurrentBal"),0),0)::float8, MAX("LoadDate") + FROM d WHERE "CurrentBal" <> 0 GROUP BY "SecCode" ORDER BY "SecCode"` + +var On02 = ` + SELECT DISTINCT ON ("SecCode") "SecCode", upper(COALESCE("ISINCode",'')), COALESCE("SecShortName","SecName",''), "ClassCode", COALESCE("FaceValue",0)::float8 + FROM "Securities" WHERE "SecCode" = ANY($1) + ORDER BY "SecCode", CASE "ClassCode" WHEN 'TQBR' THEN 1 WHEN 'TQOB' THEN 2 WHEN 'TQCB' THEN 3 WHEN 'TQTF' THEN 4 WHEN 'TQIF' THEN 5 WHEN 'TQRD' THEN 6 ELSE 9 END` + +var On03 = ` + WITH m AS ( + SELECT DISTINCT ON ("ClientCode","CurrCode","Tag") "CurrCode","CurrentBal","LoadDate" + FROM "MoneyLimits" WHERE "ClientCode" = ANY($1) AND "LimitKind"='T2' + ORDER BY "ClientCode","CurrCode","Tag","LoadDate" DESC) + SELECT "CurrCode", SUM("CurrentBal")::float8, MAX("LoadDate") FROM m GROUP BY "CurrCode" HAVING SUM("CurrentBal") <> 0 ORDER BY "CurrCode"` diff --git a/internal/lkgateway/fsql/lksnapshot.go b/internal/lkgateway/fsql/lksnapshot.go new file mode 100644 index 0000000..fc42894 --- /dev/null +++ b/internal/lkgateway/fsql/lksnapshot.go @@ -0,0 +1,22 @@ +package fsql + +var Sn01 = ` +CREATE TABLE IF NOT EXISTS lk.portfolio_daily ( + day DATE NOT NULL, contract VARCHAR(64) NOT NULL, + value DOUBLE PRECISION NOT NULL DEFAULT 0, cash DOUBLE PRECISION NOT NULL DEFAULT 0, + positions INT NOT NULL DEFAULT 0, priced INT NOT NULL DEFAULT 0, + source VARCHAR(16) NOT NULL DEFAULT 'quik', created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + PRIMARY KEY (day, contract))` + +var Sn02 = `SELECT day, value, cash, positions FROM lk.portfolio_daily + WHERE contract = $1 AND day >= current_date - $2::int ORDER BY day` + +var Sn03 = `INSERT INTO lk.portfolio_daily(day, contract, value, cash, positions, priced, source) + VALUES ($1,$2,$3,$4,$5,$6,$7) + ON CONFLICT (day, contract) DO UPDATE SET value = EXCLUDED.value, cash = EXCLUDED.cash, + positions = EXCLUDED.positions, priced = EXCLUDED.priced, source = EXCLUDED.source, created_at = now()` + +var Sn04 = `SELECT DISTINCT "ClientCode" FROM "DepoLimits" + WHERE "LimitKind" = 'T2' AND "CurrentBal" <> 0 AND "ClientCode" <> ''` + +var Sn05 = `SELECT count(*) FROM lk.portfolio_daily WHERE day = $1` diff --git a/internal/lkgateway/fsql/lkupcoming.go b/internal/lkgateway/fsql/lkupcoming.go new file mode 100644 index 0000000..2faf6ee --- /dev/null +++ b/internal/lkgateway/fsql/lkupcoming.go @@ -0,0 +1,17 @@ +package fsql + +var Up01 = `SELECT UPPER(v.ISIN), ISNULL(c.P_DATE, c.E_DATE), CAST(ISNULL(c.SUMMA,0) AS float), CAST(ISNULL(c.PERCENT_,0) AS float) + FROM OD_COUPONS c JOIN OD_VALUES v ON v.ID = c.SHARE + WHERE UPPER(v.ISIN) IN (%s) AND ISNULL(c.P_DATE, c.E_DATE) >= @f AND ISNULL(c.P_DATE, c.E_DATE) <= @t` + +var Up02 = `SELECT UPPER(v.ISIN), o.P_DATE, CAST(ISNULL(o.O_PRICE,0) AS float) + FROM OD_OFFERS o JOIN OD_VALUES v ON v.ID = o.SHARE + WHERE UPPER(v.ISIN) IN (%s) AND o.CANCEL_DATE IS NULL AND o.P_DATE >= @f AND o.P_DATE <= @t` + +var Up03 = `SELECT UPPER(v.ISIN), sh.DEATHDATE, CAST(ISNULL(sh.NOMINAL,0) AS float) + FROM OD_SHARES sh JOIN OD_VALUES v ON v.ID = sh.ID + WHERE UPPER(v.ISIN) IN (%s) AND sh.E_DATE > GETDATE() AND sh.DEATHDATE >= @f AND sh.DEATHDATE <= @t` + +var Up04 = `SELECT UPPER(v.ISIN), ISNULL(a.OPER_DATE, a.FIX_DATE) + FROM U_CA_ACTS a JOIN OD_VALUES v ON v.ID = a.SHARE + WHERE UPPER(v.ISIN) IN (%s) AND UPPER(ISNULL(a.KD_TYPE,'')) = 'DVCA' AND ISNULL(a.OPER_DATE, a.FIX_DATE) >= @f AND ISNULL(a.OPER_DATE, a.FIX_DATE) <= @t` diff --git a/internal/lkgateway/fsql/manual_depo.go b/internal/lkgateway/fsql/manual_depo.go new file mode 100644 index 0000000..af69c9e --- /dev/null +++ b/internal/lkgateway/fsql/manual_depo.go @@ -0,0 +1,19 @@ +package fsql + +var Man01 = `SELECT TOP 20 ISNULL(NULLIF(f.SELF_ID,0),f.ID), ISNULL(f.NAME,''), f.DATE_BIRTH, ISNULL(f.DOC_TYPE,0), ISNULL(f.DOC_SER,''), ISNULL(f.DOC_NUM,''), + f.DOC_DAT, ISNULL(f.DOC_PLACE,''), ISNULL(f.DOC_CODE,''), ISNULL(f.PHONE,'') + FROM OD_FACES f + WHERE ISNULL(f.LAST_FLAG,1)=1 AND (UPPER(f.NAME) LIKE UPPER(@q) OR f.DOC_NUM=@d OR REPLACE(ISNULL(f.DOC_SER,''),' ','')+f.DOC_NUM=@d) + AND EXISTS (SELECT 1 FROM OD_CLIENTS c WHERE c.FACE=ISNULL(NULLIF(f.SELF_ID,0),f.ID) AND c.NUMBER LIKE N'ВТНКЦ%' AND c.STATE=0 AND c.D_CLOSE IS NULL) + ORDER BY f.NAME` + +var Man02 = `SELECT c.ID, ISNULL(c.NUMBER,''), + ISNULL((SELECT TOP 1 p.PORT_NUM FROM OD_CLIENTS_PORTS p WHERE p.CLIENT=c.ID ORDER BY p.PORT_DATE, p.PORT_NUM),'') + FROM OD_CLIENTS c WHERE c.FACE=@f AND c.NUMBER LIKE N'ВТНКЦ%' AND c.STATE=0 AND c.D_CLOSE IS NULL ORDER BY c.ID` + +var Man03 = `SELECT ISNULL(v.ISIN,''), ISNULL(v.NAME,''), SUM(CAST(r.K AS float)) + FROM OD_RESTS r JOIN OD_VALUES v ON v.ID=r.VALUE_ID + WHERE r.REG_1=@c AND r.BAL_ACC=@b + GROUP BY v.ISIN, v.NAME HAVING SUM(CAST(r.K AS float)) > 0 ORDER BY v.NAME` + +var Man04 = `SELECT TOP 1 ID FROM OD_CLIENTS WHERE NUMBER=@n AND STATE=0 AND D_CLOSE IS NULL ORDER BY ID` diff --git a/internal/lkgateway/fsql/prices.go b/internal/lkgateway/fsql/prices.go new file mode 100644 index 0000000..b4fdabd --- /dev/null +++ b/internal/lkgateway/fsql/prices.go @@ -0,0 +1,13 @@ +package fsql + +var Pr01 = ` + SELECT DISTINCT ON (s."ISINCode") + upper(s."ISINCode"), s."ClassCode", COALESCE(s."SecShortName", s."SecName", ''), + COALESCE(p.last, 0)::float8, COALESCE(p.prevprice, 0)::float8, + COALESCE(s."FaceValue", 0)::float8, COALESCE(p.accruedint, 0)::float8, COALESCE(s."FaceUnit", '') + FROM "Securities" s JOIN "Params" p ON p."SecCode" = s."SecCode" AND p."ClassCode" = s."ClassCode" + WHERE s."ISINCode" <> '' AND (p.last > 0 OR p.prevprice > 0) + ORDER BY s."ISINCode", + CASE s."ClassCode" WHEN 'TQBR' THEN 1 WHEN 'TQOB' THEN 2 WHEN 'TQCB' THEN 3 + WHEN 'TQTF' THEN 4 WHEN 'TQIF' THEN 5 WHEN 'TQRD' THEN 6 ELSE 9 END, + p.last DESC` diff --git a/internal/lkgateway/fsql/quikcycle.go b/internal/lkgateway/fsql/quikcycle.go new file mode 100644 index 0000000..d98fdaa --- /dev/null +++ b/internal/lkgateway/fsql/quikcycle.go @@ -0,0 +1,9 @@ +package fsql + +var Qk01 = `SELECT ic.ClassCode, sec.SecCode FROM ISecurities sec JOIN IClasses ic ON ic.ClassId=sec.ClassId + WHERE sec.Enable=1 AND ic.ClassCode IN ('INSTRINM2M','INSTROUTM2M') ORDER BY sec.SecurityId` + +var Qk02 = `SELECT TOP 5 p.PORT_NUM FROM OD_FACES f + JOIN OD_CLIENTS c ON c.FACE=f.ID JOIN OD_CLIENTS_PORTS p ON p.CLIENT=c.ID + WHERE REPLACE(f.DOC_SER,' ','')=@p1 AND f.DOC_NUM=@p2 AND c.NUMBER LIKE N'ВТНКЦ%' AND c.STATE=0 + ORDER BY ISNULL(p.PORT_DATE,'2099-01-01'), ISNULL(c.D_OPEN,'2099-01-01'), c.NUMBER` diff --git a/internal/lkgateway/fsql/security_card.go b/internal/lkgateway/fsql/security_card.go new file mode 100644 index 0000000..aa19de9 --- /dev/null +++ b/internal/lkgateway/fsql/security_card.go @@ -0,0 +1,16 @@ +package fsql + +var Sec01 = `SELECT TOP 1 ID, ISNULL(NAME,''), ISNULL(CFI,''), V_TYPE, IS_IN FROM OD_VALUES WHERE ISIN=@p1 ORDER BY ID` + +var Sec02 = `SELECT TOP 1 ISSUER, CAST(NOMINAL AS float), NUM_REG, DEATHDATE FROM OD_SHARES + WHERE ID=@p1 AND E_DATE > GETDATE() ORDER BY B_DATE DESC` + +var Sec03 = `SELECT TOP 1 ISNULL(NAME,'') FROM OD_FACES WHERE ID=@p1 OR SELF_ID=@p1 ORDER BY ISNULL(LAST_FLAG,0) DESC, ID DESC` + +var Sec04 = `SELECT TOP 12 ISNULL(P_DATE, E_DATE) AS PAY_DATE, CAST(ISNULL(SUMMA,0) AS float), + CAST(ISNULL(PERCENT_,0) AS float), FIX_DATE, ISNULL(IS_PAY,0) + FROM OD_COUPONS WHERE SHARE=@p1 AND ISNULL(P_DATE, E_DATE) >= DATEADD(month,-3,GETDATE()) + ORDER BY PAY_DATE` + +var Sec05 = `SELECT TOP 3 P_DATE, CAST(ISNULL(O_PRICE,0) AS float) FROM OD_OFFERS + WHERE SHARE=@p1 AND CANCEL_DATE IS NULL AND P_DATE >= GETDATE() ORDER BY P_DATE` diff --git a/internal/lkgateway/incoming_handlers.go b/internal/lkgateway/incoming_handlers.go index 62f3a97..e97c1af 100644 --- a/internal/lkgateway/incoming_handlers.go +++ b/internal/lkgateway/incoming_handlers.go @@ -4,6 +4,7 @@ import ( "context" "database/sql" "encoding/json" + "git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql" "net/http" "net/url" "path" @@ -72,7 +73,7 @@ func registerIncoming(mux *http.ServeMux, a *admin, deps incomingDeps) { for i := 0; i < 8; i++ { time.Sleep(5 * time.Second) var d sql.NullString - _ = db.QueryRowContext(ctx, "SELECT CAST(DOC AS varchar(32)) FROM dbo.FTV_M2M WHERE GUID=@g", sql.Named("g", guid)).Scan(&d) + _ = db.QueryRowContext(ctx, fsql.QIn01, sql.Named("g", guid)).Scan(&d) if d.Valid && d.String != "" && d.String != "0" { doc = d.String break diff --git a/internal/lkgateway/lkarchive.go b/internal/lkgateway/lkarchive.go index 566820c..c232487 100644 --- a/internal/lkgateway/lkarchive.go +++ b/internal/lkgateway/lkarchive.go @@ -19,6 +19,7 @@ import ( "context" "database/sql" "fmt" + "git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql" "sort" "strings" "sync" @@ -63,15 +64,7 @@ func lkArchivePortfolio(ctx context.Context, s FancyMSSQLSettings, contract stri today := time.Now() isPast := day.Before(time.Date(today.Year(), today.Month(), today.Day(), 0, 0, 0, 0, time.UTC)) - rows, err := conn.QueryContext(cctx, `SELECT r.ID, b.NUM_S, ISNULL(b.SYS_NAME,''), b.NUM_1, ISNULL(r.REG_2,0), ISNULL(r.VALUE_ID,0), - CAST(ISNULL(r.A,0) AS float), CAST(ISNULL(r.D,0) AS float), CAST(ISNULL(r.EQ_D,0) AS float), - ISNULL(vs.ISIN,''), ISNULL(vs.NAME,''), ISNULL(vc.SYSNAME,''), ISNULL(sh.CLASS,0), CAST(ISNULL(sh.NOMINAL,0) AS float) - FROM OD_RESTS r JOIN OD_BALANCES b ON b.ID = r.BAL_ACC - LEFT JOIN OD_VALUES vs ON vs.ID = r.REG_2 - LEFT JOIN OD_VALUES vc ON vc.ID = r.VALUE_ID - LEFT JOIN OD_SHARES sh ON sh.ID = r.REG_2 - WHERE r.REG_3 = @c AND b.IS_BAL = 1 AND b.PART = 39 AND b.NUM_1 IN (50,51,52,55,58) - ORDER BY b.NUM_1, vs.NAME`, sql.Named("c", c.DOC)) + rows, err := conn.QueryContext(cctx, fsql.Arch01, sql.Named("c", c.DOC)) if err != nil { return nil, fmt.Errorf("остатки: %v", err) } @@ -91,12 +84,7 @@ func lkArchivePortfolio(ctx context.Context, s FancyMSSQLSettings, contract stri for _, r := range rests { ids = append(ids, fmt.Sprint(r.id)) } - trows, err := conn.QueryContext(cctx, fmt.Sprintf(`SELECT REST, - CAST(SUM(CASE WHEN TYPE_ = 1 THEN AMOUNT_ ELSE -AMOUNT_ END) AS float), - CAST(SUM(CASE WHEN TYPE_ = 1 THEN VALUE_ ELSE -VALUE_ END) AS float), - CAST(SUM(CASE WHEN TYPE_ = 1 THEN EQ_ ELSE -EQ_ END) AS float) - FROM OD_TURNS WHERE REST IN (%s) AND WIRDATE >= @d AND IS_PLAN = 'F' AND YEAR(WIRDATE) < 9000 - GROUP BY REST`, strings.Join(ids, ",")), sql.Named("d", day.AddDate(0, 0, 1))) + trows, err := conn.QueryContext(cctx, fmt.Sprintf(fsql.Arch02, strings.Join(ids, ",")), sql.Named("d", day.AddDate(0, 0, 1))) if err != nil { return nil, fmt.Errorf("обороты: %v", err) } @@ -209,7 +197,7 @@ func spectreAsOf(ctx context.Context, conn *sql.DB) time.Time { return spectreAsOfCache.v } var d sql.NullTime - _ = conn.QueryRowContext(ctx, `SELECT MAX(R_DATE) FROM OD_DOCS WHERE YEAR(R_DATE) < 9000`).Scan(&d) + _ = conn.QueryRowContext(ctx, fsql.Arch03).Scan(&d) v := time.Time{} if d.Valid { v = time.Date(d.Time.Year(), d.Time.Month(), d.Time.Day(), 0, 0, 0, 0, time.UTC) @@ -253,9 +241,7 @@ func spectreKind(class int) string { func spectreQuote(ctx context.Context, conn *sql.DB, secID int64, day time.Time) (float64, int) { var ofic sql.NullTime var rate3, pfr, closeP sql.NullFloat64 - err := conn.QueryRowContext(ctx, `SELECT TOP 1 OFICDATE, CAST(RATE3 AS float), CAST(PFR_PRICE AS float), CAST(CLOSE_ AS float) - FROM OD_VALUES_RATES WHERE VALUE_ID = @v AND OFICDATE <= @d AND (RATE3 > 0 OR PFR_PRICE > 0 OR CLOSE_ > 0) - ORDER BY OFICDATE DESC`, sql.Named("v", secID), sql.Named("d", day.AddDate(0, 0, 1))).Scan(&ofic, &rate3, &pfr, &closeP) + err := conn.QueryRowContext(ctx, fsql.Arch04, sql.Named("v", secID), sql.Named("d", day.AddDate(0, 0, 1))).Scan(&ofic, &rate3, &pfr, &closeP) if err != nil { return 0, 1 << 30 } @@ -287,7 +273,7 @@ func spectreFx(ctx context.Context, conn *sql.DB, day time.Time) func(curID int6 return v } var rate sql.NullFloat64 - _ = conn.QueryRowContext(ctx, `SELECT TOP 1 CAST(RATE AS float) FROM OD_VALUES_RATES WHERE VALUE_ID = @v AND MARKET = 0 AND OFICDATE <= @d AND RATE > 0 ORDER BY OFICDATE DESC`, + _ = conn.QueryRowContext(ctx, fsql.Arch05, sql.Named("v", curID), sql.Named("d", day.AddDate(0, 0, 1))).Scan(&rate) v := rate.Float64 if v <= 0 { @@ -355,8 +341,7 @@ func lkArchiveOperationsLoad(ctx context.Context, s FancyMSSQLSettings, contract cctx, cancel := context.WithTimeout(ctx, 40*time.Second) defer cancel() // Счета бумаг (ПОРТФЕЛЬ) и денег (касса) договора. - rows, err := conn.QueryContext(cctx, `SELECT r.ID, b.NUM_1, ISNULL(b.SYS_NAME,'') FROM OD_RESTS r JOIN OD_BALANCES b ON b.ID = r.BAL_ACC - WHERE r.REG_3 = @c AND b.PART = 39 AND ((b.NUM_1 = 58 AND b.SYS_NAME LIKE N'ПОРТФЕЛЬ%') OR b.NUM_1 IN (50,51,52,55))`, sql.Named("c", c.DOC)) + rows, err := conn.QueryContext(cctx, fsql.Arch06, sql.Named("c", c.DOC)) if err != nil { return nil, err } @@ -404,29 +389,7 @@ func lkArchiveOperationsLoad(ctx context.Context, s FancyMSSQLSettings, contract // разбирать список счетов на каждой строке журнала — год данных читался // 6–7 секунд, сейчас — десятки миллисекунд (оба индекса по D_REST/K_REST // с WIRDATE отрабатывают поиском). - wrows, err := conn.QueryContext(cctx, fmt.Sprintf(` - WITH w AS ( - SELECT wr.WIRDATE, wr.EQ_, wr.D_REST, wr.K_REST, wr.D_SUMMA, wr.K_SUMMA, wr.D_AMOUNT, wr.K_AMOUNT, wr.NAME, - wr.D_REST AS sec_rest, wr.K_REST AS money_rest - FROM OD_WIRING wr - WHERE wr.D_REST IN (%[1]s) AND wr.IS_PLAN = 'F' AND YEAR(wr.WIRDATE) < 9000 - AND wr.WIRDATE >= @f AND wr.WIRDATE < @t AND (wr.D_AMOUNT <> 0 OR wr.K_AMOUNT <> 0) - UNION ALL - SELECT wr.WIRDATE, wr.EQ_, wr.D_REST, wr.K_REST, wr.D_SUMMA, wr.K_SUMMA, wr.D_AMOUNT, wr.K_AMOUNT, wr.NAME, - wr.K_REST, wr.D_REST - FROM OD_WIRING wr - WHERE wr.K_REST IN (%[1]s) AND wr.D_REST NOT IN (%[1]s) AND wr.IS_PLAN = 'F' AND YEAR(wr.WIRDATE) < 9000 - AND wr.WIRDATE >= @f AND wr.WIRDATE < @t AND (wr.D_AMOUNT <> 0 OR wr.K_AMOUNT <> 0)) - SELECT w.WIRDATE, CAST(ISNULL(w.EQ_,0) AS float), w.D_REST, w.K_REST, - CAST(ISNULL(w.D_SUMMA,0) AS float), CAST(ISNULL(w.K_SUMMA,0) AS float), - CAST(ISNULL(w.D_AMOUNT,0) AS float), CAST(ISNULL(w.K_AMOUNT,0) AS float), - ISNULL(w.NAME,''), ISNULL(sv.NAME,''), ISNULL(sv.ISIN,''), ISNULL(mv.SYSNAME,'') - FROM w - LEFT JOIN OD_RESTS sr ON sr.ID = w.sec_rest - LEFT JOIN OD_VALUES sv ON sv.ID = sr.REG_2 - LEFT JOIN OD_RESTS mr ON mr.ID = w.money_rest - LEFT JOIN OD_VALUES mv ON mv.ID = mr.VALUE_ID - ORDER BY w.WIRDATE`, in), sql.Named("f", fromDay), sql.Named("t", toEnd)) + wrows, err := conn.QueryContext(cctx, fmt.Sprintf(fsql.Arch07, in), sql.Named("f", fromDay), sql.Named("t", toEnd)) if err != nil { return nil, fmt.Errorf("проводки: %v", err) } @@ -501,14 +464,7 @@ func spectreCashOps(ctx context.Context, conn *sql.DB, cashIDs []string, fromDay } var out []LKOperationRow in := strings.Join(cashIDs, ",") - crows, err := conn.QueryContext(ctx, fmt.Sprintf(`SELECT w.WIRDATE, CAST(ISNULL(w.EQ_,0) AS float), w.D_REST, w.K_REST, - CAST(ISNULL(w.D_SUMMA,0) AS float), CAST(ISNULL(w.K_SUMMA,0) AS float), ISNULL(w.NAME,''), ISNULL(mv.SYSNAME,'') - FROM OD_WIRING w - JOIN OD_RESTS mr ON mr.ID = CASE WHEN w.D_REST IN (%[1]s) THEN w.D_REST ELSE w.K_REST END - LEFT JOIN OD_VALUES mv ON mv.ID = mr.VALUE_ID - WHERE (w.D_REST IN (%[1]s) OR w.K_REST IN (%[1]s)) AND w.IS_PLAN = 'F' AND YEAR(w.WIRDATE) < 9000 - AND w.WIRDATE >= @f AND w.WIRDATE < @t - ORDER BY w.WIRDATE`, in), sql.Named("f", fromDay), sql.Named("t", toEnd)) + crows, err := conn.QueryContext(ctx, fmt.Sprintf(fsql.Arch08, in), sql.Named("f", fromDay), sql.Named("t", toEnd)) if err != nil { return nil } @@ -571,7 +527,7 @@ func spectreDealKind(name string, onDebit bool) string { // spectreProceeds — выручка от продаж по (ISIN|день): [сумма в валюте, сумма в рублях]. func spectreProceeds(ctx context.Context, conn *sql.DB, doc int64, from, to time.Time) map[string][2]float64 { out := map[string][2]float64{} - rows, err := conn.QueryContext(ctx, `SELECT r.ID FROM OD_RESTS r JOIN OD_BALANCES b ON b.ID = r.BAL_ACC WHERE r.REG_3 = @c AND b.SYS_NAME = N'ВЫРУЧКА'`, sql.Named("c", doc)) + rows, err := conn.QueryContext(ctx, fsql.Arch09, sql.Named("c", doc)) if err != nil { return out } @@ -589,22 +545,7 @@ func spectreProceeds(ctx context.Context, conn *sql.DB, doc int64, from, to time return out } in := strings.Join(ids, ",") - wrows, err := conn.QueryContext(ctx, fmt.Sprintf(` - WITH w AS ( - SELECT wr.WIRDATE, wr.EQ_, wr.D_REST, wr.K_REST, wr.D_SUMMA, wr.K_SUMMA, wr.D_REST AS sec_rest - FROM OD_WIRING wr - WHERE wr.K_REST IN (%[1]s) AND wr.IS_PLAN = 'F' AND YEAR(wr.WIRDATE) < 9000 - AND wr.WIRDATE >= @f AND wr.WIRDATE < @t AND wr.EQ_ <> 0 - UNION ALL - SELECT wr.WIRDATE, wr.EQ_, wr.D_REST, wr.K_REST, wr.D_SUMMA, wr.K_SUMMA, wr.K_REST - FROM OD_WIRING wr - WHERE wr.D_REST IN (%[1]s) AND wr.K_REST NOT IN (%[1]s) AND wr.IS_PLAN = 'F' AND YEAR(wr.WIRDATE) < 9000 - AND wr.WIRDATE >= @f AND wr.WIRDATE < @t AND wr.EQ_ <> 0) - SELECT w.WIRDATE, CAST(ISNULL(w.EQ_,0) AS float), w.D_REST, w.K_REST, - CAST(ISNULL(w.D_SUMMA,0) AS float), CAST(ISNULL(w.K_SUMMA,0) AS float), ISNULL(sv.ISIN,'') - FROM w - LEFT JOIN OD_RESTS sr ON sr.ID = w.sec_rest - LEFT JOIN OD_VALUES sv ON sv.ID = sr.REG_2`, in), sql.Named("f", from), sql.Named("t", to)) + wrows, err := conn.QueryContext(ctx, fmt.Sprintf(fsql.Arch10, in), sql.Named("f", from), sql.Named("t", to)) if err != nil { return out } diff --git a/internal/lkgateway/lkclient.go b/internal/lkgateway/lkclient.go index 8193600..9810537 100644 --- a/internal/lkgateway/lkclient.go +++ b/internal/lkgateway/lkclient.go @@ -9,6 +9,7 @@ package lkgateway import ( "context" "database/sql" + "git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql" "strings" "sync" "time" @@ -85,8 +86,7 @@ func spectreContractFor(ctx context.Context, s FancyMSSQLSettings, contract stri var c spectreContract var open, closed sql.NullTime var state sql.NullInt64 - err = conn.QueryRowContext(cctx, `SELECT TOP 1 DOC, INVESTOR, NAME, C_OPEN, C_CLOSE, C_STATE FROM D_B_CONTRACTS - WHERE UPPER(NAME) = @n OR UPPER(NAME) LIKE @n + ' %' ORDER BY C_STATE DESC, DOC DESC`, sql.Named("n", num)). + err = conn.QueryRowContext(cctx, fsql.Cl01, sql.Named("n", num)). Scan(&c.DOC, &c.Investor, &c.Name, &open, &closed, &state) if err == nil { c.Name = strings.TrimSpace(c.Name) diff --git a/internal/lkgateway/lkdocs.go b/internal/lkgateway/lkdocs.go index af0c548..409c0e9 100644 --- a/internal/lkgateway/lkdocs.go +++ b/internal/lkgateway/lkdocs.go @@ -11,6 +11,7 @@ import ( "context" "database/sql" "fmt" + "git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql" "strings" "time" ) @@ -78,11 +79,7 @@ func lkCorpActions(ctx context.Context, s FancyMSSQLSettings, isins []string) ([ names = append(names, fmt.Sprintf("@p%d", i+1)) args = append(args, sql.Named(fmt.Sprintf("p%d", i+1), strings.ToUpper(strings.TrimSpace(isin)))) } - q := fmt.Sprintf(`SELECT TOP 40 ISNULL(a.KD_TYPE,''), ISNULL(v.ISIN,''), ISNULL(v.NAME,''), a.FIX_DATE, a.OPER_DATE, ISNULL(a.OFFER_NAME,'') - FROM U_CA_ACTS a JOIN OD_VALUES v ON v.ID = a.SHARE - WHERE UPPER(v.ISIN) IN (%s) - AND ISNULL(a.FIX_DATE, a.OPER_DATE) >= DATEADD(month, -1, GETDATE()) - ORDER BY ISNULL(a.FIX_DATE, a.OPER_DATE)`, strings.Join(names, ",")) + q := fmt.Sprintf(fsql.Doc01, strings.Join(names, ",")) rows, err := conn.QueryContext(cctx, q, args...) if err != nil { return nil, err @@ -121,23 +118,7 @@ type LKDocument struct { } // lkDocSchema — таблица заявок на документы (создаётся вместе со схемой lk). -const lkDocSchema = ` -CREATE TABLE IF NOT EXISTS lk.doc_requests ( - id bigserial PRIMARY KEY, - created_at timestamptz NOT NULL DEFAULT now(), - contract_number text NOT NULL, - client_name text, - kind text NOT NULL, - period_from date, - period_to date, - account text, - comment text, - status text NOT NULL DEFAULT 'new', - staff_note text, - done_at timestamptz -); -CREATE INDEX IF NOT EXISTS ix_lk_doc_requests_contract ON lk.doc_requests (contract_number, created_at DESC); -` +var lkDocSchema = fsql.Doc02 // lkDocKinds — виды документов, которые клиент может заказать в кабинете. var lkDocKinds = []string{ @@ -173,8 +154,7 @@ func lkDocRequestAdd(ctx context.Context, rc *RuntimeConfig, contract, clientNam if t, err := time.Parse("2006-01-02", strings.TrimSpace(to)); err == nil { toP = t } - _, err = db.ExecContext(ctx, `INSERT INTO lk.doc_requests (contract_number, client_name, kind, period_from, period_to, account, comment) - VALUES ($1,$2,$3,$4,$5,$6,$7)`, contract, clientName, kind, fromP, toP, strings.TrimSpace(account), strings.TrimSpace(comment)) + _, err = db.ExecContext(ctx, fsql.Doc03, contract, clientName, kind, fromP, toP, strings.TrimSpace(account), strings.TrimSpace(comment)) return err } @@ -184,8 +164,7 @@ func lkDocRequests(ctx context.Context, rc *RuntimeConfig, contract string) ([]L if err != nil { return nil, err } - rows, err := db.QueryContext(ctx, `SELECT id, created_at, kind, period_from, period_to, COALESCE(account,''), status, COALESCE(staff_note,'') - FROM lk.doc_requests WHERE contract_number=$1 ORDER BY created_at DESC LIMIT 50`, contract) + rows, err := db.QueryContext(ctx, fsql.Doc04, contract) if err != nil { return nil, err } @@ -232,9 +211,7 @@ func lkReadyDocuments(ctx context.Context, s FancyMSSQLSettings, contract string defer conn.Close() cctx, cancel := context.WithTimeout(ctx, 25*time.Second) defer cancel() - q := fmt.Sprintf(`SELECT TOP 30 d.R_DATE, ISNULL(d.NUM,''), e.B_DATE, e.E_DATE, e.DEPO, d.STATE, ISNULL(e.IS_TURNS,0) - FROM U_OP_INF_EXTRACT e JOIN OD_DOCS d ON d.ID = e.DOC - WHERE e.DEPO IN (%s) ORDER BY d.R_DATE DESC`, inClause(ids)) + q := fmt.Sprintf(fsql.Doc05, inClause(ids)) rows, err := conn.QueryContext(cctx, q) if err != nil { return nil, err diff --git a/internal/lkgateway/lkflow.go b/internal/lkgateway/lkflow.go index 41b4d87..624c61c 100644 --- a/internal/lkgateway/lkflow.go +++ b/internal/lkgateway/lkflow.go @@ -16,6 +16,7 @@ import ( "database/sql" "encoding/json" "fmt" + "git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql" "log" "os" "sort" @@ -233,7 +234,7 @@ func lkDepoAnswer(ctx context.Context, rc *RuntimeConfig, guid, ourComment strin } defer db.Close() g := strings.ToUpper(guid) - rows, err := db.QueryContext(ctx, "SELECT ISNULL(STATE,''), ISNULL(COMMENT,'') FROM dbo.FTV_M2M_EVENTS WHERE GUID=@g AND EVENT_TYPE='BJ_GATEWAY_REJECT'", sql.Named("g", g)) + rows, err := db.QueryContext(ctx, fsql.QFl01, sql.Named("g", g)) if err != nil { return false, "", 0, "", err } @@ -270,9 +271,7 @@ func lkDepoAnswer(ctx context.Context, rc *RuntimeConfig, guid, ourComment strin docCol = "CAST(m.DOC AS varchar(32))" } var doc, mState, mComment, lineErr string - _ = db.QueryRowContext(ctx, `SELECT ISNULL(`+docCol+`,''), ISNULL(m.STATE,''), ISNULL(m.COMMENT,''), - ISNULL((SELECT TOP 1 ISNULL(l.COMMENT,'') FROM dbo.FTV_M2M_LINES l WHERE l.GUID=m.GUID AND ISNULL(l.STATE,'')='ERROR'),'') - FROM dbo.FTV_M2M m WHERE m.GUID=@g`, sql.Named("g", g)).Scan(&doc, &mState, &mComment, &lineErr) + _ = db.QueryRowContext(ctx, fsql.Flow01+docCol+fsql.Flow02, sql.Named("g", g)).Scan(&doc, &mState, &mComment, &lineErr) if strings.EqualFold(mState, "ERROR") && mComment != "" && !containsStr(errs, mComment) { errs = append(errs, mComment) } diff --git a/internal/lkgateway/lkfx.go b/internal/lkgateway/lkfx.go index f2e4f23..2484827 100644 --- a/internal/lkgateway/lkfx.go +++ b/internal/lkgateway/lkfx.go @@ -12,6 +12,7 @@ package lkgateway import ( "context" "database/sql" + "git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql" "strings" "sync" "time" @@ -75,10 +76,7 @@ func lkFxAccounting(ctx context.Context, s FancyMSSQLSettings) map[string]LKFxRa continue } cctx, cancel := context.WithTimeout(ctx, 15*time.Second) - rows, err := conn.QueryContext(cctx, `SELECT UPPER(LTRIM(RTRIM(v.SYSNAME))), r.OFICDATE, CAST(r.RATE AS float) - FROM OD_VALUES_RATES r JOIN OD_VALUES v ON v.ID = r.VALUE_ID - WHERE v.V_TYPE = 1 AND r.MARKET = 0 AND r.RATE > 0 - AND r.OFICDATE = (SELECT MAX(x.OFICDATE) FROM OD_VALUES_RATES x WHERE x.VALUE_ID = r.VALUE_ID AND x.MARKET = 0 AND x.RATE > 0)`) + rows, err := conn.QueryContext(cctx, fsql.Fx01) if err == nil { for rows.Next() { var code string diff --git a/internal/lkgateway/lkhistory.go b/internal/lkgateway/lkhistory.go index 5e751b0..8832d6f 100644 --- a/internal/lkgateway/lkhistory.go +++ b/internal/lkgateway/lkhistory.go @@ -13,6 +13,7 @@ import ( "context" "database/sql" "fmt" + "git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql" "strings" "time" ) @@ -54,9 +55,7 @@ func depoClientIDs(ctx context.Context, s FancyMSSQLSettings, contract string) ( defer conn.Close() cctx, cancel := context.WithTimeout(ctx, 20*time.Second) defer cancel() - rows, err := conn.QueryContext(cctx, `SELECT DISTINCT c.ID, ISNULL(c.NUMBER,''), ISNULL(c.FACE,0) - FROM OD_CLIENTS c JOIN OD_CLIENTS_PORTS p ON p.CLIENT = c.ID - WHERE p.PORT_NUM = @p1 OR p.PORT_NUM LIKE @p2`, contract, likeEscape(contract)+" %") + rows, err := conn.QueryContext(cctx, fsql.Hist01, contract, likeEscape(contract)+" %") if err != nil { return nil, 0, nil, err } @@ -137,32 +136,7 @@ func lkOperations(ctx context.Context, s FancyMSSQLSettings, contract, from, to } fromT, toT := parseRange(from, to) // Три вида операций в одном списке: зачисление, списание, перевод между счетами. - q := fmt.Sprintf(`SELECT TOP %d * FROM ( - SELECT d.R_DATE AS RD, d.PROC_DATE AS PD, ISNULL(d.NUM,'') AS NUM, d.STATE AS ST, N'зачисление' AS KIND, - z.DEPO AS ACC, ISNULL(v.ISIN,'') AS ISIN, ISNULL(v.NAME,'') AS SEC, CAST(ISNULL(z.AMOUNT,0) AS float) AS QTY, - LTRIM(RTRIM(ISNULL(z.COMMENT,'') + ' ' + ISNULL(z.REJECT_REASON,''))) AS NOTE - FROM U_OP_INV_ZACH z JOIN OD_DOCS d ON d.ID = z.DOC LEFT JOIN OD_VALUES v ON v.ID = z.SHARE - WHERE z.DEPO IN (%s) - UNION ALL - SELECT d.R_DATE, d.PROC_DATE, ISNULL(d.NUM,''), d.STATE, N'списание', - p.DEPO, ISNULL(v.ISIN,''), ISNULL(v.NAME,''), -CAST(ISNULL(p.AMOUNT,0) AS float), - LTRIM(RTRIM(ISNULL(p.COMMENT,'') + ' ' + ISNULL(p.REJECT_REASON,''))) - FROM U_OP_INV_SPIS p JOIN OD_DOCS d ON d.ID = p.DOC LEFT JOIN OD_VALUES v ON v.ID = p.SHARE - WHERE p.DEPO IN (%s) - UNION ALL - SELECT d.R_DATE, d.PROC_DATE, ISNULL(d.NUM,''), d.STATE, N'перевод: списание', - t.D_DEPO, ISNULL(v.ISIN,''), ISNULL(v.NAME,''), -CAST(ISNULL(t.AMOUNT,0) AS float), - LTRIM(RTRIM(ISNULL(t.COMMENT,'') + ' ' + ISNULL(t.REJECT_REASON,''))) - FROM U_OP_INV_PEREVOD t JOIN OD_DOCS d ON d.ID = t.DOC LEFT JOIN OD_VALUES v ON v.ID = t.SHARE - WHERE t.D_DEPO IN (%s) - UNION ALL - SELECT d.R_DATE, d.PROC_DATE, ISNULL(d.NUM,''), d.STATE, N'перевод: зачисление', - t.K_DEPO, ISNULL(v.ISIN,''), ISNULL(v.NAME,''), CAST(ISNULL(t.AMOUNT,0) AS float), - LTRIM(RTRIM(ISNULL(t.COMMENT,'') + ' ' + ISNULL(t.REJECT_REASON,''))) - FROM U_OP_INV_PEREVOD t JOIN OD_DOCS d ON d.ID = t.DOC LEFT JOIN OD_VALUES v ON v.ID = t.SHARE - WHERE t.K_DEPO IN (%s)) x - WHERE x.RD >= @p1 AND x.RD < DATEADD(day, 1, @p2) - ORDER BY x.RD DESC, x.NUM DESC`, limit, inClause(ids), inClause(ids), inClause(ids), inClause(ids)) + q := fmt.Sprintf(fsql.Hist02, limit, inClause(ids), inClause(ids), inClause(ids), inClause(ids)) rows, err := conn.QueryContext(cctx, q, fromT, toT) if err != nil { return nil, err @@ -226,17 +200,7 @@ func lkIncome(ctx context.Context, s FancyMSSQLSettings, contract, from, to stri cctx, cancel := context.WithTimeout(ctx, 30*time.Second) defer cancel() fromT, toT := parseRange(from, to) - q := fmt.Sprintf(`SELECT TOP 200 h.P_DATE, h.INCOME_KIND, ISNULL(v.ISIN,''), ISNULL(v.NAME,''), - CAST(ISNULL(l.AMOUNT,0) AS float), CAST(ISNULL(l.SUMMA_TOTAL,0) AS float), - CAST(ISNULL(l.TAX_HOLDED,0) AS float), CAST(ISNULL(l.TOTAL_TO_PAY,0) AS float), - ISNULL(l.STATUS,''), ISNULL(l.DEPO,0) - FROM U_OP_INF_INCOME_DOL l - JOIN OD_DOLS o ON o.ID = l.DOL - JOIN U_OP_INF_INCOME h ON h.DOC = o.DOC - LEFT JOIN OD_VALUES v ON v.ID = h.SHARE - WHERE (l.DEPO IN (%s) OR l.INCOME_FACE = @p3) - AND h.P_DATE >= @p1 AND h.P_DATE < DATEADD(day, 1, @p2) - ORDER BY h.P_DATE DESC`, inClause(ids)) + q := fmt.Sprintf(fsql.Hist03, inClause(ids)) rows, err := conn.QueryContext(cctx, q, fromT, toT, face) if err != nil { return nil, 0, err @@ -266,8 +230,7 @@ func lkIncome(ctx context.Context, s FancyMSSQLSettings, contract, from, to stri } // Удержанный налог по лицу за период — по журналу НДФЛ депозитария. if face > 0 { - _ = conn.QueryRowContext(cctx, `SELECT CAST(ISNULL(SUM(CAST(SUMMA AS float)),0) AS float) FROM OD_NDFL - WHERE FACE = @p1 AND R_DATE >= @p2 AND R_DATE < DATEADD(day, 1, @p3)`, + _ = conn.QueryRowContext(cctx, fsql.Hist04, sql.Named("p1", face), sql.Named("p2", fromT), sql.Named("p3", toT)).Scan(&taxTotal) } return rowsOut, taxTotal, nil diff --git a/internal/lkgateway/lkonline.go b/internal/lkgateway/lkonline.go index f510fda..7a9bbe7 100644 --- a/internal/lkgateway/lkonline.go +++ b/internal/lkgateway/lkonline.go @@ -10,6 +10,7 @@ package lkgateway import ( "context" "fmt" + "git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql" "sort" "strconv" "strings" @@ -88,14 +89,7 @@ func lkOnlinePortfolio(ctx context.Context, rc *RuntimeConfig, contract string) out := &LKPortfolio{Source: "quik"} // Позиции: свежая строка по каждому (клиент, бумага, торговый счёт), затем сумма по бумаге. - rows, err := p.Query(cctx, ` - WITH d AS ( - SELECT DISTINCT ON ("ClientCode","SecCode","Trdacc") "SecCode","CurrentBal","AwgPositionPrice","LoadDate" - FROM "DepoLimits" WHERE "ClientCode" = ANY($1) AND "LimitKind"='T2' - ORDER BY "ClientCode","SecCode","Trdacc","LoadDate" DESC) - SELECT "SecCode", SUM("CurrentBal")::float8, - COALESCE(SUM("CurrentBal"*"AwgPositionPrice")/NULLIF(SUM("CurrentBal"),0),0)::float8, MAX("LoadDate") - FROM d WHERE "CurrentBal" <> 0 GROUP BY "SecCode" ORDER BY "SecCode"`, codes) + rows, err := p.Query(cctx, fsql.On01, codes) if err != nil { return nil, fmt.Errorf("позиции: %v", err) } @@ -127,10 +121,7 @@ func lkOnlinePortfolio(ctx context.Context, rc *RuntimeConfig, contract string) for _, r := range pos { codesList = append(codesList, r.code) } - srows, err := p.Query(cctx, ` - SELECT DISTINCT ON ("SecCode") "SecCode", upper(COALESCE("ISINCode",'')), COALESCE("SecShortName","SecName",''), "ClassCode", COALESCE("FaceValue",0)::float8 - FROM "Securities" WHERE "SecCode" = ANY($1) - ORDER BY "SecCode", CASE "ClassCode" WHEN 'TQBR' THEN 1 WHEN 'TQOB' THEN 2 WHEN 'TQCB' THEN 3 WHEN 'TQTF' THEN 4 WHEN 'TQIF' THEN 5 WHEN 'TQRD' THEN 6 ELSE 9 END`, codesList) + srows, err := p.Query(cctx, fsql.On02, codesList) if err == nil { for srows.Next() { var code, isin, name, class string @@ -189,12 +180,7 @@ func lkOnlinePortfolio(ctx context.Context, rc *RuntimeConfig, contract string) } // Деньги по валютам. - crows, err := p.Query(cctx, ` - WITH m AS ( - SELECT DISTINCT ON ("ClientCode","CurrCode","Tag") "CurrCode","CurrentBal","LoadDate" - FROM "MoneyLimits" WHERE "ClientCode" = ANY($1) AND "LimitKind"='T2' - ORDER BY "ClientCode","CurrCode","Tag","LoadDate" DESC) - SELECT "CurrCode", SUM("CurrentBal")::float8, MAX("LoadDate") FROM m GROUP BY "CurrCode" HAVING SUM("CurrentBal") <> 0 ORDER BY "CurrCode"`, codes) + crows, err := p.Query(cctx, fsql.On03, codes) if err == nil { for crows.Next() { var c LKCash diff --git a/internal/lkgateway/lksnapshot.go b/internal/lkgateway/lksnapshot.go index 95dc576..093699d 100644 --- a/internal/lkgateway/lksnapshot.go +++ b/internal/lkgateway/lksnapshot.go @@ -11,18 +11,13 @@ package lkgateway import ( "context" + "git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql" "log" "strings" "time" ) -const lkSnapshotSchema = ` -CREATE TABLE IF NOT EXISTS lk.portfolio_daily ( - day DATE NOT NULL, contract VARCHAR(64) NOT NULL, - value DOUBLE PRECISION NOT NULL DEFAULT 0, cash DOUBLE PRECISION NOT NULL DEFAULT 0, - positions INT NOT NULL DEFAULT 0, priced INT NOT NULL DEFAULT 0, - source VARCHAR(16) NOT NULL DEFAULT 'quik', created_at TIMESTAMPTZ NOT NULL DEFAULT now(), - PRIMARY KEY (day, contract))` +var lkSnapshotSchema = fsql.Sn01 // lkSnapshotEnabled — ведём ли историю стоимости. По умолчанию (режим не выбран) // ведём, если настроен экспорт QUIK: история накапливается только вперёд, и @@ -76,8 +71,7 @@ func lkSnapshotHistory(ctx context.Context, rc *RuntimeConfig, contract string, } cctx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() - rows, err := db.QueryContext(cctx, `SELECT day, value, cash, positions FROM lk.portfolio_daily - WHERE contract = $1 AND day >= current_date - $2::int ORDER BY day`, num, days) + rows, err := db.QueryContext(cctx, fsql.Sn02, num, days) if err != nil { return nil, err } @@ -104,10 +98,7 @@ func lkSnapshotSave(ctx context.Context, rc *RuntimeConfig, day time.Time, contr } cctx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() - _, err = db.ExecContext(cctx, `INSERT INTO lk.portfolio_daily(day, contract, value, cash, positions, priced, source) - VALUES ($1,$2,$3,$4,$5,$6,$7) - ON CONFLICT (day, contract) DO UPDATE SET value = EXCLUDED.value, cash = EXCLUDED.cash, - positions = EXCLUDED.positions, priced = EXCLUDED.priced, source = EXCLUDED.source, created_at = now()`, + _, err = db.ExecContext(cctx, fsql.Sn03, day.Format("2006-01-02"), lkContractNumber(contract), p.Securities, p.CashRUB, len(p.Positions), p.Priced, p.Source) return err @@ -123,8 +114,7 @@ func lkSnapshotContracts(ctx context.Context, rc *RuntimeConfig) []string { } cctx, cancel := context.WithTimeout(ctx, 30*time.Second) defer cancel() - rows, err := p.Query(cctx, `SELECT DISTINCT "ClientCode" FROM "DepoLimits" - WHERE "LimitKind" = 'T2' AND "CurrentBal" <> 0 AND "ClientCode" <> ''`) + rows, err := p.Query(cctx, fsql.Sn04) if err != nil { return nil } @@ -218,7 +208,7 @@ func lkSnapshotDone(ctx context.Context, rc *RuntimeConfig, day time.Time) bool cctx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() var n int - if db.QueryRowContext(cctx, `SELECT count(*) FROM lk.portfolio_daily WHERE day = $1`, + if db.QueryRowContext(cctx, fsql.Sn05, day.Format("2006-01-02")).Scan(&n) != nil { return true } diff --git a/internal/lkgateway/lkupcoming.go b/internal/lkgateway/lkupcoming.go index 1b35e4f..26cbd33 100644 --- a/internal/lkgateway/lkupcoming.go +++ b/internal/lkgateway/lkupcoming.go @@ -9,6 +9,7 @@ import ( "context" "database/sql" "fmt" + "git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql" "sort" "strings" "time" @@ -75,9 +76,7 @@ func lkUpcoming(ctx context.Context, s FancyMSSQLSettings, positions []LKPositio } // Купоны: дата выплаты — P_DATE, до выплаты — конец периода. IS_PAY здесь означает // «купон выплачиваемый» (не «уже выплачен»), поэтому по нему не фильтруем. - rows, err := conn.QueryContext(cctx, fmt.Sprintf(`SELECT UPPER(v.ISIN), ISNULL(c.P_DATE, c.E_DATE), CAST(ISNULL(c.SUMMA,0) AS float), CAST(ISNULL(c.PERCENT_,0) AS float) - FROM OD_COUPONS c JOIN OD_VALUES v ON v.ID = c.SHARE - WHERE UPPER(v.ISIN) IN (%s) AND ISNULL(c.P_DATE, c.E_DATE) >= @f AND ISNULL(c.P_DATE, c.E_DATE) <= @t`, in), args...) + rows, err := conn.QueryContext(cctx, fmt.Sprintf(fsql.Up01, in), args...) if err == nil { for rows.Next() { var isin string @@ -90,9 +89,7 @@ func lkUpcoming(ctx context.Context, s FancyMSSQLSettings, positions []LKPositio rows.Close() } // Оферты. - rows, err = conn.QueryContext(cctx, fmt.Sprintf(`SELECT UPPER(v.ISIN), o.P_DATE, CAST(ISNULL(o.O_PRICE,0) AS float) - FROM OD_OFFERS o JOIN OD_VALUES v ON v.ID = o.SHARE - WHERE UPPER(v.ISIN) IN (%s) AND o.CANCEL_DATE IS NULL AND o.P_DATE >= @f AND o.P_DATE <= @t`, in), args...) + rows, err = conn.QueryContext(cctx, fmt.Sprintf(fsql.Up02, in), args...) if err == nil { for rows.Next() { var isin string @@ -105,9 +102,7 @@ func lkUpcoming(ctx context.Context, s FancyMSSQLSettings, positions []LKPositio rows.Close() } // Погашения: по номиналу в дату DEATHDATE. - rows, err = conn.QueryContext(cctx, fmt.Sprintf(`SELECT UPPER(v.ISIN), sh.DEATHDATE, CAST(ISNULL(sh.NOMINAL,0) AS float) - FROM OD_SHARES sh JOIN OD_VALUES v ON v.ID = sh.ID - WHERE UPPER(v.ISIN) IN (%s) AND sh.E_DATE > GETDATE() AND sh.DEATHDATE >= @f AND sh.DEATHDATE <= @t`, in), args...) + rows, err = conn.QueryContext(cctx, fmt.Sprintf(fsql.Up03, in), args...) if err == nil { for rows.Next() { var isin string @@ -120,9 +115,7 @@ func lkUpcoming(ctx context.Context, s FancyMSSQLSettings, positions []LKPositio rows.Close() } // Дивиденды (корпоративные действия DVCA): дата фиксации или выплаты, суммы нет. - rows, err = conn.QueryContext(cctx, fmt.Sprintf(`SELECT UPPER(v.ISIN), ISNULL(a.OPER_DATE, a.FIX_DATE) - FROM U_CA_ACTS a JOIN OD_VALUES v ON v.ID = a.SHARE - WHERE UPPER(v.ISIN) IN (%s) AND UPPER(ISNULL(a.KD_TYPE,'')) = 'DVCA' AND ISNULL(a.OPER_DATE, a.FIX_DATE) >= @f AND ISNULL(a.OPER_DATE, a.FIX_DATE) <= @t`, in), args...) + rows, err = conn.QueryContext(cctx, fmt.Sprintf(fsql.Up04, in), args...) if err == nil { for rows.Next() { var isin string diff --git a/internal/lkgateway/manual_depo.go b/internal/lkgateway/manual_depo.go index df5e077..a3cf583 100644 --- a/internal/lkgateway/manual_depo.go +++ b/internal/lkgateway/manual_depo.go @@ -7,6 +7,7 @@ import ( "database/sql" "encoding/json" "fmt" + "git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql" "net/http" "strconv" "strings" @@ -70,12 +71,7 @@ func manualDepoSearch(rc *RuntimeConfig, q string) ([]manualDepoFace, string) { defer db.Close() digits := strings.ReplaceAll(q, " ", "") // Действующая версия карточки (LAST_FLAG=1); счета привязаны к исходной записи (SELF_ID). - rows, err := db.QueryContext(ctx, `SELECT TOP 20 ISNULL(NULLIF(f.SELF_ID,0),f.ID), ISNULL(f.NAME,''), f.DATE_BIRTH, ISNULL(f.DOC_TYPE,0), ISNULL(f.DOC_SER,''), ISNULL(f.DOC_NUM,''), - f.DOC_DAT, ISNULL(f.DOC_PLACE,''), ISNULL(f.DOC_CODE,''), ISNULL(f.PHONE,'') - FROM OD_FACES f - WHERE ISNULL(f.LAST_FLAG,1)=1 AND (UPPER(f.NAME) LIKE UPPER(@q) OR f.DOC_NUM=@d OR REPLACE(ISNULL(f.DOC_SER,''),' ','')+f.DOC_NUM=@d) - AND EXISTS (SELECT 1 FROM OD_CLIENTS c WHERE c.FACE=ISNULL(NULLIF(f.SELF_ID,0),f.ID) AND c.NUMBER LIKE N'ВТНКЦ%' AND c.STATE=0 AND c.D_CLOSE IS NULL) - ORDER BY f.NAME`, sql.Named("q", q+"%"), sql.Named("d", digits)) + rows, err := db.QueryContext(ctx, fsql.Man01, sql.Named("q", q+"%"), sql.Named("d", digits)) if err != nil { return nil, "поиск в DEPO: " + err.Error() } @@ -119,9 +115,7 @@ func manualDepoSearch(rc *RuntimeConfig, q string) ([]manualDepoFace, string) { rows.Close() out := make([]manualDepoFace, 0, len(found)) for _, r := range found { - ar, err := db.QueryContext(ctx, `SELECT c.ID, ISNULL(c.NUMBER,''), - ISNULL((SELECT TOP 1 p.PORT_NUM FROM OD_CLIENTS_PORTS p WHERE p.CLIENT=c.ID ORDER BY p.PORT_DATE, p.PORT_NUM),'') - FROM OD_CLIENTS c WHERE c.FACE=@f AND c.NUMBER LIKE N'ВТНКЦ%' AND c.STATE=0 AND c.D_CLOSE IS NULL ORDER BY c.ID`, sql.Named("f", r.id)) + ar, err := db.QueryContext(ctx, fsql.Man02, sql.Named("f", r.id)) if err == nil { for ar.Next() { var a manualDepoAcc @@ -154,10 +148,7 @@ func manualDepoPositions(rc *RuntimeConfig, clientID int64) ([]manualDepoPos, st } defer cancel() defer db.Close() - rows, err := db.QueryContext(ctx, `SELECT ISNULL(v.ISIN,''), ISNULL(v.NAME,''), SUM(CAST(r.K AS float)) - FROM OD_RESTS r JOIN OD_VALUES v ON v.ID=r.VALUE_ID - WHERE r.REG_1=@c AND r.BAL_ACC=@b - GROUP BY v.ISIN, v.NAME HAVING SUM(CAST(r.K AS float)) > 0 ORDER BY v.NAME`, + rows, err := db.QueryContext(ctx, fsql.Man03, sql.Named("c", clientID), sql.Named("b", depoTradeBalAcc)) if err != nil { return nil, "остатки в DEPO: " + err.Error() @@ -210,7 +201,7 @@ func manualDepoClientByAccount(rc *RuntimeConfig, number string) (int64, string) defer cancel() defer db.Close() var id int64 - err := db.QueryRowContext(ctx, `SELECT TOP 1 ID FROM OD_CLIENTS WHERE NUMBER=@n AND STATE=0 AND D_CLOSE IS NULL ORDER BY ID`, + err := db.QueryRowContext(ctx, fsql.Man04, sql.Named("n", number)).Scan(&id) if err != nil { return 0, "" diff --git a/internal/lkgateway/prices.go b/internal/lkgateway/prices.go index d38c706..8d33e94 100644 --- a/internal/lkgateway/prices.go +++ b/internal/lkgateway/prices.go @@ -10,6 +10,7 @@ package lkgateway import ( "context" + "git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql" "strings" "sync" "time" @@ -88,17 +89,7 @@ func lkPrices(ctx context.Context, rc *RuntimeConfig, isins []string) map[string fxRates := lkFxRates(ctx, rc) // для выпусков с валютным номиналом // Одна цена на бумагу: берём класс основных торгов (акции TQBR, ОФЗ TQOB, // корпоративные облигации TQCB, фонды TQTF/TQIF), иначе — класс с торгами. - rows, err := p.Query(cctx, ` - SELECT DISTINCT ON (s."ISINCode") - upper(s."ISINCode"), s."ClassCode", COALESCE(s."SecShortName", s."SecName", ''), - COALESCE(p.last, 0)::float8, COALESCE(p.prevprice, 0)::float8, - COALESCE(s."FaceValue", 0)::float8, COALESCE(p.accruedint, 0)::float8, COALESCE(s."FaceUnit", '') - FROM "Securities" s JOIN "Params" p ON p."SecCode" = s."SecCode" AND p."ClassCode" = s."ClassCode" - WHERE s."ISINCode" <> '' AND (p.last > 0 OR p.prevprice > 0) - ORDER BY s."ISINCode", - CASE s."ClassCode" WHEN 'TQBR' THEN 1 WHEN 'TQOB' THEN 2 WHEN 'TQCB' THEN 3 - WHEN 'TQTF' THEN 4 WHEN 'TQIF' THEN 5 WHEN 'TQRD' THEN 6 ELSE 9 END, - p.last DESC`) + rows, err := p.Query(cctx, fsql.Pr01) if err != nil { return out } diff --git a/internal/lkgateway/quikcycle.go b/internal/lkgateway/quikcycle.go index a4ce533..1438f5c 100644 --- a/internal/lkgateway/quikcycle.go +++ b/internal/lkgateway/quikcycle.go @@ -15,6 +15,7 @@ import ( "encoding/json" "errors" "fmt" + "git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql" "log" "os" "path/filepath" @@ -214,8 +215,7 @@ func quikM2MInstruments(ctx context.Context, s QuikMNPSettings) (in, out string, defer conn.Close() cctx, cancel := context.WithTimeout(ctx, 15*time.Second) defer cancel() - rows, e := conn.QueryContext(cctx, `SELECT ic.ClassCode, sec.SecCode FROM ISecurities sec JOIN IClasses ic ON ic.ClassId=sec.ClassId - WHERE sec.Enable=1 AND ic.ClassCode IN ('INSTRINM2M','INSTROUTM2M') ORDER BY sec.SecurityId`) + rows, e := conn.QueryContext(cctx, fsql.Qk01) if e != nil { return "", "", fmt.Errorf("quik: справочник инструментов M2M: %s", quikErrorDetail(e)) } @@ -445,10 +445,7 @@ func quikResolveClient(ctx context.Context, rc *RuntimeConfig, ser, num string) // Правило депозитария: при нескольких договорах клиента зачисление идёт на // САМЫЙ РАННИЙ договор — берём портфель с наименьшей датой (затем по дате // открытия счёта и номеру). - rows, err := db.QueryContext(cctx, `SELECT TOP 5 p.PORT_NUM FROM OD_FACES f - JOIN OD_CLIENTS c ON c.FACE=f.ID JOIN OD_CLIENTS_PORTS p ON p.CLIENT=c.ID - WHERE REPLACE(f.DOC_SER,' ','')=@p1 AND f.DOC_NUM=@p2 AND c.NUMBER LIKE N'ВТНКЦ%' AND c.STATE=0 - ORDER BY ISNULL(p.PORT_DATE,'2099-01-01'), ISNULL(c.D_OPEN,'2099-01-01'), c.NUMBER`, serN, numN) + rows, err := db.QueryContext(cctx, fsql.Qk02, serN, numN) if err != nil { return "", "", fmt.Errorf("DEPO: %w", err) } diff --git a/internal/lkgateway/security_card.go b/internal/lkgateway/security_card.go index d1c17ee..9068674 100644 --- a/internal/lkgateway/security_card.go +++ b/internal/lkgateway/security_card.go @@ -8,6 +8,7 @@ import ( "context" "database/sql" "fmt" + "git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql" "strings" "time" ) @@ -86,7 +87,7 @@ func securityCard(ctx context.Context, rc *RuntimeConfig, isin string, pos *Clie var valID int64 var name, cfi string var vType, isIn sql.NullInt64 - err := db.QueryRowContext(dctx, `SELECT TOP 1 ID, ISNULL(NAME,''), ISNULL(CFI,''), V_TYPE, IS_IN FROM OD_VALUES WHERE ISIN=@p1 ORDER BY ID`, + err := db.QueryRowContext(dctx, fsql.Sec01, sql.Named("p1", isin)).Scan(&valID, &name, &cfi, &vType, &isIn) if err != nil { card.Note = "бумаги нет в справочнике депозитария" @@ -102,8 +103,7 @@ func securityCard(ctx context.Context, rc *RuntimeConfig, isin string, pos *Clie var nominal sql.NullFloat64 var regNum sql.NullString var death sql.NullTime - err = db.QueryRowContext(dctx, `SELECT TOP 1 ISSUER, CAST(NOMINAL AS float), NUM_REG, DEATHDATE FROM OD_SHARES - WHERE ID=@p1 AND E_DATE > GETDATE() ORDER BY B_DATE DESC`, sql.Named("p1", valID)).Scan(&issuer, &nominal, ®Num, &death) + err = db.QueryRowContext(dctx, fsql.Sec02, sql.Named("p1", valID)).Scan(&issuer, &nominal, ®Num, &death) if err == nil { card.Face = nominal.Float64 card.RegNumber = strings.TrimSpace(regNum.String) @@ -112,7 +112,7 @@ func securityCard(ctx context.Context, rc *RuntimeConfig, isin string, pos *Clie } if issuer.Valid && issuer.Int64 > 0 { var iname string - if db.QueryRowContext(dctx, `SELECT TOP 1 ISNULL(NAME,'') FROM OD_FACES WHERE ID=@p1 OR SELF_ID=@p1 ORDER BY ISNULL(LAST_FLAG,0) DESC, ID DESC`, + if db.QueryRowContext(dctx, fsql.Sec03, sql.Named("p1", issuer.Int64)).Scan(&iname) == nil { card.Issuer = strings.TrimSpace(iname) } @@ -122,10 +122,7 @@ func securityCard(ctx context.Context, rc *RuntimeConfig, isin string, pos *Clie // График: купоны за полгода назад и вперёд, ближайшие оферты, погашение. // Дата выплаты купона: P_DATE, а пока купон не выплачен — конец купонного периода. - rows, err := db.QueryContext(dctx, `SELECT TOP 12 ISNULL(P_DATE, E_DATE) AS PAY_DATE, CAST(ISNULL(SUMMA,0) AS float), - CAST(ISNULL(PERCENT_,0) AS float), FIX_DATE, ISNULL(IS_PAY,0) - FROM OD_COUPONS WHERE SHARE=@p1 AND ISNULL(P_DATE, E_DATE) >= DATEADD(month,-3,GETDATE()) - ORDER BY PAY_DATE`, sql.Named("p1", valID)) + rows, err := db.QueryContext(dctx, fsql.Sec04, sql.Named("p1", valID)) if err == nil { for rows.Next() { var pd, fx sql.NullTime @@ -143,8 +140,7 @@ func securityCard(ctx context.Context, rc *RuntimeConfig, isin string, pos *Clie } rows.Close() } - orows, err := db.QueryContext(dctx, `SELECT TOP 3 P_DATE, CAST(ISNULL(O_PRICE,0) AS float) FROM OD_OFFERS - WHERE SHARE=@p1 AND CANCEL_DATE IS NULL AND P_DATE >= GETDATE() ORDER BY P_DATE`, sql.Named("p1", valID)) + orows, err := db.QueryContext(dctx, fsql.Sec05, sql.Named("p1", valID)) if err == nil { for orows.Next() { var pd sql.NullTime diff --git a/scripts/build-release.sh b/scripts/build-release.sh index 8fbc76a..780e345 100755 --- a/scripts/build-release.sh +++ b/scripts/build-release.sh @@ -25,7 +25,24 @@ echo "Сборка релиза $VERSION (канал $CHANNEL) в $DIST" # справки (архитектура НРД, протокол FTV_M2M, схема базы, крипто, QUIK) в бинарь не # попадают (см. internal/lkgateway/internaldocs_off.go). Свою сборку со справкой # делаем отдельно: go build -tags internaldocs. Не добавляйте этот тег сюда. -go build -trimpath -ldflags "-s -w -X $MOD/internal/lkgateway.BuildVersion=$(ver BJ_SERVER_VERSION)" -o "$DIST/bj-server" ./cmd/bj-server +# bj-server несёт SQL-рецепты чтения DEPO/SPECTRE/QUIK (пакет internal/lkgateway/fsql). +# В релизе обфусцируем ТОЛЬКО этот пакет garble (-literals): строки запросов шифруются +# и не читаются `strings`; шаблоны и обработчики не затрагиваются. garble требует +# отдельный SDK go1.26 (модуль-кэшный тулчейн ему не подходит). +GARBLE=${GARBLE:-$(command -v garble || echo "$HOME/go/bin/garble")} +GO126_ROOT=${GO126_ROOT:-$HOME/sdk/go1.26.8} +if [ -x "$GARBLE" ] && [ -x "$GO126_ROOT/bin/go" ]; then + echo " bj-server: обфускация garble пакета fsql (SQL-рецепты)" + PATH="$GO126_ROOT/bin:$PATH" GOROOT="$GO126_ROOT" GOTOOLCHAIN=local \ + GOGARBLE="$MOD/internal/lkgateway/fsql" \ + "$GARBLE" -literals build -ldflags "-s -w -X $MOD/internal/lkgateway.BuildVersion=$(ver BJ_SERVER_VERSION)" -o "$DIST/bj-server" ./cmd/bj-server +else + echo " ВНИМАНИЕ: garble или go1.26 (~/sdk/go1.26.8) не найдены — bj-server собирается БЕЗ обфускации," + echo " SQL-рецепты DEPO/SPECTRE будут читаться strings. Установка обфускатора:" + echo " go install golang.org/dl/go1.26.8@latest && go1.26.8 download" + echo " go install mvdan.cc/garble@latest" + go build -trimpath -ldflags "-s -w -X $MOD/internal/lkgateway.BuildVersion=$(ver BJ_SERVER_VERSION)" -o "$DIST/bj-server" ./cmd/bj-server +fi CGO_ENABLED=0 go build -trimpath -ldflags "-s -w -X main.installerVersion=$(ver INSTALLER_VERSION)" -o "$DIST/bj-installer" ./cmd/bj-installer CGO_ENABLED=0 go build -trimpath -ldflags "-s -w -X main.version=$(ver COMPONENT_INSTALL_VERSION)" -o "$DIST/bj-component-install" ./cmd/bj-component-install CGO_ENABLED=0 go build -trimpath -ldflags "-s -w -X main.version=$(ver NPR_VERSION)" -o "$DIST/bj-npr" ./cmd/bj-npr