0%

cc-develop-note-v

新闻敏感词过滤

image-20201227211648167

新建数据库表 cc_news_sensitive,在后端依次匹配并替换敏感文本内容为“**”,在前端显示出来,没有敏感词后再上传保存。

注意附件上传时的id及对应关系,不要重复上传

feat-filter-news-sensitive-words 分支

(暂时只处理”新建“,不处理”新增“)

后端

model:backend/modules/api/v1/models/news/NewsSensitiveModel.php

关键词匹配

  • 遍历文本
  • 正则表达式
  • DFA算法。前两种方法当需要匹配的关键词数量增大时,效率很低。Deterministic Finite automation,确定性的有穷状态自动机。从一个状态输入一个字符集合能到达下一个确定的状态。https://github.com/FireLustre/php-dfa-sensitive

mb_substr() 函数返回字符串的一部分, substr() 函数只针对英文字符,如果要分割的中文文字则需要使用 mb_substr()。

backend/modules/api/v1/models/news/NewsModel.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 过滤新闻中的敏感词
public static function filterNews($data)
{
$db=Yii::$app->db;
$sql="SELECT cc_news_sensitive.WordContent FROM cc_news_sensitive";
$words=$db->createCommand($sql)->queryAll();
$sensitiveArr=[];
foreach ($words as $word) {
$sensitiveArr[]=$word['WordContent'];
}
$res['sensitive']=false;
$content=$data['Content'];

// 构建敏感词库树
$handle = SensitiveHelper::init()->setTree($sensitiveArr);
// 检测是否含有敏感词
$isLegal = $handle->islegal($content);
if($isLegal===true)
{
// 含有敏感词
$res['sensitive']=true;
// 敏感词替换为**
$filterContent = $handle->replace($content, '**');
$res['newContent']=$filterContent;
}
return $res;
}

backend/modules/api/v1/controllers/NewsController.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//添加新闻
public function actionPostNews()
{
$request = \Yii::$app->request;
if ($request->getIsOptions()) {
return $this->ResponseOptions($this->verbs()['post-news']);
}
$data = $request->getBodyParams();
$filterRes=NewsModel::filterNews($data);


if($filterRes['sensitive']===true)
{
$res['success'] = false;
$res['sensitive']=true;
$res['newContent']=$filterRes['newContent'];
$res['data']=$data;
return $res;
}
date_default_timezone_set('PRC');
$data['PublishTime'] = date('Y-m-d H:i:s', time());
$data['UpdateTime'] = $data['PublishTime'];
$data['DeleteStatus'] = 1;

$res=NewsModel::postNews($data);
$res['sensitive']=false;
return $res;
}

前端

src\views\news\NewsCompose.vue 的 postNews()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
.then((res) => {
if (res.data.success === true) {
// 添加新闻之后,服务器返回新闻ID,传给对应的上传组件
this.NIDForm.NID = res.data.data.NID;
// 上传附件操作
this.$refs.upload.submit();
this.$message({
type: "success",
message: "发布成功!",
});
this.newsJumpTip = false;
this.goToNewsList();
} else {
if (res.data.sensitive === true) {
// 含有敏感词
this.$message({
type: "info",
message: "已将敏感词替换为**,请检查后重新上传!",
});
// 重新显示修改后的内容
this.newsForm.newsContent = res.data.newContent;
this.$refs.tinymce.setContent(this.newsForm.newsContent);
} else {
this.$message({
type: "error",
message: "发布失败,请检查信息是否都已正确填写!",
});
}
}
})

数据修改后没有在视图中同时显示,因此使用 this.$refs 获取 tinymce 组件,调用 setContent() 方法手动更新。