feat(phase2): 追责取证与确权举证(F19/F20)
- internal/provenance: 全链路存证(送审/审核/发码/转码/入库/注入)+责任界定 - service: Provenance/Accountability(定位首次哈希变化节点)/CopyrightEvidence/MatchInfringement - api: /content/provenance, /accountability, /evidence, /infringe-match - 转码版哈希不误判为篡改; 感知哈希侵权比对分级(high/medium) - 11项新测试通过; 端到端: 审播一致判定+证据链+侵权命中
This commit is contained in:
@@ -46,6 +46,10 @@ func (h *Handler) Register(rg *gin.RouterGroup) {
|
||||
rg.POST("/data/playback", h.reportPlayback) // 播放数据回传(需求9)
|
||||
rg.GET("/data/playback-summary", h.playbackSummary) // 按MA码聚合可信播放数据(需求9/21)
|
||||
rg.POST("/settlement/compute", h.computeSettlement) // 基于可信播放数据分账(需求21)
|
||||
rg.GET("/content/provenance", h.provenance) // 全链路存证(需求22)
|
||||
rg.GET("/content/accountability", h.accountability) // 责任界定取证(需求22)
|
||||
rg.GET("/content/evidence", h.evidence) // 版权确权证据链(需求23)
|
||||
rg.POST("/content/infringe-match", h.infringeMatch) // 感知哈希侵权比对(需求23)
|
||||
}
|
||||
|
||||
func roleOf(c *gin.Context) chain.Role {
|
||||
@@ -450,3 +454,63 @@ func (h *Handler) computeSettlement(c *gin.Context) {
|
||||
}
|
||||
httpx.OK(c, st)
|
||||
}
|
||||
|
||||
// ---- 二期:追责取证与确权举证(需求22/23) ----
|
||||
|
||||
func (h *Handler) provenance(c *gin.Context) {
|
||||
maCode := c.Query("ma_code")
|
||||
if maCode == "" {
|
||||
httpx.Error(c, http.StatusBadRequest, "INVALID_REQUEST", "缺少 ma_code")
|
||||
return
|
||||
}
|
||||
httpx.OK(c, gin.H{"ma_code": maCode, "trail": h.svc.Provenance(maCode)})
|
||||
}
|
||||
|
||||
func (h *Handler) accountability(c *gin.Context) {
|
||||
maCode := c.Query("ma_code")
|
||||
if maCode == "" {
|
||||
httpx.Error(c, http.StatusBadRequest, "INVALID_REQUEST", "缺少 ma_code")
|
||||
return
|
||||
}
|
||||
httpx.OK(c, h.svc.Accountability(maCode))
|
||||
}
|
||||
|
||||
func (h *Handler) evidence(c *gin.Context) {
|
||||
maCode := c.Query("ma_code")
|
||||
if maCode == "" {
|
||||
httpx.Error(c, http.StatusBadRequest, "INVALID_REQUEST", "缺少 ma_code")
|
||||
return
|
||||
}
|
||||
ev, err := h.svc.CopyrightEvidence(maCode)
|
||||
if err != nil {
|
||||
httpx.Error(c, http.StatusNotFound, "NOT_FOUND", err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OK(c, ev)
|
||||
}
|
||||
|
||||
type infringeReq struct {
|
||||
Perceptual string `json:"perceptual_hash"`
|
||||
High int `json:"high_threshold"`
|
||||
Medium int `json:"medium_threshold"`
|
||||
}
|
||||
|
||||
func (h *Handler) infringeMatch(c *gin.Context) {
|
||||
var req infringeReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Error(c, http.StatusBadRequest, "INVALID_REQUEST", err.Error())
|
||||
return
|
||||
}
|
||||
if req.High == 0 {
|
||||
req.High = 5
|
||||
}
|
||||
if req.Medium == 0 {
|
||||
req.Medium = 10
|
||||
}
|
||||
matches, err := h.svc.MatchInfringement(req.Perceptual, req.High, req.Medium)
|
||||
if err != nil {
|
||||
httpx.Error(c, http.StatusBadRequest, "MATCH_FAILED", err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OK(c, gin.H{"matches": matches, "count": len(matches)})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user