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
+58 -58
View File
@@ -32,8 +32,8 @@ func (s *Service) BindTranscoded(role chain.Role, ctid, parentFileHash, transcod
// ---- 工作包8:媒体资源库入库、发布与映射(需求6) ----
// IngestToLibrary 审核合格内容入媒资库,建立媒资编码映射(需求6-AC1/AC2/AC3)。
func (s *Service) IngestToLibrary(role chain.Role, maCode, ctid, mediaAssetID, libName string) error {
c, err := s.chain.QueryContent(maCode)
func (s *Service) IngestToLibrary(role chain.Role, ccCode, ctid, mediaAssetID, libName string) error {
c, err := s.chain.QueryContent(ccCode)
if err != nil {
return err
}
@@ -49,19 +49,19 @@ func (s *Service) IngestToLibrary(role chain.Role, maCode, ctid, mediaAssetID, l
}); err != nil {
return err
}
s.prov.Record(model.ProvenanceEvent{MACode: maCode, Node: model.NodeIngest, Operator: libName, Detail: "审合格入媒资库"})
return s.chain.SetContentStatus(maCode, model.StatusInLibrary)
s.prov.Record(model.ProvenanceEvent{CCCode: ccCode, Node: model.NodeIngest, Operator: libName, Detail: "审合格入媒资库"})
return s.chain.SetContentStatus(ccCode, model.StatusInLibrary)
}
// PublishRequest 从媒资库向运营商发布的请求(需求6-AC4)。
type PublishRequest struct {
MACode string
Certificate string // 必须携带 MA码+哈希证书
CCCode string
Certificate string // 必须携带 CC码+哈希证书
}
// PublishToOperator 校验证书后将内容置为已发布(需求6-AC4/AC5、需求3-AC8)。
func (s *Service) PublishToOperator(req PublishRequest) error {
c, err := s.chain.QueryContent(req.MACode)
c, err := s.chain.QueryContent(req.CCCode)
if err != nil {
return err
}
@@ -69,10 +69,10 @@ func (s *Service) PublishToOperator(req PublishRequest) error {
return ErrNotApproved
}
// 发布必须携带证书(含 MA 码)
if req.Certificate == "" || !certContainsMA(req.Certificate, req.MACode) {
if req.Certificate == "" || !certContainsMA(req.Certificate, req.CCCode) {
return ErrNoCertificate
}
return s.chain.SetContentStatus(req.MACode, model.StatusPublished)
return s.chain.SetContentStatus(req.CCCode, model.StatusPublished)
}
// ---- 工作包9CDN 注入校验(需求7) ----
@@ -85,9 +85,9 @@ type InjectResult struct {
}
// InjectToCDN 运营商注入 CDN 前校验哈希;匹配则放行并注册运营商映射(需求7-AC1~AC4)。
func (s *Service) InjectToCDN(role chain.Role, ctid, maCode, injectFileHash, operatorID, cdnEndpoint string) (InjectResult, error) {
func (s *Service) InjectToCDN(role chain.Role, ctid, ccCode, injectFileHash, operatorID, cdnEndpoint string) (InjectResult, error) {
// 内容须处于已发布状态
c, err := s.chain.QueryContent(maCode)
c, err := s.chain.QueryContent(ccCode)
if err != nil {
return InjectResult{}, err
}
@@ -95,7 +95,7 @@ func (s *Service) InjectToCDN(role chain.Role, ctid, maCode, injectFileHash, ope
return InjectResult{Allowed: false, Reason: "内容已下架"}, ErrNotApproved
}
res, err := s.chain.VerifyHash(maCode, injectFileHash)
res, err := s.chain.VerifyHash(ccCode, injectFileHash)
if err != nil {
return InjectResult{Allowed: false, Reason: err.Error()}, err
}
@@ -105,7 +105,7 @@ func (s *Service) InjectToCDN(role chain.Role, ctid, maCode, injectFileHash, ope
}
// 授权核验(需求25-AC2/AC3):若已登记授权,校验该运营商是否在授权平台内
if authRes := s.CheckAuthorization(maCode, "", operatorID); !authRes.Allowed {
if authRes := s.CheckAuthorization(ccCode, "", operatorID); !authRes.Allowed {
return InjectResult{Allowed: false, Reason: authRes.Reason}, ErrNotApproved
}
@@ -118,7 +118,7 @@ func (s *Service) InjectToCDN(role chain.Role, ctid, maCode, injectFileHash, ope
}); err != nil {
return InjectResult{}, err
}
s.prov.Record(model.ProvenanceEvent{MACode: maCode, Node: model.NodeInject, HashValue: injectFileHash, Operator: operatorID, Detail: "CDN 注入校验通过"})
s.prov.Record(model.ProvenanceEvent{CCCode: ccCode, Node: model.NodeInject, HashValue: injectFileHash, Operator: operatorID, Detail: "CDN 注入校验通过"})
return InjectResult{Allowed: true, DistributionID: distID}, nil
}
@@ -159,28 +159,28 @@ func (s *Service) ReportVersionChange(ctid, reason, prevHash, newHash string, ol
// ---- 工作包14:违规应急下架(需求11) ----
// Takedown 监管主体一键下架:解析 MA 码绑定的三方编码与 CDN 端点(需求11-AC1/AC2/AC4)。
func (s *Service) Takedown(role chain.Role, maCode, reason string) (chain.MappingsResult, error) {
return s.chain.Revoke(role, maCode, reason)
func (s *Service) Takedown(role chain.Role, ccCode, reason string) (chain.MappingsResult, error) {
return s.chain.Revoke(role, ccCode, reason)
}
// TakedownEpisode 集级下架:只下架指定集,整剧其他集继续流通(仅监管主体)。
func (s *Service) TakedownEpisode(role chain.Role, maCode string, episode int, reason string) error {
return s.chain.RevokeEpisode(role, maCode, episode, reason)
func (s *Service) TakedownEpisode(role chain.Role, ccCode string, episode int, reason string) error {
return s.chain.RevokeEpisode(role, ccCode, episode, reason)
}
// Restore 恢复上架整剧(仅监管主体)。
func (s *Service) Restore(role chain.Role, maCode string) error {
return s.chain.Restore(role, maCode)
func (s *Service) Restore(role chain.Role, ccCode string) error {
return s.chain.Restore(role, ccCode)
}
// RestoreEpisode 恢复上架指定集(仅监管主体)。
func (s *Service) RestoreEpisode(role chain.Role, maCode string, episode int) error {
return s.chain.RestoreEpisode(role, maCode, episode)
func (s *Service) RestoreEpisode(role chain.Role, ccCode string, episode int) error {
return s.chain.RestoreEpisode(role, ccCode, episode)
}
// certContainsMA 校验证书是否包含指定 MA 码。
func certContainsMA(cert, maCode string) bool {
return cert != "" && maCode != "" && strings.Contains(cert, maCode)
func certContainsMA(cert, ccCode string) bool {
return cert != "" && ccCode != "" && strings.Contains(cert, ccCode)
}
// ---- 二期 F09/F18:数据回传聚合与分账(需求9/需求21) ----
@@ -190,7 +190,7 @@ func certContainsMA(cert, maCode string) bool {
func (s *Service) ReportPlayback(events []model.PlaybackEvent) (accepted int, rejected int) {
valid := make([]model.PlaybackEvent, 0, len(events))
for _, e := range events {
c, err := s.chain.QueryContent(e.MACode)
c, err := s.chain.QueryContent(e.CCCode)
if err != nil || c.Status == model.StatusRevoked {
rejected++
continue
@@ -202,40 +202,40 @@ func (s *Service) ReportPlayback(events []model.PlaybackEvent) (accepted int, re
}
// PlaybackSummary 查询按 MA 码聚合的可信播放数据(需求9-AC2/AC3)。
func (s *Service) PlaybackSummary(maCode string) model.PlaybackSummary {
return s.pb.Summary(maCode)
func (s *Service) PlaybackSummary(ccCode string) model.PlaybackSummary {
return s.pb.Summary(ccCode)
}
// ComputeSettlement 基于可信播放数据计算分账(需求21-AC3)。
func (s *Service) ComputeSettlement(maCode, period string) (model.Settlement, error) {
if _, err := s.chain.QueryContent(maCode); err != nil {
func (s *Service) ComputeSettlement(ccCode, period string) (model.Settlement, error) {
if _, err := s.chain.QueryContent(ccCode); err != nil {
return model.Settlement{}, err
}
return s.pb.ComputeSettlement(maCode, period, model.DefaultShareConfig())
return s.pb.ComputeSettlement(ccCode, period, model.DefaultShareConfig())
}
// ---- 二期 F19/F20:追责取证与确权举证(需求22/23) ----
// Provenance 返回某 MA 码的全链路存证(需求22-AC1)。
func (s *Service) Provenance(maCode string) []model.ProvenanceEvent {
return s.prov.Trail(maCode)
func (s *Service) Provenance(ccCode string) []model.ProvenanceEvent {
return s.prov.Trail(ccCode)
}
// Accountability 责任界定取证:定位首次哈希变化节点与责任方(需求22-AC2)。
func (s *Service) Accountability(maCode string) model.AccountabilityReport {
return s.prov.Accountability(maCode)
func (s *Service) Accountability(ccCode string) model.AccountabilityReport {
return s.prov.Accountability(ccCode)
}
// CopyrightEvidence 导出版权确权证据链(需求23-AC1/AC2)。
func (s *Service) CopyrightEvidence(maCode string) (model.CopyrightEvidence, error) {
c, err := s.chain.QueryContent(maCode)
func (s *Service) CopyrightEvidence(ccCode string) (model.CopyrightEvidence, error) {
c, err := s.chain.QueryContent(ccCode)
if err != nil {
return model.CopyrightEvidence{}, err
}
trail := s.prov.Trail(maCode)
trail := s.prov.Trail(ccCode)
ev := model.CopyrightEvidence{
MACode: maCode, Title: c.Title, Issuer: c.Issuer, IssueDate: c.IssueDate,
ContentHash: c.FileHash, ChainAnchor: "chain://" + maCode, Trail: trail,
CCCode: ccCode, Title: c.Title, Issuer: c.Issuer, IssueDate: c.IssueDate,
ContentHash: c.FileHash, ChainAnchor: "chain://" + ccCode, Trail: trail,
Statement: "本证据链由 MA 码、内容哈希与上链时间戳构成,遵循『谁先锁定谁有权』,不可抵赖,可用于侵权投诉与司法举证。",
}
for _, e := range trail {
@@ -271,7 +271,7 @@ func (s *Service) MatchInfringement(perceptual string, high, medium int) ([]mode
if d <= high {
sim = "high"
}
out = append(out, model.InfringeMatch{MACode: ma, Title: e.Title, Distance: d, Similarity: sim})
out = append(out, model.InfringeMatch{CCCode: ma, Title: e.Title, Distance: d, Similarity: sim})
}
}
return out, nil
@@ -280,13 +280,13 @@ func (s *Service) MatchInfringement(perceptual string, high, medium int) ([]mode
// ---- 二期 F22:授权链与发布前核验(需求25) ----
// RecordAuthorization 登记信息网络传播权授权(需求25-AC1)。
func (s *Service) RecordAuthorization(maCode string, regions, platforms []string, expiry time.Time) error {
if _, err := s.chain.QueryContent(maCode); err != nil {
func (s *Service) RecordAuthorization(ccCode string, regions, platforms []string, expiry time.Time) error {
if _, err := s.chain.QueryContent(ccCode); err != nil {
return err
}
s.mu.Lock()
s.auths[maCode] = model.Authorization{
MACode: maCode, Regions: regions, Platforms: platforms,
s.auths[ccCode] = model.Authorization{
CCCode: ccCode, Regions: regions, Platforms: platforms,
ExpiryAt: expiry, GrantedAt: time.Now(),
}
s.mu.Unlock()
@@ -295,9 +295,9 @@ func (s *Service) RecordAuthorization(maCode string, regions, platforms []string
// CheckAuthorization 核验某地域/平台是否在授权范围内(需求25-AC2/AC3)。
// 未登记授权时默认放行(向后兼容);登记后超地域/过期/非授权平台拦截。
func (s *Service) CheckAuthorization(maCode, region, platform string) model.AuthCheckResult {
func (s *Service) CheckAuthorization(ccCode, region, platform string) model.AuthCheckResult {
s.mu.Lock()
a, ok := s.auths[maCode]
a, ok := s.auths[ccCode]
s.mu.Unlock()
if !ok {
return model.AuthCheckResult{Allowed: true, Reason: "未登记授权限制"}
@@ -317,8 +317,8 @@ func (s *Service) CheckAuthorization(maCode, region, platform string) model.Auth
// ---- 二期 F21:追更与增量哈希更新(需求24) ----
// AddEpisodes 追更:为已发码剧追加新集哈希,不触发存量重审、不重新发码(需求24-AC4)。
func (s *Service) AddEpisodes(role chain.Role, maCode string, episodes []model.EpisodeHash) error {
c, err := s.chain.QueryContent(maCode)
func (s *Service) AddEpisodes(role chain.Role, ccCode string, episodes []model.EpisodeHash) error {
c, err := s.chain.QueryContent(ccCode)
if err != nil {
return err
}
@@ -336,7 +336,7 @@ func (s *Service) AddEpisodes(role chain.Role, maCode string, episodes []model.E
}
}
s.prov.Record(model.ProvenanceEvent{
MACode: maCode, Node: model.NodeSubmit,
CCCode: ccCode, Node: model.NodeSubmit,
Operator: "追更", Detail: "追加新集,增量赋码",
})
return nil
@@ -345,26 +345,26 @@ func (s *Service) AddEpisodes(role chain.Role, maCode string, episodes []model.E
// ---- 二期 F13:跨省复用快速准入(需求13) ----
// Blacklist 将 MA 码加入黑名单(用于跨省校验)。
func (s *Service) Blacklist(maCode string) {
func (s *Service) Blacklist(ccCode string) {
s.mu.Lock()
s.black[maCode] = true
s.black[ccCode] = true
s.mu.Unlock()
}
// CrossProvinceAdmit B 省凭 MA 码+哈希证书快速准入(需求13)。
// 三重校验:MA 码有效 + 哈希与原过审版一致 + 非黑名单。
func (s *Service) CrossProvinceAdmit(maCode, fileHash, province string) model.CrossProvinceResult {
func (s *Service) CrossProvinceAdmit(ccCode, fileHash, province string) model.CrossProvinceResult {
res := model.CrossProvinceResult{}
// 1. MA 码有效
if _, err := s.chain.QueryContent(maCode); err != nil {
if _, err := s.chain.QueryContent(ccCode); err != nil {
res.Reason = "MA 码无效或不存在"
return res
}
res.MACodeValid = true
res.CCCodeValid = true
// 2. 哈希与原过审版一致
vr, err := s.chain.VerifyHash(maCode, fileHash)
vr, err := s.chain.VerifyHash(ccCode, fileHash)
if err != nil || !vr.Match {
res.Reason = "哈希与原过审版不一致"
return res
@@ -373,7 +373,7 @@ func (s *Service) CrossProvinceAdmit(maCode, fileHash, province string) model.Cr
// 3. 非黑名单
s.mu.Lock()
bl := s.black[maCode]
bl := s.black[ccCode]
s.mu.Unlock()
if bl {
res.Reason = "内容在黑名单中"
@@ -391,8 +391,8 @@ func (s *Service) CrossProvinceAdmit(maCode, fileHash, province string) model.Cr
// ---- 二期 F08:终端片段抽检(需求8) ----
// TerminalVerifySegment 终端按集抽检:校验某集哈希,不匹配则提示断流(需求8-AC1/AC2)。
func (s *Service) TerminalVerifySegment(maCode string, episode int, segHash string) (bool, string) {
res, err := s.chain.VerifyEpisodeHash(maCode, episode, segHash)
func (s *Service) TerminalVerifySegment(ccCode string, episode int, segHash string) (bool, string) {
res, err := s.chain.VerifyEpisodeHash(ccCode, episode, segHash)
if err != nil {
return false, "无法校验:" + err.Error()
}