Files
knowledge-governance/cmd/worker/qa_mirror.go
T
姚章浩 6e67034fbd
build / build (push) Successful in 1m10s
feat(qa-ops): 交付十一项统计看板与完整图片导出
以治理库单一只读事务分开聚合累计、自然周、截止月及当前通讯录使用分母;浏览器不再读取全量长答案来构建看板。

新增迁移52与普通worker周质量冻结,保留真实冻结时点、规则和标量贡献;缺少当时快照留空。提交后冻结头与贡献不可修改、删除或追加;只读接口保留管理员、固定租户、严格JSON与输入冲突保护。

已验证:make verify退出0,686个Go用例和305个前端用例通过,必需跳过0;镜像与组织隔离PostgreSQL竞态回归退出0。真实治理副本浏览器核对第41周累计1885与新增471、十一项和完整PNG下载,390px无外溢、JS异常0;明确模拟503后不伪零且重试恢复。没有修改DobotAI原始问答、真实区域规则或生产发布门禁。
2026-10-18 23:26:55 +08:00

53 lines
1.8 KiB
Go

package main
import (
"context"
"github.com/jackc/pgx/v5/pgxpool"
"knowledge-governance/internal/dobotaiops"
"knowledge-governance/internal/jobs"
"knowledge-governance/internal/qaopsmirror"
"strings"
"time"
)
func configureRecordsSync(pool *pgxpool.Pool, getenv func(string) string, governanceDSN string, handlers map[string]jobs.Handler) (*qaopsmirror.Store, error) {
settings := dobotaiops.LoadSettings(getenv, governanceDSN)
if !settings.Enabled {
return nil, nil
}
if settings.TenantKey == "" || len(settings.TenantKey) > 128 || strings.ContainsRune(settings.TenantKey, 0) {
return nil, qaopsmirror.ErrInvalid
}
store := qaopsmirror.NewStore(pool, settings.TenantKey)
handlers[store.SyncJobType()] = func(ctx context.Context, job jobs.Job) error {
return store.HandleSync(ctx, job, func(ctx context.Context) (qaopsmirror.Source, error) {
current := dobotaiops.LoadSettings(getenv, governanceDSN)
if !current.Enabled || current.Problem != "" || current.TenantKey != settings.TenantKey {
return nil, dobotaiops.ErrUnavailable
}
// One source connection, shared by every manifest and bounded payload read in this job.
current.Connection.MaxConns = 1
current.Connection.MinConns = 0
return dobotaiops.Build(ctx, current.Connection)
})
}
return store, nil
}
// The next due time and automatic operation key live in PostgreSQL, so a worker
// restart does not reset the five-minute cadence or duplicate an active import.
func runRecordsSyncScheduler(ctx context.Context, store *qaopsmirror.Store, onError func(error)) {
tick := time.NewTicker(15 * time.Second)
defer tick.Stop()
for {
if e := runRecordsSyncCycle(ctx, store, time.Now()); e != nil && ctx.Err() == nil && onError != nil {
onError(e)
}
select {
case <-ctx.Done():
return
case <-tick.C:
}
}
}