Files
knowledge-governance/internal/platform/postgres/ui_settings.go
T
zheromiku dcdc8d1ddd feat: 知识治理平台(含已验证发布/回滚契约)
全栈知识治理平台首次入库(P1-P10 + 发布链路模拟验证闭环):

- 治理核心:源目录/变更集/人工审核/发布批次/发布执行(fail-closed 闸口)
- 测试通道:WeKnora 测试 KB 上传/轮询/验收;poll 完成时写入
  parser_generation/index_generation,修复 manual/seed 路径发布闸口永久拦截
- 发布适配器 v071(本地 source=测试角色 / 云端 target=生产角色 模拟验证通过):
  * ES 导出/导入 mapping 自适应(.keyword 子字段、可选 tenant_id)
  * OSS locator 支持嵌套 scheme;chunks.seq_id 由目标序列分配
  * bundle 归档同身份重试原子替换;verifier 按 bundle chunk_type 计数
  * 回滚 job payload 支持 prior_execution_id(strict 解码)
- 三通道发布+回滚演练实测:publish 4/4 verified,检索命中,
  rollback 4/4 rolled_back,目标端 PG is_enabled=false、ES is_enabled=false
- 前端 A·Data-console 风格全站统一,120 用例通过
- 门禁:go vet / go test 全包通过,gofmt 干净
2026-09-30 10:54:06 +08:00

135 lines
4.4 KiB
Go

package postgres
import (
"context"
"errors"
"fmt"
"time"
"github.com/jackc/pgx/v5"
"knowledge-governance/internal/governance"
)
var ErrInvalidUISettings = errors.New("invalid UI settings")
const uiSettingsColumns = `id,state,document_refresh_seconds,resource_refresh_seconds,stalled_after_seconds,finalizing_after_seconds,updated_by,created_at,activated_at,retired_at,error_code`
const getActiveUISettingsSQL = `
SELECT ` + uiSettingsColumns + `
FROM ui_setting_revisions
WHERE state = 'active'
`
const createUISettingsCandidateSQL = `
INSERT INTO ui_setting_revisions(
id,state,document_refresh_seconds,resource_refresh_seconds,
stalled_after_seconds,finalizing_after_seconds,updated_by,created_at
) VALUES($1,'validated',$2,$3,$4,$5,$6,$7)
RETURNING ` + uiSettingsColumns + `
`
const lockUISettingsRevisionSQL = `
SELECT ` + uiSettingsColumns + `
FROM ui_setting_revisions
WHERE id=$1
FOR UPDATE
`
const retireActiveUISettingsSQL = `
UPDATE ui_setting_revisions
SET state='retired',retired_at=$2
WHERE state='active' AND id<>$1
`
const activateUISettingsSQL = `
UPDATE ui_setting_revisions
SET state='active',activated_at=$2,retired_at=NULL,error_code=''
WHERE id=$1 AND state='validated'
RETURNING ` + uiSettingsColumns + `
`
func (s *Store) GetActiveUISettings(ctx context.Context) (governance.UISettings, error) {
return scanUISettings(s.db.QueryRow(ctx, getActiveUISettingsSQL))
}
func (s *Store) CreateUISettingsCandidate(ctx context.Context, request governance.CreateUISettingsCandidateRequest) (governance.UISettings, error) {
if !request.Valid() {
return governance.UISettings{}, ErrInvalidUISettings
}
id, err := newOpaqueID("uis-")
if err != nil {
return governance.UISettings{}, fmt.Errorf("create UI settings id: %w", err)
}
return scanUISettings(s.db.QueryRow(ctx, createUISettingsCandidateSQL,
id, request.DocumentRefreshSeconds, request.ResourceRefreshSeconds,
request.StalledAfterSeconds, request.FinalizingAfterSeconds, request.UpdatedBy, request.Now.UTC()))
}
func (s *Store) ActivateUISettings(ctx context.Context, revisionID string, now time.Time) (governance.UISettings, error) {
if !validShortID(revisionID) || now.IsZero() {
return governance.UISettings{}, ErrInvalidUISettings
}
var tx pgx.Tx
var err error
ownsTransaction := false
if existing, ok := s.db.(pgx.Tx); ok {
tx = existing
} else {
beginner, ok := s.db.(interface {
Begin(context.Context) (pgx.Tx, error)
})
if !ok {
return governance.UISettings{}, errors.New("store does not support transactions")
}
tx, err = beginner.Begin(ctx)
if err != nil {
return governance.UISettings{}, fmt.Errorf("begin UI settings activation: %w", err)
}
ownsTransaction = true
defer func() { _ = tx.Rollback(ctx) }()
}
candidate, err := scanUISettings(tx.QueryRow(ctx, lockUISettingsRevisionSQL, revisionID))
if err != nil {
return governance.UISettings{}, err
}
if candidate.State == "active" {
if ownsTransaction {
if err := tx.Commit(ctx); err != nil {
return governance.UISettings{}, fmt.Errorf("commit UI settings activation: %w", err)
}
}
return candidate, nil
}
if candidate.State != "validated" {
return governance.UISettings{}, ErrInvalidUISettings
}
activatedAt := now.UTC()
if _, err := tx.Exec(ctx, retireActiveUISettingsSQL, revisionID, activatedAt); err != nil {
return governance.UISettings{}, fmt.Errorf("retire active UI settings: %w", err)
}
active, err := scanUISettings(tx.QueryRow(ctx, activateUISettingsSQL, revisionID, activatedAt))
if err != nil {
return governance.UISettings{}, err
}
if ownsTransaction {
if err := tx.Commit(ctx); err != nil {
return governance.UISettings{}, fmt.Errorf("commit UI settings activation: %w", err)
}
}
return active, nil
}
func scanUISettings(row interface{ Scan(...any) error }) (governance.UISettings, error) {
var settings governance.UISettings
if err := row.Scan(&settings.ID, &settings.State, &settings.DocumentRefreshSeconds,
&settings.ResourceRefreshSeconds, &settings.StalledAfterSeconds, &settings.FinalizingAfterSeconds,
&settings.UpdatedBy, &settings.CreatedAt, &settings.ActivatedAt, &settings.RetiredAt, &settings.ErrorCode); err != nil {
return governance.UISettings{}, fmt.Errorf("scan UI settings: %w", err)
}
if settings.State != "validated" && settings.State != "active" && settings.State != "retired" && settings.State != "failed" {
return governance.UISettings{}, errors.New("invalid UI settings state")
}
return settings, nil
}