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
+127
View File
@@ -0,0 +1,127 @@
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/macode"
"github.com/tcs-iptv/tcs/internal/model"
)
// 24 集微短剧:一剧一 MA 码,每集独立哈希,可按集验真。
func TestEpisodeLevel_OneSeriesOneCodeMultiEpisodeHash(t *testing.T) {
s := newService(t)
eps := make([]model.EpisodeHash, 0, 24)
for i := 1; i <= 24; i++ {
eps = append(eps, model.EpisodeHash{
Episode: i,
FileSHA256: "ep-hash-" + string(rune('a'+i)),
MerkleRoot: "ep-mr-" + string(rune('a'+i)),
Duration: 180,
})
}
sub := Submission{
Title: "长安少年行", EpisodeCount: 24, Category: macode.CategoryMicroDrama,
FileHash: "series-root-hash", MerkleRoot: "series-merkle-root",
Episodes: eps,
CPMediaID: "XAQJSL-2026-001", CPName: "西安曲江丝路文化传播有限公司",
}
r, err := s.SubmitForReview(sub)
require.NoError(t, err)
require.NoError(t, s.ReviewCSPS(r.ReviewID, true, "rv-1"))
issued, err := s.ApproveAndIssue(chain.RoleRegulator, r.ReviewID, "陕西IPTV运营公司")
require.NoError(t, err)
// 一剧一码
assert.True(t, macode.IsValid(issued.MACode))
// 24 集哈希全部绑定在同一 MA 码下
list, err := s.ListEpisodes(issued.MACode)
require.NoError(t, err)
assert.Len(t, list, 24)
// 按集验真:第 7 集正确哈希匹配
res, err := s.VerifyEpisode(issued.MACode, 7, "ep-hash-"+string(rune('a'+7)))
require.NoError(t, err)
assert.True(t, res.Match)
// 第 7 集错误哈希 → 不匹配(疑似该集被替换)
_, err = s.VerifyEpisode(issued.MACode, 7, "tampered-ep7")
assert.ErrorIs(t, err, ErrHashMismatch)
}
// 集级下架:只下架第3集,整剧其他集不受影响。
func TestEpisodeTakedown(t *testing.T) {
s := newService(t)
eps := []model.EpisodeHash{
{Episode: 1, FileSHA256: "h1"}, {Episode: 2, FileSHA256: "h2"},
{Episode: 3, FileSHA256: "h3"}, {Episode: 4, FileSHA256: "h4"},
}
sub := Submission{
Title: "多集剧", EpisodeCount: 4, Category: macode.CategoryMicroDrama,
FileHash: "series-h", MerkleRoot: "series-mr", Episodes: eps,
}
r, err := s.SubmitForReview(sub)
require.NoError(t, err)
require.NoError(t, s.ReviewCSPS(r.ReviewID, true, "rv-1"))
issued, err := s.ApproveAndIssue(chain.RoleRegulator, r.ReviewID, "陕西IPTV运营公司")
require.NoError(t, err)
// 运营商无权集级下架
err = s.TakedownEpisode(chain.RoleOperator, issued.MACode, 3, "第3集违规")
assert.ErrorIs(t, err, chain.ErrPermissionDenied)
// 监管下架第3集
require.NoError(t, s.TakedownEpisode(chain.RoleRegulator, issued.MACode, 3, "第3集违规"))
list, err := s.ListEpisodes(issued.MACode)
require.NoError(t, err)
for _, b := range list {
if b.Episode == 3 {
assert.True(t, b.Revoked, "第3集应已下架")
} else {
assert.False(t, b.Revoked, "第%d集不应受影响", b.Episode)
}
}
}
// 集级子标识:MA码#E07 解析与生成。
func TestEpisodeSubID(t *testing.T) {
ma := "MA.156.8531.6101/WD/20260000001"
sub := macode.EpisodeSubID(ma, 7)
assert.Equal(t, "MA.156.8531.6101/WD/20260000001#E07", sub)
parsedMA, ep := macode.ParseEpisodeSubID(sub)
assert.Equal(t, ma, parsedMA)
assert.Equal(t, 7, ep)
// 无后缀 → 整剧(episode 0
parsedMA2, ep2 := macode.ParseEpisodeSubID(ma)
assert.Equal(t, ma, parsedMA2)
assert.Equal(t, 0, ep2)
}
// 单体内容(电影,无分集):episodes 为空也能正常签发与整剧验真。
func TestSingleContent_NoEpisodes(t *testing.T) {
s := newService(t)
sub := sampleSub()
sub.Episodes = nil
r, err := s.SubmitForReview(sub)
require.NoError(t, err)
require.NoError(t, s.ReviewCSPS(r.ReviewID, true, "rv-1"))
issued, err := s.ApproveAndIssue(chain.RoleRegulator, r.ReviewID, "陕西IPTV运营公司")
require.NoError(t, err)
list, err := s.ListEpisodes(issued.MACode)
require.NoError(t, err)
assert.Empty(t, list, "单体内容无集级绑定")
// 整剧验真仍可用
res, err := s.Verify(issued.MACode, sub.FileHash)
require.NoError(t, err)
assert.True(t, res.Match)
}