211 lines
4.5 KiB
Go
211 lines
4.5 KiB
Go
package model
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
|
|
mysqldriver "github.com/go-sql-driver/mysql"
|
|
"gorm.io/gorm"
|
|
|
|
modentity "spark-server/internal/module/moderation/entity"
|
|
"spark-server/internal/pkg/errs"
|
|
)
|
|
|
|
type Repo struct {
|
|
db *gorm.DB
|
|
matcher *MatcherStore
|
|
}
|
|
|
|
func NewRepo(db *gorm.DB) *Repo {
|
|
if db == nil {
|
|
return nil
|
|
}
|
|
return &Repo{db: db, matcher: NewMatcherStore()}
|
|
}
|
|
|
|
func (r *Repo) Ready() bool {
|
|
return r != nil && r.db != nil
|
|
}
|
|
|
|
func (r *Repo) Matcher() *MatcherStore {
|
|
if r == nil {
|
|
return nil
|
|
}
|
|
return r.matcher
|
|
}
|
|
|
|
func (r *Repo) ReloadMatcher(ctx context.Context) error {
|
|
if !r.Ready() {
|
|
return errs.ErrAvailable
|
|
}
|
|
var rows []WordRow
|
|
if err := r.db.WithContext(ctx).Where("enabled = ?", 1).Find(&rows).Error; err != nil {
|
|
return err
|
|
}
|
|
r.matcher.Replace(rows)
|
|
return nil
|
|
}
|
|
|
|
func (r *Repo) ListWords(ctx context.Context, q string, category int, enabled *bool, page, size int) ([]modentity.Word, int64, error) {
|
|
if !r.Ready() {
|
|
return nil, 0, errs.ErrAvailable
|
|
}
|
|
if size <= 0 || size > 200 {
|
|
size = 50
|
|
}
|
|
if page < 0 {
|
|
page = 0
|
|
}
|
|
db := r.db.WithContext(ctx).Model(&WordRow{})
|
|
q = trim(q)
|
|
if q != "" {
|
|
like := "%" + q + "%"
|
|
db = db.Where("word_raw LIKE ? OR word LIKE ?", like, like)
|
|
}
|
|
if category > 0 {
|
|
db = db.Where("category = ?", category)
|
|
}
|
|
if enabled != nil {
|
|
if *enabled {
|
|
db = db.Where("enabled = ?", 1)
|
|
} else {
|
|
db = db.Where("enabled = ?", 0)
|
|
}
|
|
}
|
|
var total int64
|
|
if err := db.Count(&total).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
var rows []WordRow
|
|
if err := db.Order("id DESC").Offset(page * size).Limit(size).Find(&rows).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
out := make([]modentity.Word, 0, len(rows))
|
|
for i := range rows {
|
|
out = append(out, *rows[i].entity())
|
|
}
|
|
return out, total, nil
|
|
}
|
|
|
|
func (r *Repo) CreateWord(ctx context.Context, w *modentity.Word) error {
|
|
if !r.Ready() || w == nil || w.Word == "" {
|
|
return errs.ErrInvalidArgument
|
|
}
|
|
now := time.Now()
|
|
row := WordRow{
|
|
WordRaw: w.WordRaw, Word: w.Word, Category: int8(w.Category), Action: int8(w.Action),
|
|
MuteSeconds: w.MuteSeconds, Enabled: boolToTiny(w.Enabled), CreatedAt: now, UpdatedAt: now,
|
|
}
|
|
if err := r.db.WithContext(ctx).Create(&row).Error; err != nil {
|
|
return mapDB(err)
|
|
}
|
|
w.ID = row.ID
|
|
w.CreatedAt = now
|
|
w.UpdatedAt = now
|
|
return r.ReloadMatcher(ctx)
|
|
}
|
|
|
|
func (r *Repo) UpdateWord(ctx context.Context, id int64, patch func(*WordRow) error) error {
|
|
if !r.Ready() || id <= 0 {
|
|
return errs.ErrInvalidArgument
|
|
}
|
|
var row WordRow
|
|
if err := r.db.WithContext(ctx).Where("id = ?", id).Take(&row).Error; err != nil {
|
|
return mapDB(err)
|
|
}
|
|
if patch != nil {
|
|
if err := patch(&row); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
row.UpdatedAt = time.Now()
|
|
if err := r.db.WithContext(ctx).Save(&row).Error; err != nil {
|
|
return mapDB(err)
|
|
}
|
|
return r.ReloadMatcher(ctx)
|
|
}
|
|
|
|
func (r *Repo) SoftDeleteWord(ctx context.Context, id int64) error {
|
|
return r.UpdateWord(ctx, id, func(row *WordRow) error {
|
|
row.Enabled = 0
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func (r *Repo) InsertHits(ctx context.Context, rows []HitRow) error {
|
|
if !r.Ready() || len(rows) == 0 {
|
|
return nil
|
|
}
|
|
return r.db.WithContext(ctx).Create(&rows).Error
|
|
}
|
|
|
|
func (r *Repo) ListHits(ctx context.Context, userID int64, category int, since, until *time.Time, page, size int) ([]HitRow, int64, error) {
|
|
if !r.Ready() {
|
|
return nil, 0, errs.ErrAvailable
|
|
}
|
|
if size <= 0 || size > 200 {
|
|
size = 50
|
|
}
|
|
if page < 0 {
|
|
page = 0
|
|
}
|
|
db := r.db.WithContext(ctx).Model(&HitRow{})
|
|
if userID > 0 {
|
|
db = db.Where("user_id = ?", userID)
|
|
}
|
|
if category > 0 {
|
|
db = db.Where("category = ?", category)
|
|
}
|
|
if since != nil {
|
|
db = db.Where("created_at >= ?", *since)
|
|
}
|
|
if until != nil {
|
|
db = db.Where("created_at <= ?", *until)
|
|
}
|
|
var total int64
|
|
if err := db.Count(&total).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
var rows []HitRow
|
|
if err := db.Order("created_at DESC").Offset(page * size).Limit(size).Find(&rows).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
return rows, total, nil
|
|
}
|
|
|
|
func boolToTiny(v bool) int8 {
|
|
if v {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func trim(s string) string {
|
|
for len(s) > 0 && (s[0] == ' ' || s[0] == '\t') {
|
|
s = s[1:]
|
|
}
|
|
for len(s) > 0 {
|
|
last := s[len(s)-1]
|
|
if last != ' ' && last != '\t' {
|
|
break
|
|
}
|
|
s = s[:len(s)-1]
|
|
}
|
|
return s
|
|
}
|
|
|
|
func mapDB(err error) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return errs.ErrNotFound
|
|
}
|
|
var me *mysqldriver.MySQLError
|
|
if errors.As(err, &me) && me.Number == 1062 {
|
|
return errs.ErrDuplicate
|
|
}
|
|
return err
|
|
}
|