// Package provenance 记录内容全链路存证事件,支撑追责取证与确权举证(二期 F19/F20)。 // 对应需求22(责任界定)、需求23(确权维权)。 // MVP 用内存存储;生产落审计链 + PostgreSQL,保证不可篡改。 package provenance import ( "sync" "time" "github.com/tcs-iptv/tcs/internal/model" ) // Store 全链路存证存储。 type Store struct { mu sync.RWMutex trails map[string][]model.ProvenanceEvent // maCode -> 时间序事件 } // NewStore 创建存证存储。 func NewStore() *Store { return &Store{trails: make(map[string][]model.ProvenanceEvent)} } // Record 追加一条存证事件(按时间序)。 func (s *Store) Record(e model.ProvenanceEvent) { if e.Timestamp.IsZero() { e.Timestamp = time.Now() } s.mu.Lock() defer s.mu.Unlock() s.trails[e.MACode] = append(s.trails[e.MACode], e) } // Trail 返回某 MA 码的全链路存证(需求22-AC1)。 func (s *Store) Trail(maCode string) []model.ProvenanceEvent { s.mu.RLock() defer s.mu.RUnlock() out := make([]model.ProvenanceEvent, len(s.trails[maCode])) copy(out, s.trails[maCode]) return out } // Accountability 责任界定:以发码基准哈希为准,定位首次发生哈希变化的节点(需求22-AC2)。 func (s *Store) Accountability(maCode string) model.AccountabilityReport { trail := s.Trail(maCode) report := model.AccountabilityReport{MACode: maCode, Trail: trail, Consistent: true} // 基准哈希 = 发码节点(NodeIssue)的哈希 for _, e := range trail { if e.Node == model.NodeIssue && e.HashValue != "" { report.BaselineHash = e.HashValue break } } if report.BaselineHash == "" { report.Conclusion = "未发码或无基准哈希,无法判定" report.Consistent = false return report } // 检查后续携带哈希的节点是否与基准一致 for i := range trail { e := trail[i] if e.HashValue == "" || e.Node == model.NodeIssue || e.Node == model.NodeSubmit { continue } // 转码节点哈希本就不同(合法),跳过 if e.Node == model.NodeTranscode { continue } if e.HashValue != report.BaselineHash { report.Consistent = false ev := e report.FirstChange = &ev report.Conclusion = "在【" + string(e.Node) + "】节点(" + e.Operator + ")检出哈希与发码基准不一致,疑似该环节偷换/篡改" return report } } report.Conclusion = "全链路哈希与发码基准一致,审播一致,无偷换" return report }