267 lines
11 KiB
Go
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
|
|
}
|