package knowledgeversion import ( "crypto/sha256" "encoding/hex" "encoding/json" "time" "github.com/Tencent/WeKnora/internal/types" "gorm.io/gorm" ) // GraphRecord retains original evidence IDs and payloads in each independent KB. type GraphRecord struct { TenantID uint64 `gorm:"primaryKey"` KnowledgeBaseID string `gorm:"primaryKey"` SourceTable string `gorm:"primaryKey"` LegacyID int64 `gorm:"primaryKey;autoIncrement:false"` GraphVersionID int64 Payload types.JSON `gorm:"type:jsonb"` SourceSnapshotSHA256 string `gorm:"column:source_snapshot_sha256"` ImportedAt time.Time } func (GraphRecord) TableName() string { return "native_graph_records" } type GraphSource struct { TenantID uint64 `gorm:"primaryKey"` KnowledgeBaseID string `gorm:"primaryKey"` SourceChunkID string `gorm:"primaryKey"` SourceFile string SourceContentHash string Excerpt string NativeKnowledgeID *string NativeChunkID *string NativeContentSHA256 *string `gorm:"column:native_content_sha256"` MappingState string } func (GraphSource) TableName() string { return "native_graph_sources" } func GraphTablesExist(db *gorm.DB) bool { return db.Migrator().HasTable(&GraphRecord{}) && db.Migrator().HasTable(&GraphSource{}) } // GraphFingerprint participates in approval conflict detection without exposing // source paths, legacy payloads or private document identifiers to reviewers. func GraphFingerprint(db *gorm.DB, tenant uint64, kb string) (string, error) { if !GraphTablesExist(db) { return "", nil } h := sha256.New() count := 0 for _, item := range []struct { model any order string }{ {&GraphRecord{}, "source_table, legacy_id"}, {&GraphSource{}, "source_chunk_id"}, } { rows, err := db.Model(item.model).Where("tenant_id=? AND knowledge_base_id=?", tenant, kb).Order(item.order).Rows() if err != nil { return "", err } for rows.Next() { var value any = &GraphRecord{} if _, ok := item.model.(*GraphSource); ok { value = &GraphSource{} } if err := db.ScanRows(rows, value); err != nil { rows.Close() return "", err } if err := json.NewEncoder(h).Encode(value); err != nil { rows.Close() return "", err } count++ } err = rows.Err() rows.Close() if err != nil { return "", err } } if count == 0 { return "", nil } return hex.EncodeToString(h.Sum(nil)), nil } func DeleteGraph(db *gorm.DB, tenant uint64, kb string) error { if !GraphTablesExist(db) { return nil } for _, model := range []any{&GraphSource{}, &GraphRecord{}} { if err := db.Where("tenant_id=? AND knowledge_base_id=?", tenant, kb).Delete(model).Error; err != nil { return err } } return nil }