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
+111
View File
@@ -0,0 +1,111 @@
package service
import (
"strings"
"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"
)
func newService(t *testing.T) *Service {
t.Helper()
gen := macode.NewGenerator(macode.NewMemoryStore())
require.NoError(t, gen.RegisterSegment(macode.Segment{
IndustryNode: "8531", OrgNode: "4401",
Category: macode.CategoryMicroDrama, Start: 1, End: 100, SeqWidth: 7,
}))
return New(chain.NewMemoryChain(), gen)
}
func sampleSub() Submission {
return Submission{
Title: "示例微短剧", EpisodeCount: 24, Category: macode.CategoryMicroDrama,
FileHash: "filehash-abc", MerkleRoot: "merkle-abc", Perceptual: "phash-abc",
CPMediaID: "FS-MEDIA-77821", CPName: "飞翮信息",
}
}
func TestSubmit_IncompleteHashRejected(t *testing.T) {
s := newService(t)
_, err := s.SubmitForReview(Submission{Title: "无哈希", Category: macode.CategoryMicroDrama})
assert.ErrorIs(t, err, ErrIncompleteHashPkg)
}
func TestSubmit_Success(t *testing.T) {
s := newService(t)
res, err := s.SubmitForReview(sampleSub())
require.NoError(t, err)
assert.NotEmpty(t, res.ReviewID)
assert.NotEmpty(t, res.ContentTwinID)
assert.Equal(t, "pending", res.Status)
}
func TestApproveAndIssue_GeneratesMACode(t *testing.T) {
s := newService(t)
sub, err := s.SubmitForReview(sampleSub())
require.NoError(t, err)
require.NoError(t, s.ReviewCSPS(sub.ReviewID, true, "rv-1"))
issued, err := s.ApproveAndIssue(chain.RoleRegulator, sub.ReviewID, "北京市广播电视局")
require.NoError(t, err)
// 模式BMA 码由系统按号段生成
assert.True(t, macode.IsValid(issued.MACode), "应生成合法 MA 码: %s", issued.MACode)
assert.True(t, strings.HasPrefix(issued.MACode, "MA.156.8531.4401/WD/"), "前缀应匹配号段: %s", issued.MACode)
assert.NotEmpty(t, issued.TxID)
assert.Contains(t, issued.Certificate, issued.MACode)
}
func TestApproveAndIssue_OnlyRegulator(t *testing.T) {
s := newService(t)
sub, _ := s.SubmitForReview(sampleSub())
require.NoError(t, s.ReviewCSPS(sub.ReviewID, true, "rv-1")) // 先过审,才轮到校验角色
_, err := s.ApproveAndIssue(chain.RoleCP, sub.ReviewID, "x")
assert.ErrorIs(t, err, chain.ErrPermissionDenied)
}
func TestSubmit_DuplicateRejectedAfterIssue(t *testing.T) {
s := newService(t)
sub, _ := s.SubmitForReview(sampleSub())
require.NoError(t, s.ReviewCSPS(sub.ReviewID, true, "rv-1"))
_, err := s.ApproveAndIssue(chain.RoleRegulator, sub.ReviewID, "issuer")
require.NoError(t, err)
// 同哈希再次送审 → 换壳重发拦截
_, err = s.SubmitForReview(sampleSub())
assert.ErrorIs(t, err, ErrDuplicateContent)
}
func TestVerify_MatchAndMismatch(t *testing.T) {
s := newService(t)
sub, _ := s.SubmitForReview(sampleSub())
require.NoError(t, s.ReviewCSPS(sub.ReviewID, true, "rv-1"))
issued, _ := s.ApproveAndIssue(chain.RoleRegulator, sub.ReviewID, "issuer")
res, err := s.Verify(issued.MACode, "filehash-abc")
require.NoError(t, err)
assert.True(t, res.Match)
_, err = s.Verify(issued.MACode, "tampered")
assert.ErrorIs(t, err, ErrHashMismatch)
}
func TestApproveAndIssue_TwoContentsUniqueCodes(t *testing.T) {
s := newService(t)
sub1, _ := s.SubmitForReview(sampleSub())
require.NoError(t, s.ReviewCSPS(sub1.ReviewID, true, "rv-1"))
i1, err := s.ApproveAndIssue(chain.RoleRegulator, sub1.ReviewID, "issuer")
require.NoError(t, err)
sub2v := sampleSub()
sub2v.FileHash = "filehash-def"
sub2v.MerkleRoot = "merkle-def"
sub2, _ := s.SubmitForReview(sub2v)
require.NoError(t, s.ReviewCSPS(sub2.ReviewID, true, "rv-1"))
i2, err := s.ApproveAndIssue(chain.RoleRegulator, sub2.ReviewID, "issuer")
require.NoError(t, err)
assert.NotEqual(t, i1.MACode, i2.MACode, "两条内容应分配不同 MA 码")
}