package model import ( "context" "time" modentity "spark-server/internal/module/moderation/entity" modlogic "spark-server/internal/module/moderation/logic" "spark-server/internal/pkg/sensitive" ) // LogicBridge adapts Repo to moderation/logic.Repository. type LogicBridge struct { *Repo } func NewLogicBridge(r *Repo) *LogicBridge { if r == nil { return nil } return &LogicBridge{Repo: r} } func (b *LogicBridge) LookupWord(id int64) *modentity.Word { if b == nil || b.matcher == nil { return nil } row := b.matcher.WordByID(id) if row == nil { return nil } return row.entity() } func (b *LogicBridge) MatcherFind(text string) ([]modlogic.MatchDetail, string, bool) { m := b.matcher.Get() idx := sensitive.FoldAndIndex(text) if m == nil || len(idx.Norm) == 0 { return nil, text, true } rawMatches := m.FindAll(idx.Norm) if len(rawMatches) == 0 { return nil, text, true } intervals, ok := sensitive.IntervalsFromMatches(&idx, rawMatches) masked := text if ok { masked = sensitive.Mask(idx.OrigBytes, intervals, "***") } details := make([]modlogic.MatchDetail, 0, len(rawMatches)) for _, rm := range rawMatches { row := b.matcher.WordByID(rm.EntryID) if row == nil { continue } ent := row.entity() from, to := 0, 0 if ok && rm.Start >= 0 && rm.End <= len(idx.OrigFrom) { from = idx.OrigFrom[rm.Start] to = idx.OrigTo[rm.End-1] } ex := sensitive.Excerpt(idx.OrigBytes, from, to, 128) details = append(details, modlogic.MatchDetail{ WordID: ent.ID, Category: ent.Category, Action: ent.Action, Excerpt: ex, From: from, To: to, }) } if len(details) == 0 { return nil, text, true } return details, masked, ok } func (b *LogicBridge) UpdateWord(ctx context.Context, id int64, patch modlogic.WordPatch) error { return b.Repo.UpdateWord(ctx, id, func(row *WordRow) error { if patch == nil { return nil } ent := row.entity() if err := patch(ent); err != nil { return err } row.WordRaw = ent.WordRaw row.Word = ent.Word row.Category = int8(ent.Category) row.Action = int8(ent.Action) row.MuteSeconds = ent.MuteSeconds row.Enabled = boolToTiny(ent.Enabled) return nil }) } func (b *LogicBridge) InsertHits(ctx context.Context, hits []modlogic.HitRecord) error { rows := make([]HitRow, 0, len(hits)) for i := range hits { h := hits[i] rows = append(rows, hitFromEntity(h.ID, h.UserID, h.ConversationID, modentity.HitMatch{ WordID: h.WordID, Category: h.Category, Action: h.Action, Excerpt: h.Excerpt, }, h.RealIP, h.CreatedAt)) } return b.Repo.InsertHits(ctx, rows) } func (b *LogicBridge) ListHits(ctx context.Context, userID int64, category int, since, until *time.Time, page, size int) ([]modlogic.HitRecord, int64, error) { rows, total, err := b.Repo.ListHits(ctx, userID, category, since, until, page, size) if err != nil { return nil, 0, err } out := make([]modlogic.HitRecord, 0, len(rows)) for i := range rows { r := rows[i] out = append(out, modlogic.HitRecord{ ID: r.ID, UserID: r.UserID, ConversationID: r.ConversationID, WordID: r.WordID, Category: modentity.Category(r.Category), Action: modentity.Action(r.Action), Excerpt: r.Excerpt, RealIP: r.RealIP, CreatedAt: r.CreatedAt, }) } return out, total, nil }