在Fastadmin框架中实现敏感词管理笔记
目录
安装依赖库
需要使用overtrue/pinyin依赖库实现中文分词,安装命令:
bash
composer require overtrue/pinyin
安装版本如下:

创建数据表
设计数据表
sql
CREATE TABLE `fa_sensitive_words` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键 ID',
`word` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '敏感词内容',
`category` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT 'general' COMMENT '敏感词分类:general=通用,politics=政治,porn=色情,violence=暴力,ad=广告',
`level` tinyint(1) NOT NULL DEFAULT '1' COMMENT '敏感等级:1=低,2=中,3=高',
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态:0=禁用,1=启用',
`replace_char` varchar(10) COLLATE utf8mb4_unicode_ci DEFAULT '*' COMMENT '替换字符,默认*',
`description` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注说明',
`created_at` datetime NOT NULL COMMENT '创建时间',
`updated_at` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `idx_word_unique` (`word`) USING BTREE,
KEY `idx_category` (`category`) USING BTREE,
KEY `idx_status` (`status`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='敏感词库表';
插入测试数据
sql
INSERT INTO `fa_sensitive_words` (`id`, `word`, `category`, `level`, `status`, `replace_char`, `description`, `created_at`, `updated_at`) VALUES (1, '敏感词', 'general', 1, 1, '*', '测试敏感词', '2026-04-01 09:00:30', '2026-04-01 09:00:30');
INSERT INTO `fa_sensitive_words` (`id`, `word`, `category`, `level`, `status`, `replace_char`, `description`, `created_at`, `updated_at`) VALUES (2, '广告', 'ad', 2, 1, '*', '广告类敏感词', '2026-04-01 09:00:30', '2026-04-01 09:00:30');
INSERT INTO `fa_sensitive_words` (`id`, `word`, `category`, `level`, `status`, `replace_char`, `description`, `created_at`, `updated_at`) VALUES (3, '暴力', 'violence', 3, 1, '*', '暴力类敏感词', '2026-04-01 09:00:30', '2026-04-01 09:00:30');
INSERT INTO `fa_sensitive_words` (`id`, `word`, `category`, `level`, `status`, `replace_char`, `description`, `created_at`, `updated_at`) VALUES (4, '政治敏感', 'politics', 3, 1, '*', '政治类敏感词', '2026-04-01 09:00:30', '2026-04-01 09:00:30');
INSERT INTO `fa_sensitive_words` (`id`, `word`, `category`, `level`, `status`, `replace_char`, `description`, `created_at`, `updated_at`) VALUES (5, '色情', 'porn', 3, 1, '*', '色情类敏感词', '2026-04-01 09:00:30', '2026-04-01 09:00:30');
创建模型类
在common/model下创建SensitiveWord.php文件,内容如下:
php
<?php
namespace app\common\model;
use think\Model;
/**
* 敏感词模型
*/
class SensitiveWord extends Model
{
// 设置当前模型对应的完整表名
protected $name = 'sensitive_words';
// 定义主键
protected $pk = 'id';
// 自动写入时间戳
protected $autoWriteTimestamp = true;
// 时间戳字段名
protected $createTime = 'created_at';
protected $updateTime = 'updated_at';
// 字段类型转换
protected $type = [
'id' => 'integer',
'word' => 'string',
'category' => 'string',
'level' => 'integer',
'status' => 'integer',
'replace_char' => 'string',
'description' => 'string',
];
// 字段验证规则
protected $validate = [
'word' => 'require|max:100',
'category' => 'max:50',
'level' => 'number|between:1,3',
'status' => 'number|in:0,1',
];
// 允许自动填充的字段列表
protected $insert = ['status' => 1];
/**
* 获取所有启用的敏感词列表
* @return array
*/
public static function getEnabledWords()
{
return self::where('status', 1)
->column('word');
}
/**
* 根据分类获取敏感词
* @param string $category 分类
* @return array
*/
public static function getWordsByCategory($category)
{
return self::where('status', 1)
->where('category', $category)
->column('word');
}
/**
* 检查词是否敏感
* @param string $word 要检查的词
* @return bool
*/
public static function isSensitive($word)
{
return self::where('status', 1)
->where('word', $word)
->find() ? true : false;
}
/**
* 添加敏感词
* @param string $word 敏感词
* @param string $category 分类
* @param int $level 等级
* @param string $description 描述
* @return bool
*/
public static function addWord($word, $category = 'general', $level = 1, $description = '')
{
$data = [
'word' => trim($word),
'category' => $category,
'level' => $level,
'description' => $description,
];
$result = self::create($data);
return $result ? true : false;
}
/**
* 批量添加敏感词
* @param array $words 敏感词数组
* @param string $category 分类
* @return int 成功添加的数量
*/
public static function addWordsBatch($words, $category = 'general')
{
$count = 0;
foreach ($words as $word) {
if (self::addWord($word, $category)) {
$count++;
}
}
return $count;
}
/**
* 删除敏感词
* @param int $id 敏感词 ID
* @return bool
*/
public static function deleteWord($id)
{
return self::destroy($id);
}
/**
* 更新敏感词状态
* @param int $id 敏感词 ID
* @param int $status 状态
* @return bool
*/
public static function updateStatus($id, $status)
{
return self::update(['id' => $id, 'status' => $status]);
}
}
创建公共方法
在application/common.php文件中增加对敏感词的检测、过滤方法,以供全局调用,
内容如下:
php
if (!function_exists('filter_sensitive_words')) {
/**
* 敏感词过滤函数
* 对输入内容进行分词,查询敏感词库,若存在则对输入内容进行替换为*处理并返回
* @param string $content 待过滤的内容
* @param string $replaceChar 替换字符,默认*
* @param bool $useCache 是否使用缓存,默认 true
* @return array 返回过滤后的内容和是否包含敏感词
*/
function filter_sensitive_words($content, $replaceChar = '*', $useCache = true)
{
if (empty($content)) {
return [
'content' => $content,
'has_sensitive' => false,
'filtered_count' => 0
];
}
static $sensitiveWordsCache = null;
// 使用缓存获取敏感词列表
if ($useCache && $sensitiveWordsCache === null) {
$sensitiveWordsCache = \app\common\model\SensitiveWord::getEnabledWords();
} elseif (!$useCache) {
$sensitiveWordsCache = \app\common\model\SensitiveWord::getEnabledWords();
}
if (empty($sensitiveWordsCache)) {
return [
'content' => $content,
'has_sensitive' => false,
'filtered_count' => 0
];
}
$filteredCount = 0;
$filteredContent = $content;
// 按长度降序排序,优先替换长词
usort($sensitiveWordsCache, function($a, $b) {
return mb_strlen($b) - mb_strlen($a);
});
// 逐个替换敏感词
foreach ($sensitiveWordsCache as $word) {
if (mb_stripos($filteredContent, $word) !== false) {
$replaceStr = str_repeat($replaceChar, mb_strlen($word));
$filteredContent = str_ireplace($word, $replaceStr, $filteredContent);
$filteredCount++;
}
}
return [
'content' => $filteredContent,
'has_sensitive' => $filteredCount > 0,
'filtered_count' => $filteredCount
];
}
}
if (!function_exists('check_sensitive_words')) {
/**
* 检查内容是否包含敏感词
* @param string $content 待检查的内容
* @param bool $useCache 是否使用缓存,默认 true
* @return array 返回检查结果和敏感词列表
*/
function check_sensitive_words($content, $useCache = true)
{
if (empty($content)) {
return [
'has_sensitive' => false,
'sensitive_words' => []
];
}
static $sensitiveWordsCache = null;
if ($useCache && $sensitiveWordsCache === null) {
$sensitiveWordsCache = \app\common\model\SensitiveWord::getEnabledWords();
} elseif (!$useCache) {
$sensitiveWordsCache = \app\common\model\SensitiveWord::getEnabledWords();
}
if (empty($sensitiveWordsCache)) {
return [
'has_sensitive' => false,
'sensitive_words' => []
];
}
$foundSensitiveWords = [];
foreach ($sensitiveWordsCache as $word) {
if (mb_stripos($content, $word) !== false) {
$foundSensitiveWords[] = $word;
}
}
return [
'has_sensitive' => !empty($foundSensitiveWords),
'sensitive_words' => $foundSensitiveWords
];
}
}
if (!function_exists('segment_text')) {
/**
* 对文本进行分词(使用 overtrue/pinyin)
* @param string $text 待分词的文本
* @param int $minLength 最小词长度,默认 2
* @return array 分词结果
*/
function segment_text($text, $minLength = 2)
{
if (empty($text)) {
return [];
}
$pinyin = new \Overtrue\Pinyin\Pinyin();
$segments = [];
// 移除特殊字符和空白
$text = preg_replace('/[^\x{4e00}-\x{9fa5}a-zA-Z0-9]/u', ' ', $text);
$text = preg_replace('/\s+/', ' ', trim($text));
if (empty($text)) {
return [];
}
// 简单分词:按空格分割
$words = explode(' ', $text);
foreach ($words as $word) {
if (mb_strlen($word) >= $minLength) {
$segments[] = $word;
}
}
return $segments;
}
}
if (!function_exists('filter_and_segment_text')) {
/**
* 先过滤敏感词再分词
* @param string $content 待处理的内容
* @param string $replaceChar 替换字符
* @param int $minLength 最小词长度
* @return array 处理结果
*/
function filter_and_segment_text($content, $replaceChar = '*', $minLength = 2)
{
// 先过滤敏感词
$filterResult = filter_sensitive_words($content, $replaceChar);
// 再分词
$segments = segment_text($filterResult['content'], $minLength);
return [
'original_content' => $content,
'filtered_content' => $filterResult['content'],
'has_sensitive' => $filterResult['has_sensitive'],
'filtered_count' => $filterResult['filtered_count'],
'segments' => $segments
];
}
}
业务调用
在业务中进行调用,分为两类:
- 对于不允许输入敏感词的场景,使用检测方法,给与用户提示并展示相应敏感词;
- 对于允许输入敏感词的场景,对检测到的敏感词进行相应静默过滤处理。
不允许输入敏感词
检测并给与用户提示。
php
// 检测是否有敏感词
$sensitiveWords = check_sensitive_words($nickname);
if ($sensitiveWords['has_sensitive']) {
$this->error('你输入的词语中含有敏感词', $sensitiveWords['sensitive_words']);
}
过滤敏感词
对输入内容进行检测、过滤,把过滤后的内容进行保存。
php
$sensitiveWords = filter_sensitive_words($content);
$content = $sensitiveWords['content'];
总结
在Fastadmin框架中实现敏感词管理笔记