517d5f862d
目标非空又没有生效版本时,首次发布以前只能停在"权威版本未知"。现在有了 真实入口:管理员发起 baseline 作业,worker 在维护隔离下把目标整库捕获成 归档快照,再用同一套 Verify 证明目标与快照逐类一致,最后登记 production_effective(证据写明 origin=target_baseline、快照 ID、逐表行数、 目标索引映射)。不写目标内容、不清空目标、不手写版本。 同时修两处会误导人的判定: - productionScopeEmpty 原来只数 knowledges,"文档删了但分块/Wiki/资源还在" 的目标会被当成空库;改用适配层同一份表清单与范围过滤逐表计数。 - 概览里的"测试封存候选"原来取该 scope 最新一份归档,而归档里还有生产 预备份与目标基线快照;改为只认版本登记里 kind=test_sealed 的那一份。 迁移 075 放开 kind='baseline' 并把它并入 sync/restore 的写互斥索引; worker 重启时用同一作业 id 接管未完成的基线窗口。维护接口补 capability, 页面才能区分"未运行维护"与"维护能力没接线"。 验证:go build ./... / go vet ./... 通过;新增用例覆盖管理员校验、已有生效 版本拒绝、目标读不到时零作业零版本、候选来自登记、缺项文案指向基线入口、 capability 逐项缺失;反向自检(把候选改回按最新归档取、把文案改回旧句、 去掉管理员判断)三项均按预期变红后复原。
47 lines
1.8 KiB
Go
47 lines
1.8 KiB
Go
package v071
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
)
|
|
|
|
// ScopeContentCounts 数一个知识库在各内容表里的行数(数据库侧事实)。
|
|
//
|
|
// 为什么不能只数 knowledges:文档被删掉、分块与 Wiki/资源还在的库并不为空。
|
|
// 只数一张表会把这种目标判成"首次发布的空库",从而让一条本该走基线初始化的
|
|
// 路径绕过去;这里用与整库读写完全相同的表清单与过滤条件,保证"空"的定义只有一份。
|
|
func ScopeContentCounts(ctx context.Context, pool *pgxpool.Pool, identity ProductionScope) (map[string]int64, error) {
|
|
if pool == nil || identity.TenantID == 0 || !validSearchIdentifier(identity.KnowledgeBaseID) {
|
|
return nil, errors.New("invalid scope identity")
|
|
}
|
|
counts := make(map[string]int64, len(productionLibraryTables))
|
|
for _, table := range productionLibraryTables {
|
|
var count int64
|
|
if err := pool.QueryRow(ctx, fmt.Sprintf(`SELECT count(*) FROM %s WHERE %s`, table, scopeWhere(table)),
|
|
identity.TenantID, identity.KnowledgeBaseID).Scan(&count); err != nil {
|
|
return nil, fmt.Errorf("count %s: %w", table, err)
|
|
}
|
|
counts[table] = count
|
|
}
|
|
return counts, nil
|
|
}
|
|
|
|
// ScopeContentEmpty 只有每张内容表都是 0 才算空目标,并交回逐表行数作为证据。
|
|
// 注意它**不**包含检索索引:索引由分块派生,分块为空而索引非空属于不一致,
|
|
// 应在执行侧(预备份/基线捕获)报出来,不能在这里被当成"空"。
|
|
func ScopeContentEmpty(ctx context.Context, pool *pgxpool.Pool, identity ProductionScope) (bool, map[string]int64, error) {
|
|
counts, err := ScopeContentCounts(ctx, pool, identity)
|
|
if err != nil {
|
|
return false, nil, err
|
|
}
|
|
for _, count := range counts {
|
|
if count != 0 {
|
|
return false, counts, nil
|
|
}
|
|
}
|
|
return true, counts, nil
|
|
}
|