DEPO: процедуры ставятся в мастере «Подготовить» + правка под клиента
- «Подготовить» в мастере (step 3, режим «дать логин администратора») теперь ставит и процедуры обработки bj2depo (CREATE OR ALTER) + GRANT, не только логин/таблицы/джобу. - Скачиваемый скрипт мастера включает процедуры. - Тексты процедур редактируются под клиента (Настройка → Обмен DEPO → Процедуры): сохранённый вариант используется при установке и при «Подготовить». Пусто/совпадение со встроенным — исходная версия. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q7eREaYwUt1i4fNLZcGnjn
This commit is contained in:
@@ -43,7 +43,7 @@ func splitSQLBatches(s string) []string {
|
||||
return out
|
||||
}
|
||||
|
||||
// depoProcFile — тело встроенной процедуры.
|
||||
// depoProcFile — тело встроенной (исходной) процедуры из файла.
|
||||
func depoProcFile(file string) (string, error) {
|
||||
b, err := depoProcFS.ReadFile(file)
|
||||
if err != nil {
|
||||
@@ -52,25 +52,83 @@ func depoProcFile(file string) (string, error) {
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
// embeddedProcBody — встроенный текст процедуры по имени.
|
||||
func embeddedProcBody(name string) string {
|
||||
for _, p := range depoProcInstallOrder {
|
||||
if p.Name == name {
|
||||
b, _ := depoProcFile(p.File)
|
||||
return b
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// depoProcEffective — текст процедуры с учётом правки под клиента: сохранённый
|
||||
// в настройках вариант, иначе встроенный. Используется в предпросмотре, установке
|
||||
// и скачиваемом скрипте.
|
||||
func depoProcEffective(f FancyMSSQLSettings, name string) string {
|
||||
if v, ok := f.DepoProcOverrides[name]; ok && strings.TrimSpace(v) != "" {
|
||||
return v
|
||||
}
|
||||
return embeddedProcBody(name)
|
||||
}
|
||||
|
||||
// depoProcList — порядок процедур для шаблона (редактор текстов).
|
||||
func depoProcList() []struct{ Name, File string } { return depoProcInstallOrder }
|
||||
|
||||
// depoProcsCombined — единый скрипт установки (для скачивания администратору): USE, процедуры, GRANT.
|
||||
func depoProcsCombined(f FancyMSSQLSettings) string {
|
||||
return depoProcsScriptFor(f, f.DepoDB(), depoSetupLogin(f))
|
||||
}
|
||||
|
||||
// depoProcsScriptFor — скрипт установки процедур в базу db с выдачей EXECUTE логину login.
|
||||
// Тексты процедур — с учётом правки под клиента (depoProcEffective).
|
||||
func depoProcsScriptFor(f FancyMSSQLSettings, db, login string) string {
|
||||
var b strings.Builder
|
||||
login := depoSetupLogin(f)
|
||||
b.WriteString("/* Bridge&Join: установка процедур обработки bj2depo в базу DEPO.\n")
|
||||
b.WriteString("/* Bridge&Join: процедуры обработки bj2depo для базы DEPO.\n")
|
||||
b.WriteString(" Порядок WIRING -> OPERATION -> EVENTS -> CREATE -> JOB. Запуск под sysadmin в SSMS.\n")
|
||||
b.WriteString(" CREATE OR ALTER — повторный запуск безопасен (данные не меняются). */\n")
|
||||
fmt.Fprintf(&b, "USE %s;\nGO\n\n", quoteIdent(f.DepoDB()))
|
||||
fmt.Fprintf(&b, "USE %s;\nGO\n\n", quoteIdent(db))
|
||||
for _, p := range depoProcInstallOrder {
|
||||
body, err := depoProcFile(p.File)
|
||||
if err != nil {
|
||||
body := depoProcEffective(f, p.Name)
|
||||
if strings.TrimSpace(body) == "" {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(&b, "/* ===== %s ===== */\n%s\nGO\n\n", p.Name, strings.TrimRight(body, "\n"))
|
||||
}
|
||||
fmt.Fprintf(&b, "/* Право запуска задания логину bj */\nGRANT EXECUTE ON dbo.FTV_M2M_JOB TO %s;\nGO\n", quoteIdent(login))
|
||||
if depoLoginRe.MatchString(login) {
|
||||
fmt.Fprintf(&b, "/* Право запуска задания логину bj */\nGRANT EXECUTE ON dbo.FTV_M2M_JOB TO %s;\nGO\n", quoteIdent(login))
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// depoProcsInstallInto — установить процедуры (CREATE OR ALTER, с учётом правки под клиента) в базу
|
||||
// dbName соединением adm (логин администратора) и выдать EXECUTE логину grantLogin.
|
||||
func depoProcsInstallInto(ctx context.Context, adm, f FancyMSSQLSettings, dbName, grantLogin string) error {
|
||||
db, err := fancyOpen(adm, dbName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer db.Close()
|
||||
for _, p := range depoProcInstallOrder {
|
||||
body := depoProcEffective(f, p.Name)
|
||||
if strings.TrimSpace(body) == "" {
|
||||
return fmt.Errorf("пустой текст процедуры %s", p.Name)
|
||||
}
|
||||
for _, batch := range splitSQLBatches(body) {
|
||||
if _, err := db.ExecContext(ctx, batch); err != nil {
|
||||
return fmt.Errorf("установка %s: %s", p.Name, quikErrorDetail(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
if depoLoginRe.MatchString(grantLogin) {
|
||||
if _, err := db.ExecContext(ctx, "GRANT EXECUTE ON dbo.FTV_M2M_JOB TO "+quoteIdent(grantLogin)); err != nil {
|
||||
return fmt.Errorf("право EXECUTE логину %s: %s", grantLogin, quikErrorDetail(err))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// quoteIdent — квадратные скобки для идентификатора T-SQL.
|
||||
func quoteIdent(s string) string { return "[" + strings.ReplaceAll(s, "]", "]]") + "]" }
|
||||
|
||||
@@ -137,35 +195,15 @@ func depoProcsInstall(ctx context.Context, f FancyMSSQLSettings, adminUser, admi
|
||||
if !canApply {
|
||||
return "", fmt.Errorf("нет прав на установку. %s", report)
|
||||
}
|
||||
conn := f
|
||||
adm := f
|
||||
if u := strings.TrimSpace(adminUser); u != "" {
|
||||
conn.User, conn.Password = u, adminPass
|
||||
adm.User, adm.Password = u, adminPass
|
||||
}
|
||||
db, err := fancyOpen(conn, f.DepoDB())
|
||||
if err != nil {
|
||||
login := depoSetupLogin(f)
|
||||
if err := depoProcsInstallInto(ctx, adm, f, f.DepoDB(), login); err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer db.Close()
|
||||
for _, p := range depoProcInstallOrder {
|
||||
body, err := depoProcFile(p.File)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("встроенный файл %s: %w", p.File, err)
|
||||
}
|
||||
for _, batch := range splitSQLBatches(body) {
|
||||
if _, err := db.ExecContext(ctx, batch); err != nil {
|
||||
return "", fmt.Errorf("установка %s: %s", p.Name, quikErrorDetail(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
msg := "установлено процедур: 5 (WIRING, OPERATION, EVENTS, CREATE, JOB)"
|
||||
login := depoSetupLogin(f)
|
||||
if depoLoginRe.MatchString(login) {
|
||||
if _, err := db.ExecContext(ctx, "GRANT EXECUTE ON dbo.FTV_M2M_JOB TO "+quoteIdent(login)); err != nil {
|
||||
msg += "; право EXECUTE логину " + login + " не выдано: " + quikErrorDetail(err)
|
||||
} else {
|
||||
msg += "; право EXECUTE логину " + login + " выдано"
|
||||
}
|
||||
}
|
||||
msg := "установлено процедур: 5 (WIRING, OPERATION, EVENTS, CREATE, JOB); право EXECUTE логину " + login + " выдано"
|
||||
log.Printf("depo-procs: %s", msg)
|
||||
return msg, nil
|
||||
}
|
||||
@@ -237,3 +275,35 @@ func (h *setupHandlers) fancyDepoProcsScript(w http.ResponseWriter, r *http.Requ
|
||||
// UTF-8 BOM (0xEF 0xBB 0xBF): SSMS без него читает кириллицу в комментариях как ANSI.
|
||||
_, _ = w.Write(append([]byte{0xEF, 0xBB, 0xBF}, depoProcsCombined(s)...))
|
||||
}
|
||||
|
||||
// saveFancyDepoProcsText — POST /admin/setup/fancy/depo-procs/save-text: сохранить правку текстов процедур
|
||||
// под клиента. Пустое поле или совпадение со встроенным текстом — вернуть встроенную версию (убрать правку).
|
||||
func (h *setupHandlers) saveFancyDepoProcsText(w http.ResponseWriter, r *http.Request) {
|
||||
if !requireDepo(w, r) {
|
||||
return
|
||||
}
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "method", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
s := h.rc.Snapshot().Fancy
|
||||
ov := map[string]string{}
|
||||
changed := 0
|
||||
for _, p := range depoProcInstallOrder {
|
||||
v := strings.ReplaceAll(r.FormValue("proc_"+p.Name), "\r\n", "\n")
|
||||
if strings.TrimSpace(v) != "" && strings.TrimSpace(v) != strings.TrimSpace(embeddedProcBody(p.Name)) {
|
||||
ov[p.Name] = v
|
||||
changed++
|
||||
}
|
||||
}
|
||||
if changed == 0 {
|
||||
s.DepoProcOverrides = nil
|
||||
} else {
|
||||
s.DepoProcOverrides = ov
|
||||
}
|
||||
if err := h.rc.UpdateFancy(s); err != nil {
|
||||
setupFlash(w, r, "Тексты процедур: ошибка сохранения — "+err.Error())
|
||||
return
|
||||
}
|
||||
setupFlash(w, r, fmt.Sprintf("Тексты процедур сохранены. Изменённых под клиента: %d (пустые или совпадающие со встроенными — используется встроенная версия).", changed))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user