4421dd38ce
build / build (push) Successful in 1m35s
verifyCapturedSource 失败时原实现只 return err:队列作业已是 failed,业务行 library_jobs 却停在 running/export、error 为空,页面永远显示"导出中"(实测 libseal-lib-internal-20261102T234053)。同函数其余四条失败分支都同时写 stage 与 job,这一条漏了。 现在 ensureCapturedSource 在校验失败时按既有惯例写 stage failed + job failed, 原错原样进取值、stage detail 与 job.error,恢复文案指向"修配置后重新封存"(快照 字节完好,不需要重采)。成功路径不变。 验证: - RED:新增用例在修复前失败于 "stage writes = [], want exactly one failed stage" - GREEN:TestCapturedSourceMismatchPersistsFailedStageAndJob / TestCapturedSourceMatchStaysSuccessful 通过 - make verify:16 项必需检查全绿(tests-go 含新用例)
54 lines
2.2 KiB
Go
54 lines
2.2 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"knowledge-governance/internal/appconfig"
|
|
"knowledge-governance/internal/governance"
|
|
"knowledge-governance/internal/librarysnapshot"
|
|
"time"
|
|
)
|
|
|
|
func configuredCaptureSource(config appconfig.Config, scope string) governance.LibrarySourceIdentity {
|
|
kb := config.LibrarySourceInternalKB
|
|
if scope == "external" {
|
|
kb = config.LibrarySourceExternalKB
|
|
}
|
|
return governance.LibrarySourceIdentity{Project: config.SourceProject(), App: config.LibrarySourceApp, Scope: scope, TenantID: config.LibrarySourceTenantID, KnowledgeBaseID: kb}
|
|
}
|
|
func verifyCapturedSource(root, snapshotID string, source governance.LibrarySourceIdentity) error {
|
|
if _, err := source.ID(); err != nil {
|
|
return err
|
|
}
|
|
archive, err := librarysnapshot.Open(root, 256*1024*1024)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer archive.Close()
|
|
manifest, err := archive.Load(snapshotID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if manifest.Scope != source.Scope || manifest.TenantID != source.TenantID || manifest.KnowledgeBaseID != source.KnowledgeBaseID {
|
|
return errors.New("captured manifest differs from configured source")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ensureCapturedSource 校验快照来源。校验失败必须把失败写进业务行:
|
|
// 队列侧此时已 failed,只 return err 会让页面永远停在"导出中"
|
|
// (实际事故:libseal-lib-internal-20261102T234053 running/export/error 空)。
|
|
func ensureCapturedSource(ctx context.Context, store libraryStageStore, jobID, root, snapshotID string, source governance.LibrarySourceIdentity, now time.Time) error {
|
|
err := verifyCapturedSource(root, snapshotID, source)
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
// 快照本身已产生且字节完好,错的只是 worker 读到的来源身份;因此恢复路径是
|
|
// "修配置后重新登记/重新封存",不是"重采数据"。原错原样写进 stage、job 与
|
|
// 返回值:改写成更友好的文案会让真正的配置漂移查不出来。
|
|
libraryStageRecorder{store: store, jobID: jobID}.Stage(ctx, "export", "failed", err.Error())
|
|
_ = store.AdvanceLibraryJob(ctx, jobID, governance.LibraryJobFailed, "export", err.Error(),
|
|
"快照已产生但来源身份与配置不一致,未登记为测试封存;修正来源配置后重新封存", "", "", now)
|
|
return err
|
|
}
|