feat: 更新CC码监管方案业务版文档,新增摘要、UGC专项、直播专项、存量处置、误判申诉、水印嵌入、CC码生命周期、组织保障等章节;同步macode到cccode重命名及其他代码变更

This commit is contained in:
freedakgmail
2026-07-20 22:25:40 +08:00
parent 9f1e2d0a21
commit eb4a82a877
76 changed files with 2600 additions and 2122 deletions
+35 -35
View File
@@ -10,7 +10,7 @@ import (
"time"
"github.com/tcs-iptv/tcs/internal/chain"
"github.com/tcs-iptv/tcs/internal/macode"
"github.com/tcs-iptv/tcs/internal/cccode"
"github.com/tcs-iptv/tcs/internal/model"
"github.com/tcs-iptv/tcs/internal/playback"
"github.com/tcs-iptv/tcs/internal/provenance"
@@ -30,7 +30,7 @@ var (
type Submission struct {
Title string
EpisodeCount int
Category string // 内容类目(macode.CategoryXxx),决定发码号段
Category string // 内容类目(cccode.CategoryXxx),决定发码号段
FileHash string
MerkleRoot string
Perceptual string
@@ -50,13 +50,13 @@ type SubmissionResult struct {
// Service 业务编排器。
type Service struct {
chain chain.Client
gen *macode.Generator
gen *cccode.Generator
pb *playback.Store
prov *provenance.Store
phash map[string]phashEntry // maCode -> 感知哈希条目(确权侵权比对)
auths map[string]model.Authorization // maCode -> 授权(F22
black map[string]bool // maCode -> 黑名单(跨省复用校验)
filings map[string]model.FilingRecord // maCode -> 备案/网标关联(三期 A.1)
phash map[string]phashEntry // ccCode -> 感知哈希条目(确权侵权比对)
auths map[string]model.Authorization // ccCode -> 授权(F22
black map[string]bool // ccCode -> 黑名单(跨省复用校验)
filings map[string]model.FilingRecord // ccCode -> 备案/网标关联(三期 A.1)
rights map[string]*model.UserRights // userHash -> 跨屏权益账户(四期 D.1)
mu sync.Mutex
seqMu sync.Mutex
@@ -69,7 +69,7 @@ type reviewItem struct {
ContentTwinID string
Sub Submission
Status string
MACode string
CCCode string
}
// phashEntry 感知哈希注册项,用于确权侵权比对。
@@ -79,7 +79,7 @@ type phashEntry struct {
}
// New 创建业务服务。
func New(c chain.Client, gen *macode.Generator) *Service {
func New(c chain.Client, gen *cccode.Generator) *Service {
return &Service{
chain: c, gen: gen,
pb: playback.NewStore(),
@@ -107,10 +107,10 @@ func (s *Service) SubmitForReview(sub Submission) (SubmissionResult, error) {
return SubmissionResult{}, ErrIncompleteHashPkg
}
// 防换壳重发(需求2-AC3、需求15-AC5
if maCode, exists := s.chain.HashExists(sub.FileHash); exists {
if ccCode, exists := s.chain.HashExists(sub.FileHash); exists {
return SubmissionResult{
Status: "rejected",
Message: fmt.Sprintf("内容哈希已存在,关联原 MA 码: %s", maCode),
Message: fmt.Sprintf("内容哈希已存在,关联原 MA 码: %s", ccCode),
}, ErrDuplicateContent
}
@@ -133,14 +133,14 @@ func (s *Service) SubmitForReview(sub Submission) (SubmissionResult, error) {
// IssueResult 签发结果。
type IssueResult struct {
MACode string `json:"ma_code"`
CCCode string `json:"ma_code"`
ContentTwinID string `json:"content_twin_id"`
TxID string `json:"tx_id"`
Certificate string `json:"certificate"` // MA码+哈希证书(MVP 简化为字符串)
Certificate string `json:"certificate"` // CC码+哈希证书(MVP 简化为字符串)
}
// ReviewCSPS CSPS 合规审核(发码前)。审核通过后方可发码,体现"审过才发证发码"。
// 对应需求5(CSPS审核)+ 需求3-AC2(审核通过后生成MA码)。
// 对应需求5(CSPS审核)+ 需求3-AC2(审核通过后生成CC码)。
func (s *Service) ReviewCSPS(reviewID string, approved bool, reviewerID string) error {
s.mu.Lock()
defer s.mu.Unlock()
@@ -158,7 +158,7 @@ func (s *Service) ReviewCSPS(reviewID string, approved bool, reviewerID string)
// ApproveAndIssue 在 CSPS 审核通过后**生成 MA 码**并强绑定哈希(需求3,模式B 自行发码)。
// 前置:该送审必须已通过 CSPS 审核(审过才发码)。
// MA 码由 macode.Generator 按内容类目从号段中原子分配。仅监管主体可调用。
// MA 码由 cccode.Generator 按内容类目从号段中原子分配。仅监管主体可调用。
func (s *Service) ApproveAndIssue(role chain.Role, reviewID, issuer string) (IssueResult, error) {
s.mu.Lock()
item, ok := s.reviews[reviewID]
@@ -179,10 +179,10 @@ func (s *Service) ApproveAndIssue(role chain.Role, reviewID, issuer string) (Iss
if err != nil {
return IssueResult{}, fmt.Errorf("service: allocate MA code: %w", err)
}
maCode := issued.MACode
ccCode := issued.CCCode
txID, err := s.chain.IssueMA(role, chain.IssueRequest{
MACode: maCode,
CCCode: ccCode,
ContentTwinID: item.ContentTwinID,
MerkleRoot: item.Sub.MerkleRoot,
FileHash: item.Sub.FileHash,
@@ -202,7 +202,7 @@ func (s *Service) ApproveAndIssue(role chain.Role, reviewID, issuer string) (Iss
s.mu.Lock()
item.Status = model.StatusIssued // 已发码,移出"待发码"队列
item.MACode = maCode
item.CCCode = ccCode
s.mu.Unlock()
// CP 注册本方映射
@@ -214,18 +214,18 @@ func (s *Service) ApproveAndIssue(role chain.Role, reviewID, issuer string) (Iss
})
// 记录全链路存证(送审→审核→发码)+ 注册感知哈希供确权比对
s.prov.Record(model.ProvenanceEvent{MACode: maCode, Node: model.NodeSubmit, HashValue: item.Sub.FileHash, Operator: item.Sub.CPName, Detail: "CP 送审"})
s.prov.Record(model.ProvenanceEvent{MACode: maCode, Node: model.NodeCSPSReview, Operator: "CSPS", Detail: "合规审核通过"})
s.prov.Record(model.ProvenanceEvent{MACode: maCode, Node: model.NodeIssue, HashValue: item.Sub.FileHash, Operator: issuer, Detail: "发码签发,绑定基准哈希"})
s.prov.Record(model.ProvenanceEvent{CCCode: ccCode, Node: model.NodeSubmit, HashValue: item.Sub.FileHash, Operator: item.Sub.CPName, Detail: "CP 送审"})
s.prov.Record(model.ProvenanceEvent{CCCode: ccCode, Node: model.NodeCSPSReview, Operator: "CSPS", Detail: "合规审核通过"})
s.prov.Record(model.ProvenanceEvent{CCCode: ccCode, Node: model.NodeIssue, HashValue: item.Sub.FileHash, Operator: issuer, Detail: "发码签发,绑定基准哈希"})
if item.Sub.Perceptual != "" {
s.mu.Lock()
s.phash[maCode] = phashEntry{Title: item.Sub.Title, Perceptual: item.Sub.Perceptual}
s.phash[ccCode] = phashEntry{Title: item.Sub.Title, Perceptual: item.Sub.Perceptual}
s.mu.Unlock()
}
cert := fmt.Sprintf("CERT|%s|%s|%s", maCode, item.Sub.FileHash, item.Sub.MerkleRoot)
cert := fmt.Sprintf("CERT|%s|%s|%s", ccCode, item.Sub.FileHash, item.Sub.MerkleRoot)
return IssueResult{
MACode: maCode,
CCCode: ccCode,
ContentTwinID: item.ContentTwinID,
TxID: txID,
Certificate: cert,
@@ -233,8 +233,8 @@ func (s *Service) ApproveAndIssue(role chain.Role, reviewID, issuer string) (Iss
}
// Verify 送审文件验真 / CDN 注入校验通用入口(需求4、需求7)。
func (s *Service) Verify(maCode, fileHash string) (chain.VerifyResult, error) {
res, err := s.chain.VerifyHash(maCode, fileHash)
func (s *Service) Verify(ccCode, fileHash string) (chain.VerifyResult, error) {
res, err := s.chain.VerifyHash(ccCode, fileHash)
if err != nil {
return res, err
}
@@ -245,13 +245,13 @@ func (s *Service) Verify(maCode, fileHash string) (chain.VerifyResult, error) {
}
// QueryMappings 查询 MA 码绑定的三方映射与 CDN 端点(需求11/17)。
func (s *Service) QueryMappings(maCode string) (chain.MappingsResult, error) {
return s.chain.QueryMappings(maCode)
func (s *Service) QueryMappings(ccCode string) (chain.MappingsResult, error) {
return s.chain.QueryMappings(ccCode)
}
// VerifyEpisode 按集级子标识(MA码#E07)或 MA码+集号 验真单集。
func (s *Service) VerifyEpisode(maCode string, episode int, fileHash string) (chain.VerifyResult, error) {
res, err := s.chain.VerifyEpisodeHash(maCode, episode, fileHash)
// VerifyEpisode 按集级子标识(CC码#E07)或 CC码+集号 验真单集。
func (s *Service) VerifyEpisode(ccCode string, episode int, fileHash string) (chain.VerifyResult, error) {
res, err := s.chain.VerifyEpisodeHash(ccCode, episode, fileHash)
if err != nil {
return res, err
}
@@ -262,8 +262,8 @@ func (s *Service) VerifyEpisode(maCode string, episode int, fileHash string) (ch
}
// ListEpisodes 列出某剧的全部集级哈希绑定。
func (s *Service) ListEpisodes(maCode string) ([]model.HashBinding, error) {
return s.chain.ListEpisodes(maCode)
func (s *Service) ListEpisodes(ccCode string) ([]model.HashBinding, error) {
return s.chain.ListEpisodes(ccCode)
}
// ReviewSummary 送审待办摘要(发码前阶段)。
@@ -275,7 +275,7 @@ type ReviewSummary struct {
EpisodeCount int `json:"episode_count"`
Status string `json:"status"`
CPName string `json:"cp_name"`
MACode string `json:"ma_code"`
CCCode string `json:"ma_code"`
}
// ListReviews 列出指定状态的送审待办(用于审核台/发码台队列)。
@@ -292,7 +292,7 @@ func (s *Service) ListReviews(status string) []ReviewSummary {
ReviewID: id, ContentTwinID: item.ContentTwinID,
Title: item.Sub.Title, Category: item.Sub.Category,
EpisodeCount: item.Sub.EpisodeCount, Status: item.Status,
CPName: item.Sub.CPName, MACode: item.MACode,
CPName: item.Sub.CPName, CCCode: item.CCCode,
})
}
return out