spark-server/internal/module/moderation/model/bridge.go
2026-09-29 16:37:36 +08:00

120 lines
3.2 KiB
Go

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
}