Files
knowledge-governance/internal/qaopsquality/master_fields.go
T
姚章浩 09b2c1dce5
build / build (push) Successful in 1m17s
fix(qa): 表头筛选并统一未覆盖正确性待确认
修正筛选入口位置:五项条件在表头就地选择,保持完整集筛选与无感续载。
统一固定规则:未覆盖一律待确认,原问答/人工模型证据不改写,已覆盖仍保留点踩优先。
验证:行为红绿、前端/Go全套及隔离PostgreSQL全mirror竞态通过;完整本地门禁与原生备份真实恢复通过。
2026-10-25 13:58:08 +08:00

175 lines
8.2 KiB
Go

package qaopsquality
import (
"context"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"errors"
"github.com/jackc/pgx/v5"
"knowledge-governance/internal/dobotaiops"
"knowledge-governance/internal/qaopsfields"
"regexp"
"strings"
"time"
"unicode/utf8"
)
type MasterFieldPatch struct {
InputHash string `json:"input_hash"`
InputEpoch int64 `json:"input_epoch"`
BaseRevision int64 `json:"base_revision"`
BaseReviewRevision int64 `json:"base_review_revision"`
Nonce string `json:"nonce"`
Fields map[string]string `json:"fields"`
}
type MasterFieldReceipt struct {
SavedRevision int64 `json:"saved_revision"`
Replayed bool `json:"replayed"`
}
var inlineHash = regexp.MustCompile(`^[a-f0-9]{64}$`)
var MasterFieldNoncePattern = regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9_.:-]{15,127}$`)
func ValidMasterFieldID(value string) bool {
return value != "" && len(value) <= 256 && utf8.ValidString(value) && !strings.ContainsAny(value, "\x00\x1f")
}
func (s *Store) PatchMasterFields(ctx context.Context, id string, q MasterFieldPatch, actor string) (MasterFieldReceipt, error) {
if s == nil || s.pool == nil {
return MasterFieldReceipt{}, dobotaiops.ErrUnavailable
}
ctx, cancel := context.WithTimeout(ctx, 8*time.Second)
defer cancel()
tx, err := s.pool.Begin(ctx)
if err != nil {
return MasterFieldReceipt{}, dobotaiops.ErrUnavailable
}
defer tx.Rollback(context.Background())
receipt, err := s.PatchMasterFieldsInTx(ctx, tx, id, q, actor)
if err != nil {
return MasterFieldReceipt{}, err
}
if tx.Commit(ctx) != nil {
return MasterFieldReceipt{}, dobotaiops.ErrUnavailable
}
return receipt, nil
}
func (s *Store) PatchMasterFieldsInTx(ctx context.Context, tx pgx.Tx, id string, q MasterFieldPatch, actor string) (MasterFieldReceipt, error) {
if s == nil || s.pool == nil || !ValidMasterFieldID(s.tenant) {
return MasterFieldReceipt{}, dobotaiops.ErrUnavailable
}
if !ValidMasterFieldID(id) || !ValidMasterFieldID(actor) || !inlineHash.MatchString(q.InputHash) || !MasterFieldNoncePattern.MatchString(q.Nonce) || q.InputEpoch < 1 || q.BaseRevision < 0 || q.BaseReviewRevision < 0 || len(q.Fields) < 1 || len(q.Fields) > 5 {
return MasterFieldReceipt{}, dobotaiops.ErrInvalidQuery
}
for field, value := range q.Fields {
if !qaopsfields.ValidEditableValue(field, value) {
return MasterFieldReceipt{}, dobotaiops.ErrInvalidQuery
}
}
var err error
if _, err = tx.Exec(ctx, `SELECT pg_advisory_xact_lock(hashtextextended($1||chr(31)||$2,258))`, s.tenant, id); err != nil {
return MasterFieldReceipt{}, dobotaiops.ErrUnavailable
}
payload, _ := json.Marshal(q)
sum := sha256.Sum256(payload)
requestHash := hex.EncodeToString(sum[:])
var savedRevision int64
var savedHash, savedActor string
err = tx.QueryRow(ctx, `SELECT revision,request_hash,actor FROM qa_ops_master_field_edits WHERE tenant_key=$1 AND record_id=$2 AND nonce=$3`, s.tenant, id, q.Nonce).Scan(&savedRevision, &savedHash, &savedActor)
if err == nil {
if savedHash != requestHash || savedActor != actor {
return MasterFieldReceipt{}, ErrConflict
}
return MasterFieldReceipt{SavedRevision: savedRevision, Replayed: true}, nil
}
if !errors.Is(err, pgx.ErrNoRows) {
return MasterFieldReceipt{}, dobotaiops.ErrUnavailable
}
var hash string
var reviewRevision, epoch int64
err = tx.QueryRow(ctx, `SELECT input_hash,review_revision,input_epoch FROM qa_ops_quality_records WHERE tenant_key=$1 AND record_id=$2 FOR UPDATE`, s.tenant+"::master", id).Scan(&hash, &reviewRevision, &epoch)
if errors.Is(err, pgx.ErrNoRows) {
return MasterFieldReceipt{}, dobotaiops.ErrRecordNotFound
}
if err != nil {
return MasterFieldReceipt{}, dobotaiops.ErrUnavailable
}
if hash != q.InputHash || epoch != q.InputEpoch || reviewRevision != q.BaseReviewRevision {
return MasterFieldReceipt{}, ErrConflict
}
var currentRevision, previousReview int64
var previousHash string
var raw []byte
overrides := map[string]string{}
err = tx.QueryRow(ctx, `SELECT revision,input_hash,review_revision,fields FROM qa_ops_master_field_overrides WHERE tenant_key=$1 AND record_id=$2`, s.tenant, id).Scan(&currentRevision, &previousHash, &previousReview, &raw)
if err != nil && !errors.Is(err, pgx.ErrNoRows) {
return MasterFieldReceipt{}, dobotaiops.ErrUnavailable
}
if currentRevision != q.BaseRevision {
return MasterFieldReceipt{}, ErrConflict
}
if previousHash == hash && previousReview == reviewRevision && json.Unmarshal(raw, &overrides) != nil {
return MasterFieldReceipt{}, dobotaiops.ErrUnavailable
}
var masterHash string
var fieldsRaw, coverageRaw, editsRaw, feedbackRaw []byte
err = tx.QueryRow(ctx, `SELECT quality_hash,effective_fields,coverage_result,inline_fields,current_feedback FROM qa_ops_master_read_model WHERE tenant_key=$1 AND id=$2`, s.tenant, id).Scan(&masterHash, &fieldsRaw, &coverageRaw, &editsRaw, &feedbackRaw)
if err != nil {
return MasterFieldReceipt{}, dobotaiops.ErrUnavailable
}
if masterHash != q.InputHash {
return MasterFieldReceipt{}, ErrConflict
}
var fields, edits map[string]string
var coverage dobotaiops.CoverageCheck
var feedback dobotaiops.RecordFeedback
if json.Unmarshal(fieldsRaw, &fields) != nil || json.Unmarshal(editsRaw, &edits) != nil || json.Unmarshal(coverageRaw, &coverage) != nil || json.Unmarshal(feedbackRaw, &feedback) != nil {
return MasterFieldReceipt{}, dobotaiops.ErrUnavailable
}
views, err := NewStore(s.pool, s.tenant+"::master").ReadViewsInTx(ctx, tx, []dobotaiops.QualityKey{{ID: id, InputHash: masterHash}})
if err != nil {
return MasterFieldReceipt{}, err
}
current := ProjectMasterWithEdits(views[id], fields, coverage, edits, feedback).Effective
beforeAll := map[string]string{"coverage": map[string]string{"covered": "是", "uncovered": "否", "partial": "部分覆盖", "pending": "待确认", "excluded": "否但不计入"}[current.Coverage], "is_correct": map[string]string{"correct": "是", "incorrect": "否", "pending": "待确认"}[current.Correctness], "product_category": current.ProductCategory, "problem_type": current.ProblemType, "root_cause": current.RootCause}
correctness := beforeAll["is_correct"]
if value, ok := q.Fields["is_correct"]; ok {
correctness = value
}
coverageValue := beforeAll["coverage"]
if value, changed := q.Fields["coverage"]; changed {
coverageValue = value
}
_, correctnessChanged := q.Fields["is_correct"]
if correctnessChanged && ((correctness == "待确认") != (coverageValue == "否")) {
return MasterFieldReceipt{}, dobotaiops.ErrInvalidQuery
}
if correctnessChanged && coverageValue != "否" && correctness != "否" && FeedbackForcesIncorrect(feedback) {
return MasterFieldReceipt{}, ErrDownvoted
}
if correctness == "是" && q.Fields["root_cause"] != "" {
return MasterFieldReceipt{}, dobotaiops.ErrInvalidQuery
}
before := map[string]string{}
for key, value := range q.Fields {
before[key] = beforeAll[key]
overrides[key] = value
}
encoded, _ := json.Marshal(overrides)
changed, _ := json.Marshal(q.Fields)
previous, _ := json.Marshal(before)
revision := currentRevision + 1
if _, err = tx.Exec(ctx, `INSERT INTO qa_ops_master_field_overrides(tenant_key,record_id,revision,input_hash,review_revision,fields) VALUES($1,$2,$3,$4,$5,$6) ON CONFLICT(tenant_key,record_id) DO UPDATE SET revision=EXCLUDED.revision,input_hash=EXCLUDED.input_hash,review_revision=EXCLUDED.review_revision,fields=EXCLUDED.fields,updated_at=now()`, s.tenant, id, revision, hash, reviewRevision, encoded); err != nil {
return MasterFieldReceipt{}, dobotaiops.ErrUnavailable
}
if _, err = tx.Exec(ctx, `INSERT INTO qa_ops_master_field_edits(tenant_key,record_id,revision,input_hash,review_revision,changed_fields,before_fields,fields,actor,nonce,request_hash) VALUES($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11)`, s.tenant, id, revision, hash, reviewRevision, changed, previous, encoded, actor, q.Nonce, requestHash); err != nil {
return MasterFieldReceipt{}, dobotaiops.ErrUnavailable
}
tag, err := tx.Exec(ctx, `UPDATE qa_ops_master_records SET row_revision=row_revision+1 WHERE tenant_key=$1 AND id=$2 AND quality_hash=$3`, s.tenant, id, hash)
if err != nil || tag.RowsAffected() != 1 {
return MasterFieldReceipt{}, ErrConflict
}
return MasterFieldReceipt{SavedRevision: revision}, nil
}