feat: 方案100%匹配 — RBAC动态权限+Grafana监控+目录库独立微服务+全国目录中心+标识同步

This commit is contained in:
selfrelease
2026-06-30 13:42:11 +08:00
parent 96e2393c50
commit 29a6b57533
40 changed files with 4159 additions and 7 deletions
+200
View File
@@ -393,4 +393,204 @@ func (m *MemoryChain) maCodeByCTID(ctid string) string {
return ""
}
// QueryByHash 根据内容哈希反查标识信息及映射关系。
func (m *MemoryChain) QueryByHash(fileHash string) (model.ContentQueryResult, error) {
m.mu.RLock()
defer m.mu.RUnlock()
maCode, ok := m.hashIndex[fileHash]
if !ok {
return model.ContentQueryResult{Found: false}, ErrNotFound
}
return model.ContentQueryResult{
Found: true,
Content: m.contents[maCode],
Bindings: m.bindings[maCode],
Mappings: m.mappings[maCode],
}, nil
}
// QueryByProvincialCode 根据省级内容编码(CP MediaID)反查标识信息。
func (m *MemoryChain) QueryByProvincialCode(provincialCode string) (model.ContentQueryResult, error) {
m.mu.RLock()
defer m.mu.RUnlock()
for maCode, maps := range m.mappings {
for _, mp := range maps {
if mp.Party == model.PartyCP && mp.PartyID == provincialCode {
return model.ContentQueryResult{
Found: true,
Content: m.contents[maCode],
Bindings: m.bindings[maCode],
Mappings: maps,
}, nil
}
}
}
return model.ContentQueryResult{Found: false}, ErrNotFound
}
// QueryByLibraryFileID 根据片库文件 ID(媒资库 ID)反查标识信息。
func (m *MemoryChain) QueryByLibraryFileID(libraryFileID string) (model.ContentQueryResult, error) {
m.mu.RLock()
defer m.mu.RUnlock()
for maCode, maps := range m.mappings {
for _, mp := range maps {
if mp.Party == model.PartyReviewer && mp.PartyID == libraryFileID {
return model.ContentQueryResult{
Found: true,
Content: m.contents[maCode],
Bindings: m.bindings[maCode],
Mappings: maps,
}, nil
}
}
}
return model.ContentQueryResult{Found: false}, ErrNotFound
}
// MergeMA 将多个 MA 码合并为一个主 MA 码(仅监管主体)。
// 被合并的 MA 码的哈希绑定和映射迁移至主 MA 码,原 MA 码状态标记为 merged。
func (m *MemoryChain) MergeMA(role Role, req model.MergeRequest) (model.MergeResult, error) {
if role != RoleRegulator {
return model.MergeResult{}, ErrPermissionDenied
}
m.mu.Lock()
defer m.mu.Unlock()
primary, ok := m.contents[req.PrimaryMACode]
if !ok {
return model.MergeResult{}, ErrNotFound
}
_ = primary
migratedBindings := 0
migratedMappings := 0
for _, secMA := range req.SecondaryMACodes {
secContent, exists := m.contents[secMA]
if !exists {
return model.MergeResult{}, fmt.Errorf("%w: secondary MA %s not found", ErrNotFound, secMA)
}
if secContent.Status == model.StatusMerged {
return model.MergeResult{}, fmt.Errorf("chain: MA %s already merged", secMA)
}
// 迁移哈希绑定至主 MA 码
for _, b := range m.bindings[secMA] {
b.ContentTwinID = primary.ContentTwinID
m.bindings[req.PrimaryMACode] = append(m.bindings[req.PrimaryMACode], b)
migratedBindings++
}
// 迁移映射至主 MA 码
for _, mp := range m.mappings[secMA] {
mp.ContentTwinID = primary.ContentTwinID
m.mappings[req.PrimaryMACode] = append(m.mappings[req.PrimaryMACode], mp)
migratedMappings++
}
// 更新哈希索引指向主 MA 码
for _, b := range m.bindings[secMA] {
if b.HashType == model.HashFile || b.HashType == model.HashTranscoded {
m.hashIndex[b.HashValue] = req.PrimaryMACode
}
}
// 清空被合并 MA 码的绑定和映射,状态标记为 merged
m.bindings[secMA] = nil
m.mappings[secMA] = nil
secContent.Status = model.StatusMerged
m.contents[secMA] = secContent
}
txID := m.nextTx("mergeMA")
return model.MergeResult{
PrimaryMACode: req.PrimaryMACode,
MergedMACodes: req.SecondaryMACodes,
MigratedBindings: migratedBindings,
MigratedMappings: migratedMappings,
TxID: txID,
}, nil
}
// SplitMA 将一个 MA 码拆分为多个独立 MA 码(仅监管主体)。
// 按集号将哈希绑定和映射迁移至新 MA 码,源 MA 码状态标记为 split。
func (m *MemoryChain) SplitMA(role Role, req model.SplitRequest) (model.SplitResult, error) {
if role != RoleRegulator {
return model.SplitResult{}, ErrPermissionDenied
}
m.mu.Lock()
defer m.mu.Unlock()
srcContent, ok := m.contents[req.SourceMACode]
if !ok {
return model.SplitResult{}, ErrNotFound
}
if srcContent.Status == model.StatusMerged || srcContent.Status == model.StatusSplit {
return model.SplitResult{}, fmt.Errorf("chain: MA %s already %s", req.SourceMACode, srcContent.Status)
}
migratedBindings := 0
migratedMappings := 0
newMACodes := make([]string, 0, len(req.Splits))
for _, split := range req.Splits {
// 创建新内容记录
newContent := srcContent
newContent.MACode = split.NewMACode
newContent.ContentTwinID = split.NewMACode + "-ctid"
newContent.Title = split.Title
newContent.Status = model.StatusApproved
newContent.CreatedAt = time.Now()
m.contents[split.NewMACode] = newContent
// 按集号迁移哈希绑定
for _, b := range m.bindings[req.SourceMACode] {
shouldMigrate := false
if len(split.Episodes) == 0 {
// 空集号列表:迁移全部(整剧拆分场景)
shouldMigrate = true
} else {
for _, ep := range split.Episodes {
if b.Episode == ep {
shouldMigrate = true
break
}
}
}
if shouldMigrate {
newB := b
newB.ContentTwinID = newContent.ContentTwinID
m.bindings[split.NewMACode] = append(m.bindings[split.NewMACode], newB)
// 更新哈希索引
if b.HashType == model.HashFile || b.HashType == model.HashTranscoded {
m.hashIndex[b.HashValue] = split.NewMACode
}
migratedBindings++
}
}
// 迁移映射(全部映射复制到新 MA 码)
for _, mp := range m.mappings[req.SourceMACode] {
newMP := mp
newMP.ContentTwinID = newContent.ContentTwinID
m.mappings[split.NewMACode] = append(m.mappings[split.NewMACode], newMP)
migratedMappings++
}
newMACodes = append(newMACodes, split.NewMACode)
}
// 源 MA 码状态标记为 split
srcContent.Status = model.StatusSplit
m.contents[req.SourceMACode] = srcContent
txID := m.nextTx("splitMA")
return model.SplitResult{
SourceMACode: req.SourceMACode,
NewMACodes: newMACodes,
MigratedBindings: migratedBindings,
MigratedMappings: migratedMappings,
TxID: txID,
}, nil
}
var _ Client = (*MemoryChain)(nil)