Files
im_manager/server/internal/logic/sys/im_sensitive_word.go
T

267 lines
11 KiB
Go

package sys
import (
"context"
"crypto/sha256"
"encoding/hex"
"strings"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/library/contexts"
"hotgo/internal/library/imtenant"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
type sSysImSensitiveWord struct{}
func NewSysImSensitiveWord() *sSysImSensitiveWord { return &sSysImSensitiveWord{} }
func init() { service.RegisterSysImSensitiveWord(NewSysImSensitiveWord()) }
func normalizedSensitiveWord(value string) (string, string) {
normalized := strings.ToLower(strings.TrimSpace(value))
sum := sha256.Sum256([]byte(normalized))
return normalized, hex.EncodeToString(sum[:])
}
func (s *sSysImSensitiveWord) List(ctx context.Context, in *sysin.ImSensitiveWordListInp) (list []*sysin.ImSensitiveWordListModel, total int, err error) {
if err = in.Filter(ctx); err != nil {
return
}
tenantID, _, err := resolveImPolicyTenant(ctx, in.TenantId)
if err != nil {
return nil, 0, err
}
c := dao.ImSensitiveWords.Columns()
gc := dao.ImSensitiveWordGroups.Columns()
model := imtenant.RegistryDB().Model(dao.ImSensitiveWords.Table()+" w").Safe().Ctx(ctx).LeftJoin(dao.ImSensitiveWordGroups.Table()+" g", "g."+gc.Id+"=w."+c.GroupId).Fields("w."+c.Id, "w."+c.TenantId, "w."+c.GroupId, "g."+gc.GroupName, "w."+c.Word, "w."+c.MatchType, "CAST(w."+c.SceneJson+" AS CHAR) scene_json", "w."+c.Action, "w."+c.Replacement, "w."+c.RiskLevel, "w."+c.Priority, "w."+c.Status, "w."+c.Remark, "w."+c.CreatedAt, "w."+c.UpdatedAt).Where("w."+c.TenantId, tenantID).WhereNull("w." + c.DeletedAt)
if in.GroupId > 0 {
model = model.Where("w."+c.GroupId, in.GroupId)
}
if in.Word != "" {
model = model.WhereLike("w."+c.Word, "%"+in.Word+"%")
}
if in.MatchType != "" {
model = model.Where("w."+c.MatchType, in.MatchType)
}
if in.Action != "" {
model = model.Where("w."+c.Action, in.Action)
}
if in.RiskLevel != "" {
model = model.Where("w."+c.RiskLevel, in.RiskLevel)
}
if in.Status != 0 {
model = model.Where("w."+c.Status, in.Status)
}
err = model.Page(in.Page, in.PerPage).OrderDesc("w."+c.Priority).OrderDesc("w."+c.Id).ScanAndCount(&list, &total, false)
if err != nil {
err = gerror.Wrap(err, "获取敏感词列表失败")
}
return
}
func (s *sSysImSensitiveWord) Edit(ctx context.Context, in *sysin.ImSensitiveWordEditInp) error {
if err := in.Filter(ctx); err != nil {
return err
}
tenantID, tenantCode, err := resolveImPolicyTenant(ctx, in.TenantId)
if err != nil {
return err
}
if err = validateSensitiveWordGroup(ctx, tenantID, in.GroupId); err != nil {
return err
}
_, hash := normalizedSensitiveWord(in.Word)
c := dao.ImSensitiveWords.Columns()
db := imtenant.RegistryDB()
now := gtime.Now()
data := g.Map{c.TenantId: tenantID, c.GroupId: in.GroupId, c.Word: in.Word, c.WordHash: hash, c.MatchType: in.MatchType, c.SceneJson: policyJSON(in.SceneJson, "[]"), c.Action: in.Action, c.Replacement: in.Replacement, c.RiskLevel: in.RiskLevel, c.Priority: in.Priority, c.Status: in.Status, c.Remark: in.Remark, c.UpdatedAt: now, c.UpdatedBy: contexts.GetUserId(ctx)}
targetID := in.Id
if in.Id > 0 {
var row gdb.Record
if row, err = db.Model(dao.ImSensitiveWords.Table()).Safe().Ctx(ctx).Where(c.Id, in.Id).WhereNull(c.DeletedAt).One(); err != nil {
return err
}
if row.IsEmpty() || row[c.TenantId].Int64() != tenantID {
return gerror.New("敏感词不存在")
}
_, err = db.Model(dao.ImSensitiveWords.Table()).Safe().Ctx(ctx).Where(c.Id, in.Id).Data(data).Update()
} else {
data[c.CreatedAt] = now
data[c.CreatedBy] = contexts.GetUserId(ctx)
result, insertErr := db.Model(dao.ImSensitiveWords.Table()).Safe().Ctx(ctx).Data(data).Insert()
err = insertErr
if err == nil {
targetID, err = result.LastInsertId()
}
}
if err != nil {
return gerror.Wrap(err, "保存敏感词失败,请检查是否重复")
}
_ = writeImAdminAudit(ctx, tenantID, "im_sensitive_word_edit", "im_sensitive_word", targetID, "success", g.Map{"group_id": in.GroupId, "action": in.Action})
return publishImPolicyEvent(ctx, tenantID, tenantCode, "sensitive_words.updated", "sensitive_words", uint(targetID))
}
func (s *sSysImSensitiveWord) Delete(ctx context.Context, in *sysin.ImSensitiveWordDeleteInp) error {
if err := in.Filter(ctx); err != nil {
return err
}
return s.updateWordState(ctx, in.Id, consts.StatusDisable, true, "im_sensitive_word_delete")
}
func (s *sSysImSensitiveWord) Status(ctx context.Context, in *sysin.ImSensitiveWordStatusInp) error {
if err := in.Filter(ctx); err != nil {
return err
}
return s.updateWordState(ctx, in.Id, in.Status, false, "im_sensitive_word_status")
}
func (s *sSysImSensitiveWord) updateWordState(ctx context.Context, id int64, status int, deleted bool, action string) error {
c := dao.ImSensitiveWords.Columns()
db := imtenant.RegistryDB()
var row gdb.Record
var err error
if row, err = db.Model(dao.ImSensitiveWords.Table()).Safe().Ctx(ctx).Where(c.Id, id).WhereNull(c.DeletedAt).One(); err != nil {
return err
}
if row.IsEmpty() {
return gerror.New("敏感词不存在")
}
tenantID, tenantCode, err := resolveImPolicyTenant(ctx, row[c.TenantId].Int64())
if err != nil {
return err
}
data := g.Map{c.Status: status, c.UpdatedAt: gtime.Now(), c.UpdatedBy: contexts.GetUserId(ctx)}
if deleted {
data[c.DeletedAt] = gtime.Now()
data[c.DeletedBy] = contexts.GetUserId(ctx)
}
if _, err = db.Model(dao.ImSensitiveWords.Table()).Safe().Ctx(ctx).Where(c.Id, id).Data(data).Update(); err != nil {
return gerror.Wrap(err, "更新敏感词失败")
}
_ = writeImAdminAudit(ctx, tenantID, action, "im_sensitive_word", id, "success", g.Map{"status": status})
return publishImPolicyEvent(ctx, tenantID, tenantCode, "sensitive_words.updated", "sensitive_words", uint(id))
}
func (s *sSysImSensitiveWord) BatchImport(ctx context.Context, in *sysin.ImSensitiveWordBatchImportInp) (res *sysin.ImSensitiveWordBatchImportModel, err error) {
if err = in.Filter(ctx); err != nil {
return
}
tenantID, tenantCode, err := resolveImPolicyTenant(ctx, in.TenantId)
if err != nil {
return nil, err
}
if err = validateSensitiveWordGroup(ctx, tenantID, in.GroupId); err != nil {
return nil, err
}
res = &sysin.ImSensitiveWordBatchImportModel{}
db := imtenant.RegistryDB()
c := dao.ImSensitiveWords.Columns()
seen := map[string]struct{}{}
err = db.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
for _, raw := range in.Words {
word, hash := normalizedSensitiveWord(raw)
if word == "" {
res.Skipped++
continue
}
if _, ok := seen[hash]; ok {
res.Skipped++
continue
}
seen[hash] = struct{}{}
count, countErr := tx.Model(dao.ImSensitiveWords.Table()).Safe().Ctx(ctx).Where(c.TenantId, tenantID).Where(c.GroupId, in.GroupId).Where(c.WordHash, hash).WhereNull(c.DeletedAt).Count()
if countErr != nil {
return countErr
}
if count > 0 {
res.Skipped++
continue
}
now := gtime.Now()
_, insertErr := tx.Model(dao.ImSensitiveWords.Table()).Safe().Ctx(ctx).Data(g.Map{c.TenantId: tenantID, c.GroupId: in.GroupId, c.Word: strings.TrimSpace(raw), c.WordHash: hash, c.MatchType: in.MatchType, c.SceneJson: policyJSON(in.SceneJson, "[]"), c.Action: in.Action, c.Replacement: in.Replacement, c.RiskLevel: in.RiskLevel, c.Status: consts.StatusEnabled, c.CreatedAt: now, c.CreatedBy: contexts.GetUserId(ctx), c.UpdatedAt: now, c.UpdatedBy: contexts.GetUserId(ctx)}).Insert()
if insertErr != nil {
return insertErr
}
res.Inserted++
}
return nil
})
if err != nil {
return nil, gerror.Wrap(err, "批量导入敏感词失败")
}
_ = writeImAdminAudit(ctx, tenantID, "im_sensitive_word_batch_import", "im_sensitive_word", 0, "success", g.Map{"inserted": res.Inserted, "skipped": res.Skipped})
err = publishImPolicyEvent(ctx, tenantID, tenantCode, "sensitive_words.updated", "sensitive_words", uint(res.Inserted))
return
}
func (s *sSysImSensitiveWord) GroupList(ctx context.Context, in *sysin.ImSensitiveWordGroupListInp) (list []*sysin.ImSensitiveWordGroupModel, err error) {
if err = in.Filter(ctx); err != nil {
return
}
tenantID, _, err := resolveImPolicyTenant(ctx, in.TenantId)
if err != nil {
return nil, err
}
c := dao.ImSensitiveWordGroups.Columns()
err = imtenant.RegistryDB().Model(dao.ImSensitiveWordGroups.Table()).Safe().Ctx(ctx).Fields(c.Id, c.TenantId, c.GroupCode, c.GroupName, c.Priority, c.Status, c.Remark, c.CreatedAt).Where(c.TenantId, tenantID).WhereNull(c.DeletedAt).OrderDesc(c.Priority).OrderAsc(c.GroupName).Scan(&list)
return
}
func (s *sSysImSensitiveWord) GroupEdit(ctx context.Context, in *sysin.ImSensitiveWordGroupEditInp) error {
if err := in.Filter(ctx); err != nil {
return err
}
tenantID, tenantCode, err := resolveImPolicyTenant(ctx, in.TenantId)
if err != nil {
return err
}
c := dao.ImSensitiveWordGroups.Columns()
db := imtenant.RegistryDB()
now := gtime.Now()
data := g.Map{c.TenantId: tenantID, c.GroupCode: in.GroupCode, c.GroupName: in.GroupName, c.Priority: in.Priority, c.Status: in.Status, c.Remark: in.Remark, c.UpdatedAt: now, c.UpdatedBy: contexts.GetUserId(ctx)}
targetID := in.Id
if in.Id > 0 {
var row gdb.Record
if row, err = db.Model(dao.ImSensitiveWordGroups.Table()).Safe().Ctx(ctx).Where(c.Id, in.Id).WhereNull(c.DeletedAt).One(); err != nil {
return err
}
if row.IsEmpty() || row[c.TenantId].Int64() != tenantID {
return gerror.New("敏感词分组不存在")
}
if row[c.GroupCode].String() != in.GroupCode {
return gerror.New("分组编码创建后不能修改")
}
_, err = db.Model(dao.ImSensitiveWordGroups.Table()).Safe().Ctx(ctx).Where(c.Id, in.Id).Data(data).Update()
} else {
data[c.CreatedAt] = now
data[c.CreatedBy] = contexts.GetUserId(ctx)
result, insertErr := db.Model(dao.ImSensitiveWordGroups.Table()).Safe().Ctx(ctx).Data(data).Insert()
err = insertErr
if err == nil {
targetID, err = result.LastInsertId()
}
}
if err != nil {
return gerror.Wrap(err, "保存敏感词分组失败")
}
_ = writeImAdminAudit(ctx, tenantID, "im_sensitive_word_group_edit", "im_sensitive_word_group", targetID, "success", nil)
return publishImPolicyEvent(ctx, tenantID, tenantCode, "sensitive_words.updated", "sensitive_words", uint(targetID))
}
func validateSensitiveWordGroup(ctx context.Context, tenantID, groupID int64) error {
c := dao.ImSensitiveWordGroups.Columns()
count, err := imtenant.RegistryDB().Model(dao.ImSensitiveWordGroups.Table()).Safe().Ctx(ctx).Where(c.Id, groupID).Where(c.TenantId, tenantID).WhereNull(c.DeletedAt).Count()
if err != nil {
return gerror.Wrap(err, "检查敏感词分组失败")
}
if count == 0 {
return gerror.New("敏感词分组不存在")
}
return nil
}