init: AIGC-Hub/AVCC 方案文档 + TCS-IPTV 内容可信锁定系统 MVP

- 方案文档: AVCC 体系建设、IPTV TCS 需求(0-req)/PRD(1-prd)/任务(2-task)/二三四期任务
- tcs-iptv: Go 后端(哈希SDK/MA码生成/可信数据空间mock/业务编排/HTTP API+HMAC鉴权)
- web-console: React+AntD 监管大屏(角色工作台/全流程演示/监管片库)
- 一剧一码+集级哈希, 集级下架/恢复, 全栈测试通过
This commit is contained in:
selfrelease
2026-06-14 16:50:31 +08:00
commit a329d4906b
103 changed files with 20052 additions and 0 deletions
@@ -0,0 +1,137 @@
package service
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tcs-iptv/tcs/internal/chain"
"github.com/tcs-iptv/tcs/internal/hash"
)
// issueOne 完成一次"送审→CSPS审核→发码签发",返回 maCode、ctid、证书。
func issueOne(t *testing.T, s *Service) (string, string, string) {
t.Helper()
sub, err := s.SubmitForReview(sampleSub())
require.NoError(t, err)
require.NoError(t, s.ReviewCSPS(sub.ReviewID, true, "reviewer-1")) // 审核在前
issued, err := s.ApproveAndIssue(chain.RoleRegulator, sub.ReviewID, "北京市广播电视局")
require.NoError(t, err)
return issued.MACode, issued.ContentTwinID, issued.Certificate
}
func TestCSPSAndTranscode(t *testing.T) {
s := newService(t)
maCode, ctid, _ := issueOne(t, s)
_, err := s.BindTranscoded(chain.RoleReviewer, ctid, "filehash-abc",
"transcoded-h265-4k", "H.265", "3840x2160", "v1.0-4k")
require.NoError(t, err)
// 转码版也能验真通过
res, err := s.Verify(maCode, "transcoded-h265-4k")
require.NoError(t, err)
assert.True(t, res.Match)
}
func TestCSPSRejected(t *testing.T) {
s := newService(t)
sub, err := s.SubmitForReview(sampleSub())
require.NoError(t, err)
// CSPS 审核驳回 → 不得发码
require.NoError(t, s.ReviewCSPS(sub.ReviewID, false, "reviewer-1"))
_, err = s.ApproveAndIssue(chain.RoleRegulator, sub.ReviewID, "issuer")
assert.ErrorIs(t, err, ErrNotApproved)
}
func TestIssueRequiresCSPSApproval(t *testing.T) {
s := newService(t)
sub, err := s.SubmitForReview(sampleSub())
require.NoError(t, err)
// 未经 CSPS 审核直接发码 → 拒绝
_, err = s.ApproveAndIssue(chain.RoleRegulator, sub.ReviewID, "issuer")
assert.ErrorIs(t, err, ErrNotApproved)
}
func TestIngestAndPublish(t *testing.T) {
s := newService(t)
maCode, ctid, cert := issueOne(t, s)
require.NoError(t, s.IngestToLibrary(chain.RoleReviewer, maCode, ctid, "MEDIA-001", "广东IPTV媒资库"))
// 无证书发布被拒
err := s.PublishToOperator(PublishRequest{MACode: maCode, Certificate: ""})
assert.ErrorIs(t, err, ErrNoCertificate)
// 携带证书发布成功
require.NoError(t, s.PublishToOperator(PublishRequest{MACode: maCode, Certificate: cert}))
}
func TestInjectToCDN_MatchAndMismatch(t *testing.T) {
s := newService(t)
maCode, ctid, cert := issueOne(t, s)
require.NoError(t, s.IngestToLibrary(chain.RoleReviewer, maCode, ctid, "MEDIA-001", "媒资库"))
require.NoError(t, s.PublishToOperator(PublishRequest{MACode: maCode, Certificate: cert}))
// 哈希匹配 → 允许注入
res, err := s.InjectToCDN(chain.RoleOperator, ctid, maCode, "filehash-abc",
"CT-IPTV-GD", "cdn://ct-gd/iptv/vod/008923")
require.NoError(t, err)
assert.True(t, res.Allowed)
assert.NotEmpty(t, res.DistributionID)
// 哈希不匹配 → 拒绝注入
res, err = s.InjectToCDN(chain.RoleOperator, ctid, maCode, "tampered-hash",
"CT-IPTV-GD", "cdn://x")
assert.ErrorIs(t, err, ErrHashMismatch)
assert.False(t, res.Allowed)
}
func TestInjectToCDN_RevokedBlocked(t *testing.T) {
s := newService(t)
maCode, ctid, cert := issueOne(t, s)
require.NoError(t, s.IngestToLibrary(chain.RoleReviewer, maCode, ctid, "MEDIA-001", "媒资库"))
require.NoError(t, s.PublishToOperator(PublishRequest{MACode: maCode, Certificate: cert}))
// 下架后不得注入
_, err := s.Takedown(chain.RoleRegulator, maCode, "违规")
require.NoError(t, err)
_, err = s.InjectToCDN(chain.RoleOperator, ctid, maCode, "filehash-abc", "OP", "cdn://x")
assert.ErrorIs(t, err, ErrNotApproved)
}
func TestTakedown_ResolvesMappings(t *testing.T) {
s := newService(t)
maCode, ctid, cert := issueOne(t, s)
require.NoError(t, s.IngestToLibrary(chain.RoleReviewer, maCode, ctid, "MEDIA-001", "媒资库"))
require.NoError(t, s.PublishToOperator(PublishRequest{MACode: maCode, Certificate: cert}))
_, _ = s.InjectToCDN(chain.RoleOperator, ctid, maCode, "filehash-abc", "CT-IPTV-GD", "cdn://ct-gd/vod/1")
// 非监管主体不得下架
_, err := s.Takedown(chain.RoleOperator, maCode, "越权")
assert.ErrorIs(t, err, chain.ErrPermissionDenied)
// 监管下架,解析出 CDN 端点
res, err := s.Takedown(chain.RoleRegulator, maCode, "违规")
require.NoError(t, err)
assert.Contains(t, res.CDNEndpoints, "cdn://ct-gd/vod/1")
}
func TestReportVersionChange_LocatesEpisode(t *testing.T) {
s := newService(t)
_, ctid, _ := issueOne(t, s)
old := []string{
hash.SHA256Hex([]byte("ep1")),
hash.SHA256Hex([]byte("ep2")),
hash.SHA256Hex([]byte("ep3")),
}
neu := []string{
hash.SHA256Hex([]byte("ep1")),
hash.SHA256Hex([]byte("ep2-tampered")),
hash.SHA256Hex([]byte("ep3")),
}
episodes, err := s.ReportVersionChange(ctid, "第2集被替换", "root-old", "root-new", old, neu)
require.NoError(t, err)
assert.Equal(t, []int{2}, episodes, "应定位到第2集(1-based")
}