diff --git a/blog-admin/src/main/java/com/zyd/blog/controller/RestSensitiveWordController.java b/blog-admin/src/main/java/com/zyd/blog/controller/RestSensitiveWordController.java new file mode 100644 index 0000000..384e2b1 --- /dev/null +++ b/blog-admin/src/main/java/com/zyd/blog/controller/RestSensitiveWordController.java @@ -0,0 +1,75 @@ +package com.zyd.blog.controller; + +import com.github.pagehelper.PageInfo; +import com.zyd.blog.business.annotation.BussinessLog; +import com.zyd.blog.business.entity.SensitiveWord; +import com.zyd.blog.business.enums.ResponseStatus; +import com.zyd.blog.business.service.BizSensitiveWordService; +import com.zyd.blog.business.vo.SensitiveWordConditionVO; +import com.zyd.blog.framework.object.PageResult; +import com.zyd.blog.framework.object.ResponseVO; +import com.zyd.blog.util.ResultUtil; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/sensitive/word") +public class RestSensitiveWordController { + + @Autowired + private BizSensitiveWordService sensitiveWordService; + + @RequiresPermissions("sensitive:words") + @PostMapping("/list") + public PageResult list(SensitiveWordConditionVO vo) { + PageInfo pageInfo = sensitiveWordService.findPageBreakByCondition(vo); + return ResultUtil.tablePage(pageInfo); + } + + @RequiresPermissions("sensitive:word:add") + @PostMapping(value = "/add") + @BussinessLog("添加敏感词") + public ResponseVO add(SensitiveWord sensitiveWord) { + if (StringUtils.isEmpty(sensitiveWord.getWord())) { + return ResultUtil.error("敏感词不能为空"); + } + sensitiveWordService.insert(sensitiveWord); + return ResultUtil.success("成功"); + } + + @RequiresPermissions("sensitive:word:edit") + @PostMapping("/edit") + @BussinessLog("编辑敏感词") + public ResponseVO edit(SensitiveWord sensitiveWord) { + if (StringUtils.isEmpty(sensitiveWord.getWord())) { + return ResultUtil.error("敏感词不能为空"); + } + sensitiveWordService.updateSelective(sensitiveWord); + return ResultUtil.success(ResponseStatus.SUCCESS); + } + + @RequiresPermissions("sensitive:word:delete") + @PostMapping("/remove") + @BussinessLog("删除敏感词") + public ResponseVO remove(Long[] ids) { + if (null == ids) { + return ResultUtil.error("请至少选择一条记录"); + } + for (Long id : ids) { + sensitiveWordService.removeByPrimaryKey(id); + } + return ResultUtil.success("成功删除 [" + ids.length + "] 条敏感词"); + } + + @RequiresPermissions("sensitive:words") + @PostMapping("/get/{id}") + @BussinessLog("获取敏感词详情") + public ResponseVO get(@PathVariable Long id) { + return ResultUtil.success(null, sensitiveWordService.getByPrimaryKey(id)); + } +} diff --git a/blog-core/src/main/java/com/zyd/blog/business/entity/SensitiveWord.java b/blog-core/src/main/java/com/zyd/blog/business/entity/SensitiveWord.java new file mode 100644 index 0000000..207ae20 --- /dev/null +++ b/blog-core/src/main/java/com/zyd/blog/business/entity/SensitiveWord.java @@ -0,0 +1,98 @@ +package com.zyd.blog.business.entity; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.zyd.blog.persistence.beans.BizSensitiveWord; +import java.util.Date; + +public class SensitiveWord { + private BizSensitiveWord bizSensitiveWord; + private String matchTypeDesc; + private String statusDesc; + + public SensitiveWord() { + this.bizSensitiveWord = new BizSensitiveWord(); + } + + public SensitiveWord(BizSensitiveWord bizSensitiveWord) { + this.bizSensitiveWord = bizSensitiveWord; + } + + @JsonIgnore + public BizSensitiveWord getBizSensitiveWord() { + return this.bizSensitiveWord; + } + + // 委托所有字段访问到 bizSensitiveWord + public Long getId() { + return bizSensitiveWord.getId(); + } + + public void setId(Long id) { + bizSensitiveWord.setId(id); + } + + public String getWord() { + return bizSensitiveWord.getWord(); + } + + public void setWord(String word) { + bizSensitiveWord.setWord(word); + } + + public String getMatchType() { + return bizSensitiveWord.getMatchType(); + } + + public void setMatchType(String matchType) { + bizSensitiveWord.setMatchType(matchType); + } + + public Integer getStatus() { + return bizSensitiveWord.getStatus(); + } + + public void setStatus(Integer status) { + bizSensitiveWord.setStatus(status); + } + + public String getRemark() { + return bizSensitiveWord.getRemark(); + } + + public void setRemark(String remark) { + bizSensitiveWord.setRemark(remark); + } + + public Date getCreateTime() { + return bizSensitiveWord.getCreateTime(); + } + + public void setCreateTime(Date createTime) { + bizSensitiveWord.setCreateTime(createTime); + } + + public Date getUpdateTime() { + return bizSensitiveWord.getUpdateTime(); + } + + public void setUpdateTime(Date updateTime) { + bizSensitiveWord.setUpdateTime(updateTime); + } + + // 扩展字段 + public String getMatchTypeDesc() { + return matchTypeDesc; + } + + public void setMatchTypeDesc(String matchTypeDesc) { + this.matchTypeDesc = matchTypeDesc; + } + + public String getStatusDesc() { + return statusDesc; + } + + public void setStatusDesc(String statusDesc) { + this.statusDesc = statusDesc; + } +} \ No newline at end of file diff --git a/blog-core/src/main/java/com/zyd/blog/business/service/BizSensitiveWordService.java b/blog-core/src/main/java/com/zyd/blog/business/service/BizSensitiveWordService.java new file mode 100644 index 0000000..e58d3ac --- /dev/null +++ b/blog-core/src/main/java/com/zyd/blog/business/service/BizSensitiveWordService.java @@ -0,0 +1,14 @@ +package com.zyd.blog.business.service; + +import com.github.pagehelper.PageInfo; +import com.zyd.blog.business.entity.SensitiveWord; +import com.zyd.blog.business.vo.SensitiveWordConditionVO; +import com.zyd.blog.framework.object.AbstractService; + +import java.util.List; + +public interface BizSensitiveWordService extends AbstractService { + PageInfo findPageBreakByCondition(SensitiveWordConditionVO vo); + List findAllEnabled(); + List detectSensitiveWords(String content); +} \ No newline at end of file diff --git a/blog-core/src/main/java/com/zyd/blog/business/service/impl/BizCommentServiceImpl.java b/blog-core/src/main/java/com/zyd/blog/business/service/impl/BizCommentServiceImpl.java index 6965245..86b0452 100644 --- a/blog-core/src/main/java/com/zyd/blog/business/service/impl/BizCommentServiceImpl.java +++ b/blog-core/src/main/java/com/zyd/blog/business/service/impl/BizCommentServiceImpl.java @@ -11,6 +11,7 @@ import com.zyd.blog.business.enums.ConfigKeyEnum; import com.zyd.blog.business.enums.TemplateKeyEnum; import com.zyd.blog.business.service.BizCommentService; +import com.zyd.blog.business.service.BizSensitiveWordService; import com.zyd.blog.business.service.MailService; import com.zyd.blog.business.service.SysConfigService; import com.zyd.blog.business.vo.CommentConditionVO; @@ -62,6 +63,9 @@ public class BizCommentServiceImpl implements BizCommentService { @Autowired private SysConfigService configService; + // 添加这一行 + @Autowired + private BizSensitiveWordService sensitiveWordService; /** * 分页查询 * @@ -187,6 +191,9 @@ public Comment comment(Comment comment) throws ZhydCommentException { // set当前评论者的位置信息 this.setCurrentLocation(comment); + // 敏感词检测 + this.checkSensitiveWords(comment); + // 保存 this.insert(comment); @@ -194,7 +201,33 @@ public Comment comment(Comment comment) throws ZhydCommentException { this.sendEmail(comment); return comment; } + /** + * 敏感词检测 + */ + private void checkSensitiveWords(Comment comment) { + SysConfig enableConfig = configService.getByKey("sensitive.word.enable"); + if (enableConfig == null || !"1".equals(enableConfig.getConfigValue())) { + return; + } + + List hitWords = sensitiveWordService.detectSensitiveWords(comment.getContent()); + if (CollectionUtils.isEmpty(hitWords)) { + return; + } + SysConfig strategyConfig = configService.getByKey("sensitive.word.strategy"); + String strategy = strategyConfig != null ? strategyConfig.getConfigValue() : "pending"; + + if ("reject".equals(strategy)) { + comment.setStatus(CommentStatusEnum.REJECT.toString()); + comment.setRemark("包含敏感词:" + String.join(", ", hitWords)); + } else { + comment.setStatus(CommentStatusEnum.VERIFYING.toString()); + comment.setRemark("包含敏感词,待审核:" + String.join(", ", hitWords)); + } + + log.info("评论包含敏感词:{},处理策略:{}", hitWords, strategy); + } /** * 过滤评论内容 * diff --git a/blog-core/src/main/java/com/zyd/blog/business/service/impl/BizSensitiveWordServiceImpl.java b/blog-core/src/main/java/com/zyd/blog/business/service/impl/BizSensitiveWordServiceImpl.java new file mode 100644 index 0000000..595a8c6 --- /dev/null +++ b/blog-core/src/main/java/com/zyd/blog/business/service/impl/BizSensitiveWordServiceImpl.java @@ -0,0 +1,189 @@ +package com.zyd.blog.business.service.impl; + +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import com.zyd.blog.business.annotation.RedisCache; +import com.zyd.blog.business.entity.SensitiveWord; +import com.zyd.blog.business.service.BizSensitiveWordService; +import com.zyd.blog.business.vo.SensitiveWordConditionVO; +import com.zyd.blog.persistence.beans.BizSensitiveWord; +import com.zyd.blog.persistence.mapper.BizSensitiveWordMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +@Slf4j +@Service +public class BizSensitiveWordServiceImpl implements BizSensitiveWordService { + + @Autowired + private BizSensitiveWordMapper bizSensitiveWordMapper; + + @Autowired + private RedisTemplate redisTemplate; + + private static final String SENSITIVE_WORDS_CACHE_KEY = "sensitive:words:enabled"; + + @Override + public PageInfo findPageBreakByCondition(SensitiveWordConditionVO vo) { + // 参数校验 + if (vo == null) { + return new PageInfo<>(); + } + + // 设置分页参数 + PageHelper.startPage(vo.getPageNumber(), vo.getPageSize()); + + // 查询数据 + List list = bizSensitiveWordMapper.findPageBreakByCondition(vo); + + // 处理空结果 + if (CollectionUtils.isEmpty(list)) { + return new PageInfo<>(); + } + + // 转换为业务对象 + List boList = this.convertToBo(list); + + // 构建分页结果 + PageInfo bean = new PageInfo(list); + bean.setList(boList); + return bean; + } + + @Override + @RedisCache + public List findAllEnabled() { + List list = bizSensitiveWordMapper.findAllEnabled(); + return convertToBo(list); + } + + @Override + public List detectSensitiveWords(String content) { + if (StringUtils.isEmpty(content)) { + return new ArrayList<>(); + } + + List sensitiveWords = getCachedSensitiveWords(); + if (CollectionUtils.isEmpty(sensitiveWords)) { + return new ArrayList<>(); + } + + List hitWords = new ArrayList<>(); + String lowerContent = content.toLowerCase(); + + for (SensitiveWord word : sensitiveWords) { + if ("EXACT".equals(word.getMatchType())) { + if (lowerContent.contains(word.getWord().toLowerCase())) { + hitWords.add(word.getWord()); + } + } else if ("FUZZY".equals(word.getMatchType())) { + if (containsFuzzy(lowerContent, word.getWord().toLowerCase())) { + hitWords.add(word.getWord()); + } + } + } + + return hitWords; + } + + @SuppressWarnings("unchecked") + private List getCachedSensitiveWords() { + try { + List cachedWords = (List) redisTemplate.opsForValue().get(SENSITIVE_WORDS_CACHE_KEY); + if (cachedWords != null) { + return cachedWords; + } + } catch (Exception e) { + log.warn("获取敏感词缓存失败", e); + } + + List words = findAllEnabled(); + try { + redisTemplate.opsForValue().set(SENSITIVE_WORDS_CACHE_KEY, words, 30, TimeUnit.MINUTES); + } catch (Exception e) { + log.warn("缓存敏感词失败", e); + } + return words; + } + + private boolean containsFuzzy(String content, String word) { + String cleanContent = content.replaceAll("[\\s\\p{Punct}]", ""); + String cleanWord = word.replaceAll("[\\s\\p{Punct}]", ""); + return cleanContent.contains(cleanWord); + } + + private List convertToBo(List list) { + if (CollectionUtils.isEmpty(list)) { + return new ArrayList<>(); + } + return list.stream().map(bean -> { + SensitiveWord bo = new SensitiveWord(bean); + bo.setMatchTypeDesc("EXACT".equals(bean.getMatchType()) ? "精确匹配" : "模糊匹配"); + bo.setStatusDesc(bean.getStatus() == 1 ? "启用" : "禁用"); + return bo; + }).collect(Collectors.toList()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @RedisCache(flush = true) + public SensitiveWord insert(SensitiveWord entity) { + Assert.notNull(entity, "SensitiveWord不可为空!"); + entity.setUpdateTime(new Date()); + entity.setCreateTime(new Date()); + bizSensitiveWordMapper.insertSelective(entity.getBizSensitiveWord()); + clearSensitiveWordsCache(); + return entity; + } + + @Override + @Transactional(rollbackFor = Exception.class) + @RedisCache(flush = true) + public boolean removeByPrimaryKey(Long primaryKey) { + clearSensitiveWordsCache(); + return bizSensitiveWordMapper.deleteByPrimaryKey(primaryKey) > 0; + } + + @Override + @Transactional(rollbackFor = Exception.class) + @RedisCache(flush = true) + public boolean updateSelective(SensitiveWord entity) { + Assert.notNull(entity, "SensitiveWord不可为空!"); + entity.setUpdateTime(new Date()); + clearSensitiveWordsCache(); + return bizSensitiveWordMapper.updateByPrimaryKeySelective(entity.getBizSensitiveWord()) > 0; + } + + @Override + public SensitiveWord getByPrimaryKey(Long primaryKey) { + Assert.notNull(primaryKey, "PrimaryKey不可为空!"); + BizSensitiveWord entity = bizSensitiveWordMapper.selectByPrimaryKey(primaryKey); + return null == entity ? null : new SensitiveWord(entity); + } + + @Override + public List listAll() { + List list = bizSensitiveWordMapper.selectAll(); + return convertToBo(list); + } + + private void clearSensitiveWordsCache() { + try { + redisTemplate.delete(SENSITIVE_WORDS_CACHE_KEY); + } catch (Exception e) { + log.warn("清除敏感词缓存失败", e); + } + } +} diff --git a/blog-core/src/main/java/com/zyd/blog/business/vo/SensitiveWordConditionVO.java b/blog-core/src/main/java/com/zyd/blog/business/vo/SensitiveWordConditionVO.java new file mode 100644 index 0000000..4543b3c --- /dev/null +++ b/blog-core/src/main/java/com/zyd/blog/business/vo/SensitiveWordConditionVO.java @@ -0,0 +1,34 @@ +package com.zyd.blog.business.vo; + +import com.zyd.blog.framework.object.BaseConditionVO; + +public class SensitiveWordConditionVO extends BaseConditionVO { + private String word; + private String matchType; + private Integer status; + + // getter/setter 方法 + public String getWord() { + return word; + } + + public void setWord(String word) { + this.word = word; + } + + public String getMatchType() { + return matchType; + } + + public void setMatchType(String matchType) { + this.matchType = matchType; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } +} \ No newline at end of file diff --git a/blog-core/src/main/java/com/zyd/blog/framework/object/BaseConditionVO.java b/blog-core/src/main/java/com/zyd/blog/framework/object/BaseConditionVO.java index 3e162d9..9c57a37 100644 --- a/blog-core/src/main/java/com/zyd/blog/framework/object/BaseConditionVO.java +++ b/blog-core/src/main/java/com/zyd/blog/framework/object/BaseConditionVO.java @@ -35,4 +35,8 @@ public int getPageSize() { public int getPageStart() { return pageNumber > 0 ? (pageNumber - 1) * getPageSize() : 0; } + + public int getPageNumber() { + return pageNumber; + } } diff --git a/blog-core/src/main/java/com/zyd/blog/persistence/beans/BizSensitiveWord.java b/blog-core/src/main/java/com/zyd/blog/persistence/beans/BizSensitiveWord.java new file mode 100644 index 0000000..3dfc5db --- /dev/null +++ b/blog-core/src/main/java/com/zyd/blog/persistence/beans/BizSensitiveWord.java @@ -0,0 +1,42 @@ +package com.zyd.blog.persistence.beans; + +import com.zyd.blog.framework.object.AbstractDO; + +public class BizSensitiveWord extends AbstractDO { + private String word; + private String matchType; + private Integer status; + private String remark; + + public String getWord() { + return word; + } + + public void setWord(String word) { + this.word = word; + } + + public String getMatchType() { + return matchType; + } + + public void setMatchType(String matchType) { + this.matchType = matchType; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } +} \ No newline at end of file diff --git a/blog-core/src/main/java/com/zyd/blog/persistence/mapper/BizSensitiveWordMapper.java b/blog-core/src/main/java/com/zyd/blog/persistence/mapper/BizSensitiveWordMapper.java new file mode 100644 index 0000000..90dc00d --- /dev/null +++ b/blog-core/src/main/java/com/zyd/blog/persistence/mapper/BizSensitiveWordMapper.java @@ -0,0 +1,14 @@ +package com.zyd.blog.persistence.mapper; + +import com.zyd.blog.business.vo.SensitiveWordConditionVO; +import com.zyd.blog.persistence.beans.BizSensitiveWord; +import com.zyd.blog.plugin.BaseMapper; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface BizSensitiveWordMapper extends BaseMapper { + List findPageBreakByCondition(SensitiveWordConditionVO vo); + List findAllEnabled(); +} \ No newline at end of file diff --git a/blog-core/src/main/resources/mybatis/BizSensitiveWordMapper.xml b/blog-core/src/main/resources/mybatis/BizSensitiveWordMapper.xml new file mode 100644 index 0000000..1be8a20 --- /dev/null +++ b/blog-core/src/main/resources/mybatis/BizSensitiveWordMapper.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dblog.sql b/dblog.sql index da0964b..fe5d4d2 100644 --- a/dblog.sql +++ b/dblog.sql @@ -49,6 +49,26 @@ LOCK TABLES `biz_ad` WRITE; /*!40000 ALTER TABLE `biz_ad` ENABLE KEYS */; UNLOCK TABLES; +-- +-- Table structure for table `biz_sensitive_word` +-- + +DROP TABLE IF EXISTS `biz_sensitive_word`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `biz_sensitive_word` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `word` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '敏感词', + `match_type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT 'EXACT' COMMENT '匹配方式', + `status` tinyint NOT NULL DEFAULT '1' COMMENT '状态:0-禁用,1-启用', + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注', + `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPACT; +/*!40101 SET character_set_client = @saved_cs_client */; + + -- -- Table structure for table `biz_article` -- diff --git a/docs/docker/mysql/init_data.sql b/docs/docker/mysql/init_data.sql index ec9d94b..122aed2 100644 --- a/docs/docker/mysql/init_data.sql +++ b/docs/docker/mysql/init_data.sql @@ -3,6 +3,21 @@ # 该文件只用来初始化数据库内容,需要有限执行dblog.sql创建数据库 # ################################### readme ################################### +# 创建敏感词表(兼容未升级的数据库) +CREATE TABLE IF NOT EXISTS `biz_sensitive_word` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `word` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '敏感词', + `match_type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT 'EXACT' COMMENT '匹配方式', + `status` tinyint NOT NULL DEFAULT '1' COMMENT '状态:0-禁用,1-启用', + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注', + `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPACT; + +# 清空敏感词表 +TRUNCATE TABLE `biz_sensitive_word`; + # 清空文章表 TRUNCATE TABLE `biz_article`; # 初始化文章 @@ -72,6 +87,12 @@ INSERT INTO `sys_config`(`id`, `config_key`, `config_value`, `create_time`, `upd INSERT INTO `sys_config`(`id`, `config_key`, `config_value`, `create_time`, `update_time`) VALUES (36, 'copyright', 'Copyright zhyd.me', now(), now()); INSERT INTO `sys_config`(`id`, `config_key`, `config_value`, `create_time`, `update_time`) VALUES (37, 'dynamicTitle', '您有一条新消息', now(), now()); +-- 在现有配置数据后添加敏感词相关配置 +INSERT INTO `sys_config`(`config_key`, `config_value`, `create_time`, `update_time`) VALUES +('sensitive.word.enable', '1', now(), now()), +('sensitive.word.strategy', 'pending', now(), now()), +('sensitive.word.email.notify', '0', now(), now()); + # 清空友情链接表 TRUNCATE TABLE `sys_link`; # 初始化友情链接 @@ -181,6 +202,13 @@ INSERT INTO `sys_resources` VALUES (74, '编辑器', 'menu', '/editor', 'editor' INSERT INTO `sys_resources` VALUES (75, '文件管理', 'menu', '/files', 'files', 40, 6, 0, 1, NULL, now(), now()); +-- 添加敏感词管理权限 +INSERT INTO `sys_resources` VALUES (91, '敏感词管理', 'menu', '/sensitiveWords', 'sensitiveWords', 40, 8, 0, 1, 'fa fa-shield', now(), now()); +INSERT INTO `sys_resources` VALUES (92, '查看敏感词', 'button', NULL, 'sensitive:words', 91, 2, 0, 1, NULL, now(), now()); +INSERT INTO `sys_resources` VALUES (93, '新增敏感词', 'button', NULL, 'sensitive:word:add', 91, 3, 0, 1, NULL, now(), now()); +INSERT INTO `sys_resources` VALUES (94, '编辑敏感词', 'button', NULL, 'sensitive:word:edit', 91, 4, 0, 1, NULL, now(), now()); +INSERT INTO `sys_resources` VALUES (95, '删除敏感词', 'button', NULL, 'sensitive:word:delete', 91, 5, 0, 1, NULL, now(), now()); + # 20210427 INSERT INTO `sys_resources` VALUES (76, '社会化登录配置管理', 'menu', '/socials', 'socials', 40, 7, 0, 1, '', now(), now()); INSERT INTO `sys_resources` VALUES (77, '新增社会化登录配置', 'button', NULL, 'social:add', 76, 2, 0, 1, NULL, now(), now()); diff --git a/docs/sensitive_word_manual_sql.md b/docs/sensitive_word_manual_sql.md new file mode 100644 index 0000000..2f15b27 --- /dev/null +++ b/docs/sensitive_word_manual_sql.md @@ -0,0 +1,36 @@ +# Sensitive word manual setup + +Use these statements if your database was provisioned before the sensitive-word feature was added and you prefer to update it manually instead of re-running the bundled SQL files. + +## 1) Create the `biz_sensitive_word` table +```sql +CREATE TABLE IF NOT EXISTS `biz_sensitive_word` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `word` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '敏感词', + `match_type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT 'EXACT' COMMENT '匹配方式', + `status` tinyint NOT NULL DEFAULT '1' COMMENT '状态:0-禁用,1-启用', + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注', + `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPACT; +``` + +## 2) Seed configuration flags (enable moderation and pick strategy) +```sql +INSERT INTO `sys_config`(`config_key`, `config_value`, `create_time`, `update_time`) VALUES +('sensitive.word.enable', '1', now(), now()), +('sensitive.word.strategy', 'pending', now(), now()), +('sensitive.word.email.notify', '0', now(), now()); +``` + +## 3) Seed permissions for the admin console menu and buttons +```sql +INSERT INTO `sys_resources` VALUES (91, '敏感词管理', 'menu', '/sensitiveWords', 'sensitiveWords', 40, 8, 0, 1, 'fa fa-shield', now(), now()); +INSERT INTO `sys_resources` VALUES (92, '查看敏感词', 'button', NULL, 'sensitive:words', 91, 2, 0, 1, NULL, now(), now()); +INSERT INTO `sys_resources` VALUES (93, '新增敏感词', 'button', NULL, 'sensitive:word:add', 91, 3, 0, 1, NULL, now(), now()); +INSERT INTO `sys_resources` VALUES (94, '编辑敏感词', 'button', NULL, 'sensitive:word:edit', 91, 4, 0, 1, NULL, now(), now()); +INSERT INTO `sys_resources` VALUES (95, '删除敏感词', 'button', NULL, 'sensitive:word:delete', 91, 5, 0, 1, NULL, now(), now()); +``` + +> Tip: if the rows might already exist in `sys_config` or `sys_resources`, switch to `INSERT IGNORE` or add `ON DUPLICATE KEY UPDATE` to avoid conflicts.