166f460d57
- internal/chain/chainmaker.go [//go:build chainmaker]: ChainMakerClient 适配器骨架, 实现 chain.Client 全部方法到合约 Invoke/Query,按角色证书做链上鉴权,错误映射回标准错误 - internal/chain/chainmaker_stub.go [//go:build !chainmaker]: 占位构造函数, 保证默认构建不依赖 SDK、主工程始终可编译 - contracts/tcs_registry/registry.go: 补齐合约方法 RegisterHashBinding/VerifyEpisodeHash/ListEpisodes/HashExists/RecordVersionChange/ RevokeEpisode/Restore/RestoreEpisode/SetContentStatus/QueryMappings/ListContents 并增加集级哈希/映射/版本计数索引 KV 设计 - config: TCS_CHAIN_BACKEND=memory|pg|chainmaker + TCS_CHAINMAKER_SDK_CONF 开关 - cmd/api-svc: newChain 按 backend 选择,chainmaker 失败逐级降级 pg 到内存 - internal/chain/conformance_test.go: chain.Client 契约测试套件,双实现共用 MemoryChain 默认跑;PersistentChain 经 TCS_TEST_PG_DSN;ChainMaker 经 -tags 与 env - 验证: 默认 build/vet/test 全绿;MemoryChain 与 PersistentChain 契约套件均通过,行为一致
114 lines
3.6 KiB
Go
114 lines
3.6 KiB
Go
package main
|
|
|
|
import (
|
|
"database/sql"
|
|
"log"
|
|
|
|
_ "github.com/lib/pq"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/tcs-iptv/tcs/internal/api"
|
|
"github.com/tcs-iptv/tcs/internal/chain"
|
|
"github.com/tcs-iptv/tcs/internal/config"
|
|
"github.com/tcs-iptv/tcs/internal/httpx"
|
|
"github.com/tcs-iptv/tcs/internal/macode"
|
|
"github.com/tcs-iptv/tcs/internal/service"
|
|
)
|
|
|
|
// openDB 尝试连接 PostgreSQL,连通则返回 *sql.DB,否则返回 nil(回退内存)。
|
|
func openDB(dsn string) *sql.DB {
|
|
db, err := sql.Open("postgres", dsn)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
if err := db.Ping(); err != nil {
|
|
return nil
|
|
}
|
|
return db
|
|
}
|
|
|
|
// newAllocationStore 优先使用 PostgreSQL(持久、防重号),不可用时回退内存。
|
|
func newAllocationStore(db *sql.DB) macode.AllocationStore {
|
|
if db != nil {
|
|
log.Printf("macode: 使用 PostgreSQL 号段存储")
|
|
return macode.NewPostgresStore(db)
|
|
}
|
|
log.Printf("macode: PostgreSQL 不可用,回退内存号段存储(仅开发用)")
|
|
return macode.NewMemoryStore()
|
|
}
|
|
|
|
// newChain 按配置选择链后端:
|
|
// - chainmaker:真实链(需 -tags chainmaker 构建并配置 SDK),失败回退 pg/内存
|
|
// - pg:PG 持久化链(写穿+水合,重启不丢数据)
|
|
// - memory:纯内存(仅开发)
|
|
//
|
|
// PG 不可用时自动降级到内存。
|
|
func newChain(backend, sdkConf string, db *sql.DB) chain.Client {
|
|
switch backend {
|
|
case "chainmaker":
|
|
cm, err := chain.NewChainMakerClient(sdkConf, db)
|
|
if err == nil {
|
|
log.Printf("chain: 使用 ChainMaker 真实链后端")
|
|
return cm
|
|
}
|
|
log.Printf("chain: ChainMaker 后端不可用(%v),降级", err)
|
|
fallthrough
|
|
case "pg":
|
|
if db != nil {
|
|
if pc, err := chain.NewPersistentChain(db); err == nil {
|
|
log.Printf("chain: 使用 PostgreSQL 持久化链(写穿+水合,重启不丢数据)")
|
|
return pc
|
|
} else {
|
|
log.Printf("chain: PG 持久化链初始化失败(%v),回退内存链", err)
|
|
}
|
|
}
|
|
fallthrough
|
|
default:
|
|
log.Printf("chain: 使用内存链(仅开发用,重启丢数据)")
|
|
return chain.NewMemoryChain()
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
cfg := config.Load()
|
|
|
|
// 装配依赖:共享一个 PG 连接给链持久化与号段存储
|
|
db := openDB(cfg.PostgresDSN)
|
|
ch := newChain(cfg.ChainBackend, cfg.ChainMakerSDKConf, db)
|
|
gen := macode.NewGenerator(newAllocationStore(db))
|
|
// 示例号段(生产由与发码机构对接后配置)
|
|
// 机构节点 6101 = 陕西(管理方:陕西IPTV运营公司);行业节点 8531 = IPTV视听内容
|
|
_ = gen.RegisterSegment(macode.Segment{
|
|
IndustryNode: "8531", OrgNode: "6101",
|
|
Category: macode.CategoryMicroDrama, Start: 1, End: 9999999, SeqWidth: 7,
|
|
})
|
|
_ = gen.RegisterSegment(macode.Segment{
|
|
IndustryNode: "8531", OrgNode: "6101",
|
|
Category: macode.CategoryWebSeries, Start: 1, End: 9999999, SeqWidth: 7,
|
|
})
|
|
_ = gen.RegisterSegment(macode.Segment{
|
|
IndustryNode: "8531", OrgNode: "6101",
|
|
Category: macode.CategoryWebMovie, Start: 1, End: 9999999, SeqWidth: 7,
|
|
})
|
|
svc := service.New(ch, gen)
|
|
h := api.NewHandler(svc)
|
|
|
|
// 鉴权密钥库(MVP 预置四角色示例密钥;生产从 Vault/DB 加载)
|
|
keys := httpx.NewMemoryKeyStore()
|
|
keys.Add("ak-regulator", "sk-regulator", string(chain.RoleRegulator))
|
|
keys.Add("ak-reviewer", "sk-reviewer", string(chain.RoleReviewer))
|
|
keys.Add("ak-cp", "sk-cp", string(chain.RoleCP))
|
|
keys.Add("ak-operator", "sk-operator", string(chain.RoleOperator))
|
|
|
|
r := gin.Default()
|
|
httpx.Health(r, "api-svc")
|
|
|
|
v1 := r.Group("/api/v1", httpx.AuthMiddleware(keys))
|
|
h.Register(v1)
|
|
|
|
log.Printf("api-svc listening on %s", cfg.APIAddr)
|
|
if err := r.Run(cfg.APIAddr); err != nil {
|
|
log.Fatalf("api-svc failed: %v", err)
|
|
}
|
|
}
|