6e67034fbd
build / build (push) Successful in 1m10s
以治理库单一只读事务分开聚合累计、自然周、截止月及当前通讯录使用分母;浏览器不再读取全量长答案来构建看板。 新增迁移52与普通worker周质量冻结,保留真实冻结时点、规则和标量贡献;缺少当时快照留空。提交后冻结头与贡献不可修改、删除或追加;只读接口保留管理员、固定租户、严格JSON与输入冲突保护。 已验证:make verify退出0,686个Go用例和305个前端用例通过,必需跳过0;镜像与组织隔离PostgreSQL竞态回归退出0。真实治理副本浏览器核对第41周累计1885与新增471、十一项和完整PNG下载,390px无外溢、JS异常0;明确模拟503后不伪零且重试恢复。没有修改DobotAI原始问答、真实区域规则或生产发布门禁。
76 lines
2.7 KiB
Go
76 lines
2.7 KiB
Go
package qaopsquality
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"github.com/jackc/pgx/v5"
|
|
"knowledge-governance/internal/dobotaiops"
|
|
)
|
|
|
|
// ReadViews projects quality already observed by mirror activation. It performs
|
|
// one SELECT, never inserts inputs/checks or advances an observation epoch.
|
|
type viewQuerier interface {
|
|
Query(context.Context, string, ...any) (pgx.Rows, error)
|
|
}
|
|
|
|
func (s *Store) ReadViews(ctx context.Context, keys []dobotaiops.QualityKey) (map[string]View, error) {
|
|
return s.readViews(ctx, s.pool, keys)
|
|
}
|
|
|
|
// ReadViewsInTx shares the caller's repeatable-read snapshot with mirror and organization metadata.
|
|
func (s *Store) ReadViewsInTx(ctx context.Context, tx pgx.Tx, keys []dobotaiops.QualityKey) (map[string]View, error) {
|
|
return s.readViews(ctx, tx, keys)
|
|
}
|
|
func (s *Store) readViews(ctx context.Context, q viewQuerier, keys []dobotaiops.QualityKey) (map[string]View, error) {
|
|
if !s.available() {
|
|
return nil, ErrUnavailable
|
|
}
|
|
if len(keys) > 5000 {
|
|
return nil, ErrInvalid
|
|
}
|
|
expected := map[string]string{}
|
|
ids := make([]string, 0, len(keys))
|
|
for _, k := range keys {
|
|
if !validID(k.ID) || !hashPattern.MatchString(k.InputHash) {
|
|
return nil, ErrInvalid
|
|
}
|
|
if old, exists := expected[k.ID]; exists {
|
|
if old != k.InputHash {
|
|
return nil, ErrInvalid
|
|
}
|
|
continue
|
|
}
|
|
expected[k.ID] = k.InputHash
|
|
ids = append(ids, k.ID)
|
|
}
|
|
out := map[string]View{}
|
|
if len(ids) == 0 {
|
|
return out, nil
|
|
}
|
|
rows, e := q.Query(ctx, `SELECT h.record_id,h.input_hash,h.input_epoch,h.review_revision,`+checkObject+`::text,COALESCE((SELECT `+reviewObject+` FROM qa_ops_quality_reviews r WHERE r.tenant_key=h.tenant_key AND r.record_id=h.record_id AND r.revision=h.review_revision),'null'::jsonb)::text FROM qa_ops_quality_records h JOIN qa_ops_quality_inputs i ON i.tenant_key=h.tenant_key AND i.record_id=h.record_id AND i.input_epoch=h.input_epoch JOIN qa_ops_quality_checks c ON c.tenant_key=h.tenant_key AND c.record_id=h.record_id AND c.input_epoch=h.input_epoch AND c.rule_version=$3 WHERE h.tenant_key=$1 AND h.record_id=ANY($2::text[])`, s.tenant, ids, dobotaiops.CoverageRuleVersion)
|
|
if e != nil {
|
|
return nil, ErrUnavailable
|
|
}
|
|
defer rows.Close()
|
|
for rows.Next() {
|
|
var id, hash, checkRaw, reviewRaw string
|
|
var epoch, revision int64
|
|
if rows.Scan(&id, &hash, &epoch, &revision, &checkRaw, &reviewRaw) != nil {
|
|
return nil, ErrUnavailable
|
|
}
|
|
if expected[id] != hash {
|
|
return nil, ErrConflict
|
|
}
|
|
var automatic Check
|
|
var review *Review
|
|
if json.Unmarshal([]byte(checkRaw), &automatic) != nil || json.Unmarshal([]byte(reviewRaw), &review) != nil {
|
|
return nil, ErrUnavailable
|
|
}
|
|
out[id] = Project(hash, epoch, revision, automatic, review)
|
|
}
|
|
if rows.Err() != nil || len(out) != len(ids) {
|
|
return nil, ErrUnavailable
|
|
}
|
|
return out, nil
|
|
}
|