Commit 3f7d7c3bfac9323ab1725d12fd182e2d0a13cd87

Authored by YunaiV
1 parent bae4502e

同步 SensitiveWord 敏感词代码,原分支未正确关联仓库

Showing 24 changed files with 1066 additions and 0 deletions
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/StringLiSTTypeHandler.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.mybatis.core.type;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.StrUtil;
  5 +import org.apache.ibatis.type.JdbcType;
  6 +import org.apache.ibatis.type.MappedJdbcTypes;
  7 +import org.apache.ibatis.type.MappedTypes;
  8 +import org.apache.ibatis.type.TypeHandler;
  9 +
  10 +import java.sql.CallableStatement;
  11 +import java.sql.PreparedStatement;
  12 +import java.sql.ResultSet;
  13 +import java.sql.SQLException;
  14 +import java.util.List;
  15 +
  16 +/**
  17 + * List<String> 的类型转换器实现类,对应数据库的 varchar 类型
  18 + *
  19 + * @author 永不言败
  20 + * @since 2022 3/23 12:50:15
  21 + */
  22 +@MappedJdbcTypes(JdbcType.VARCHAR)
  23 +@MappedTypes(List.class)
  24 +public class StringLiSTTypeHandler implements TypeHandler<List<String>> {
  25 +
  26 + private static final String COMMA = ",";
  27 +
  28 + @Override
  29 + public void setParameter(PreparedStatement ps, int i, List<String> strings, JdbcType jdbcType) throws SQLException {
  30 + // 设置占位符
  31 + ps.setString(i, CollUtil.join(strings, COMMA));
  32 + }
  33 +
  34 + @Override
  35 + public List<String> getResult(ResultSet rs, String columnName) throws SQLException {
  36 + String value = rs.getString(columnName);
  37 + return getResult(value);
  38 + }
  39 +
  40 + @Override
  41 + public List<String> getResult(ResultSet rs, int columnIndex) throws SQLException {
  42 + String value = rs.getString(columnIndex);
  43 + return getResult(value);
  44 + }
  45 +
  46 + @Override
  47 + public List<String> getResult(CallableStatement cs, int columnIndex) throws SQLException {
  48 + String value = cs.getString(columnIndex);
  49 + return getResult(value);
  50 + }
  51 +
  52 + private List<String> getResult(String value) {
  53 + if (value == null) {
  54 + return null;
  55 + }
  56 + return StrUtil.splitTrim(value, COMMA);
  57 + }
  58 +}
... ...
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/sensitiveword/SensitiveWordApi.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.api.sensitiveword;
  2 +
  3 +import java.util.List;
  4 +import java.util.Set;
  5 +
  6 +/**
  7 + * 大佬,别偷懒:代码千万行,注释第一行!
  8 + *
  9 + * @author: 永不言败 <向国足学习永不言败>
  10 + * @since: 2022/3/23 17:00
  11 + * @description:
  12 + * @modification:
  13 + */
  14 +public interface SensitiveWordApi {
  15 +
  16 +}
... ...
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java
... ... @@ -119,4 +119,8 @@ public interface ErrorCodeConstants {
119 119 ErrorCode SOCIAL_USER_UNBIND_NOT_SELF = new ErrorCode(1002018001, "社交解绑失败,非当前用户绑定");
120 120 ErrorCode SOCIAL_USER_NOT_FOUND = new ErrorCode(1002018002, "社交授权失败,找不到对应的用户");
121 121  
  122 + // ========== 系统铭感词 1002019000 =========
  123 + ErrorCode SENSITIVE_WORD_NOT_EXISTS = new ErrorCode(1002019000, "系统敏感词在所有标签中都不存在");
  124 + ErrorCode SENSITIVE_WORD_EXISTS = new ErrorCode(1002019001, "系统敏感词已在标签中存在");
  125 +
122 126 }
... ...
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sensitiveword/SensitiveWordController.http 0 → 100644
  1 +GET http://localhost:81/dev-api/admin-api/system/sensitive-word/get-all-tags
  2 +Accept: application/json
  3 +Authorization: Bearer 385d533b781f44f6bb21ea08afeec47c
  4 +
  5 +
  6 +
  7 +###
  8 +POST http://localhost:81/dev-api/admin-api/system/sensitive-word/create
  9 +Content-Type: application/json
  10 +Authorization: Bearer 1649ff1f8b9a4eeeb458fe93a71c78b5
  11 +
  12 +{
  13 + "name": "test",
  14 + "tags": [
  15 + "bbb,aaa"
  16 + ],
  17 + "description": "test",
  18 + "status": true
  19 +}
  20 +
  21 +
  22 +###
... ...
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sensitiveword/SensitiveWordController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.sensitiveword;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
  6 +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
  7 +import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.*;
  8 +import cn.iocoder.yudao.module.system.convert.sensitiveword.SensitiveWordConvert;
  9 +import cn.iocoder.yudao.module.system.dal.dataobject.sensitiveword.SensitiveWordDO;
  10 +import cn.iocoder.yudao.module.system.service.sensitiveword.SensitiveWordService;
  11 +import io.swagger.annotations.Api;
  12 +import io.swagger.annotations.ApiImplicitParam;
  13 +import io.swagger.annotations.ApiOperation;
  14 +import org.springframework.security.access.prepost.PreAuthorize;
  15 +import org.springframework.validation.annotation.Validated;
  16 +import org.springframework.web.bind.annotation.*;
  17 +
  18 +import javax.annotation.Resource;
  19 +import javax.servlet.http.HttpServletResponse;
  20 +import javax.validation.Valid;
  21 +import java.io.IOException;
  22 +import java.util.List;
  23 +import java.util.Set;
  24 +
  25 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  26 +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
  27 +
  28 +@Api(tags = "管理后台 - 敏感词")
  29 +@RestController
  30 +@RequestMapping("/system/sensitive-word")
  31 +@Validated
  32 +public class SensitiveWordController {
  33 +
  34 + @Resource
  35 + private SensitiveWordService sensitiveWordService;
  36 +
  37 + @PostMapping("/create")
  38 + @ApiOperation("创建敏感词")
  39 + @PreAuthorize("@ss.hasPermission('system:sensitive-word:create')")
  40 + public CommonResult<Long> createSensitiveWord(@Valid @RequestBody SensitiveWordCreateReqVO createReqVO) {
  41 + return success(sensitiveWordService.createSensitiveWord(createReqVO));
  42 + }
  43 +
  44 + @PutMapping("/update")
  45 + @ApiOperation("更新敏感词")
  46 + @PreAuthorize("@ss.hasPermission('system:sensitive-word:update')")
  47 + public CommonResult<Boolean> updateSensitiveWord(@Valid @RequestBody SensitiveWordUpdateReqVO updateReqVO) {
  48 + sensitiveWordService.updateSensitiveWord(updateReqVO);
  49 + return success(true);
  50 + }
  51 +
  52 + @DeleteMapping("/delete")
  53 + @ApiOperation("删除敏感词")
  54 + @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
  55 + @PreAuthorize("@ss.hasPermission('system:sensitive-word:delete')")
  56 + public CommonResult<Boolean> deleteSensitiveWord(@RequestParam("id") Long id) {
  57 + sensitiveWordService.deleteSensitiveWord(id);
  58 + return success(true);
  59 + }
  60 +
  61 + @GetMapping("/get")
  62 + @ApiOperation("获得敏感词")
  63 + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
  64 + @PreAuthorize("@ss.hasPermission('system:sensitive-word:query')")
  65 + public CommonResult<SensitiveWordRespVO> getSensitiveWord(@RequestParam("id") Long id) {
  66 + SensitiveWordDO sensitiveWord = sensitiveWordService.getSensitiveWord(id);
  67 + return success(SensitiveWordConvert.INSTANCE.convert(sensitiveWord));
  68 + }
  69 +
  70 + @GetMapping("/page")
  71 + @ApiOperation("获得敏感词分页")
  72 + @PreAuthorize("@ss.hasPermission('system:sensitive-word:query')")
  73 + public CommonResult<PageResult<SensitiveWordRespVO>> getSensitiveWordPage(@Valid SensitiveWordPageReqVO pageVO) {
  74 + PageResult<SensitiveWordDO> pageResult = sensitiveWordService.getSensitiveWordPage(pageVO);
  75 + return success(SensitiveWordConvert.INSTANCE.convertPage(pageResult));
  76 + }
  77 +
  78 + @GetMapping("/get-tags")
  79 + @ApiOperation("获取所有敏感词的标签数组")
  80 + @PreAuthorize("@ss.hasPermission('system:sensitive-word:query')")
  81 + public CommonResult<Set<String>> getSensitiveWordTags() throws IOException {
  82 + return success(sensitiveWordService.getSensitiveWordTags());
  83 + }
  84 +
  85 + @GetMapping("/export-excel")
  86 + @ApiOperation("导出敏感词 Excel")
  87 + @PreAuthorize("@ss.hasPermission('system:sensitive-word:export')")
  88 + @OperateLog(type = EXPORT)
  89 + public void exportSensitiveWordExcel(@Valid SensitiveWordExportReqVO exportReqVO,
  90 + HttpServletResponse response) throws IOException {
  91 + List<SensitiveWordDO> list = sensitiveWordService.getSensitiveWordList(exportReqVO);
  92 + // 导出 Excel
  93 + List<SensitiveWordExcelVO> datas = SensitiveWordConvert.INSTANCE.convertList02(list);
  94 + ExcelUtils.write(response, "敏感词.xls", "数据", SensitiveWordExcelVO.class, datas);
  95 + }
  96 +
  97 +// @GetMapping("/is-sensitive-word-by-text-and-tag")
  98 +// @ApiOperation("通过tag判断传入text是否含有敏感词")
  99 +// @PreAuthorize("@ss.hasPermission('system:sensitive-word:checkbytextandtag')")
  100 +// public CommonResult<Boolean> isSensitiveWordByTextAndTag(@NotBlank String text, @NotBlank String tag) throws IOException {
  101 +// return success(sensitiveWordApi.isSensitiveWordByTextAndTag(text,tag));
  102 +// }
  103 +
  104 +}
... ...
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sensitiveword/vo/SensitiveWordBaseVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo;
  2 +
  3 +import io.swagger.annotations.ApiModelProperty;
  4 +import lombok.Data;
  5 +
  6 +import javax.validation.constraints.NotNull;
  7 +import java.util.List;
  8 +
  9 +/**
  10 +* 敏感词 Base VO,提供给添加、修改、详细的子 VO 使用
  11 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
  12 +*/
  13 +@Data
  14 +public class SensitiveWordBaseVO {
  15 +
  16 + @ApiModelProperty(value = "敏感词", required = true, example = "敏感词")
  17 + @NotNull(message = "敏感词不能为空")
  18 + private String name;
  19 +
  20 + @ApiModelProperty(value = "标签", required = true, example = "短信,评论")
  21 + @NotNull(message = "标签不能为空")
  22 + private List<String> tags;
  23 +
  24 + @ApiModelProperty(value = "状态", required = true, example = "1", notes = "参见 CommonStatusEnum 枚举类")
  25 + @NotNull(message = "状态不能为空")
  26 + private Integer status;
  27 +
  28 + @ApiModelProperty(value = "描述", example = "污言秽语")
  29 + private String description;
  30 +
  31 +}
... ...
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sensitiveword/vo/SensitiveWordCreateReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import lombok.Data;
  5 +import lombok.EqualsAndHashCode;
  6 +import lombok.ToString;
  7 +
  8 +@ApiModel("管理后台 - 敏感词创建 Request VO")
  9 +@Data
  10 +@EqualsAndHashCode(callSuper = true)
  11 +@ToString(callSuper = true)
  12 +public class SensitiveWordCreateReqVO extends SensitiveWordBaseVO {
  13 +
  14 +}
... ...
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sensitiveword/vo/SensitiveWordExcelVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo;
  2 +
  3 +import com.alibaba.excel.annotation.ExcelProperty;
  4 +import lombok.Data;
  5 +
  6 +import java.util.Date;
  7 +import java.util.List;
  8 +
  9 +/**
  10 + * 敏感词 Excel VO
  11 + *
  12 + * @author 永不言败
  13 + */
  14 +@Data
  15 +public class SensitiveWordExcelVO {
  16 +
  17 + @ExcelProperty("编号")
  18 + private Long id;
  19 +
  20 + @ExcelProperty("敏感词")
  21 + private String name;
  22 +
  23 + @ExcelProperty("标签")
  24 + private List<String> tags;
  25 +
  26 + @ExcelProperty("状态,true-启用,false-禁用")
  27 + private Integer status;
  28 +
  29 + @ExcelProperty("描述")
  30 + private String description;
  31 +
  32 + @ExcelProperty("创建时间")
  33 + private Date createTime;
  34 +
  35 +}
... ...
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sensitiveword/vo/SensitiveWordExportReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +import org.springframework.format.annotation.DateTimeFormat;
  7 +
  8 +import java.util.Date;
  9 +
  10 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  11 +
  12 +@ApiModel(value = "管理后台 - 敏感词 Excel 导出 Request VO", description = "参数和 SensitiveWordPageReqVO 是一致的")
  13 +@Data
  14 +public class SensitiveWordExportReqVO {
  15 +
  16 + @ApiModelProperty(value = "敏感词", example = "敏感词")
  17 + private String name;
  18 +
  19 + @ApiModelProperty(value = "标签", example = "短信,评论")
  20 + private String tag;
  21 +
  22 + @ApiModelProperty(value = "状态", example = "true-启用,false-禁用")
  23 + private Integer status;
  24 +
  25 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  26 + @ApiModelProperty(value = "开始创建时间")
  27 + private Date beginCreateTime;
  28 +
  29 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  30 + @ApiModelProperty(value = "结束创建时间")
  31 + private Date endCreateTime;
  32 +
  33 +}
... ...
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sensitiveword/vo/SensitiveWordPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.annotations.ApiModel;
  5 +import io.swagger.annotations.ApiModelProperty;
  6 +import lombok.Data;
  7 +import lombok.EqualsAndHashCode;
  8 +import lombok.ToString;
  9 +import org.springframework.format.annotation.DateTimeFormat;
  10 +
  11 +import java.util.Date;
  12 +
  13 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  14 +
  15 +@ApiModel("管理后台 - 敏感词分页 Request VO")
  16 +@Data
  17 +@EqualsAndHashCode(callSuper = true)
  18 +@ToString(callSuper = true)
  19 +public class SensitiveWordPageReqVO extends PageParam {
  20 +
  21 + @ApiModelProperty(value = "敏感词", example = "敏感词")
  22 + private String name;
  23 +
  24 + @ApiModelProperty(value = "标签", example = "短信,评论")
  25 + private String tag;
  26 +
  27 + @ApiModelProperty(value = "状态", example = "true-启用,true-禁用")
  28 + private Integer status;
  29 +
  30 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  31 + @ApiModelProperty(value = "开始创建时间")
  32 + private Date beginCreateTime;
  33 +
  34 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  35 + @ApiModelProperty(value = "结束创建时间")
  36 + private Date endCreateTime;
  37 +
  38 +}
... ...
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sensitiveword/vo/SensitiveWordRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +
  9 +import java.util.Date;
  10 +
  11 +@ApiModel("管理后台 - 敏感词 Response VO")
  12 +@Data
  13 +@EqualsAndHashCode(callSuper = true)
  14 +@ToString(callSuper = true)
  15 +public class SensitiveWordRespVO extends SensitiveWordBaseVO {
  16 +
  17 + @ApiModelProperty(value = "编号", required = true, example = "1")
  18 + private Long id;
  19 +
  20 + @ApiModelProperty(value = "创建时间", required = true)
  21 + private Date createTime;
  22 +
  23 +}
... ...
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sensitiveword/vo/SensitiveWordUpdateReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +
  9 +import javax.validation.constraints.NotNull;
  10 +
  11 +@ApiModel("管理后台 - 敏感词更新 Request VO")
  12 +@Data
  13 +@EqualsAndHashCode(callSuper = true)
  14 +@ToString(callSuper = true)
  15 +public class SensitiveWordUpdateReqVO extends SensitiveWordBaseVO {
  16 +
  17 + @ApiModelProperty(value = "编号", required = true, example = "1")
  18 + @NotNull(message = "编号不能为空")
  19 + private Long id;
  20 +
  21 +}
... ...
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/convert/sensitiveword/SensitiveWordConvert.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.convert.sensitiveword;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordCreateReqVO;
  5 +import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordExcelVO;
  6 +import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordRespVO;
  7 +import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordUpdateReqVO;
  8 +import cn.iocoder.yudao.module.system.dal.dataobject.sensitiveword.SensitiveWordDO;
  9 +import org.mapstruct.Mapper;
  10 +import org.mapstruct.factory.Mappers;
  11 +
  12 +import java.util.List;
  13 +
  14 +/**
  15 + * 敏感词 Convert
  16 + *
  17 + * @author 永不言败
  18 + */
  19 +@Mapper
  20 +public interface SensitiveWordConvert {
  21 +
  22 + SensitiveWordConvert INSTANCE = Mappers.getMapper(SensitiveWordConvert.class);
  23 +
  24 + SensitiveWordDO convert(SensitiveWordCreateReqVO bean);
  25 +
  26 + SensitiveWordDO convert(SensitiveWordUpdateReqVO bean);
  27 +
  28 + SensitiveWordRespVO convert(SensitiveWordDO bean);
  29 +
  30 + List<SensitiveWordRespVO> convertList(List<SensitiveWordDO> list);
  31 +
  32 + PageResult<SensitiveWordRespVO> convertPage(PageResult<SensitiveWordDO> page);
  33 +
  34 + List<SensitiveWordExcelVO> convertList02(List<SensitiveWordDO> list);
  35 +
  36 +}
... ...
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sensitiveword/SensitiveWordDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.dal.dataobject.sensitiveword;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  5 +import cn.iocoder.yudao.framework.mybatis.core.type.StringLiSTTypeHandler;
  6 +import com.baomidou.mybatisplus.annotation.TableField;
  7 +import com.baomidou.mybatisplus.annotation.TableId;
  8 +import com.baomidou.mybatisplus.annotation.TableName;
  9 +import lombok.*;
  10 +
  11 +import java.util.List;
  12 +
  13 +/**
  14 + * 敏感词 DO
  15 + *
  16 + * @author 永不言败
  17 + */
  18 +@TableName(value = "system_sensitive_word", autoResultMap = true)
  19 +@Data
  20 +@EqualsAndHashCode(callSuper = true)
  21 +@ToString(callSuper = true)
  22 +@Builder
  23 +@NoArgsConstructor
  24 +@AllArgsConstructor
  25 +public class SensitiveWordDO extends BaseDO {
  26 +
  27 + /**
  28 + * 编号
  29 + */
  30 + @TableId
  31 + private Long id;
  32 + /**
  33 + * 敏感词
  34 + */
  35 + private String name;
  36 + /**
  37 + * 描述
  38 + */
  39 + private String description;
  40 + /**
  41 + * 标签数组
  42 + *
  43 + * 用于实现不同的业务场景下,需要使用不同标签的敏感词。
  44 + * 例如说,tag 有短信、论坛两种,敏感词 "推广" 在短信下是敏感词,在论坛下不是敏感词。
  45 + * 此时,我们会存储一条敏感词记录,它的 name 为"推广",tag 为短信。
  46 + */
  47 + @TableField(typeHandler = StringLiSTTypeHandler.class)
  48 + private List<String> tags;
  49 + /**
  50 + * 状态
  51 + *
  52 + * 枚举 {@link CommonStatusEnum}
  53 + */
  54 + private Integer status;
  55 +
  56 +}
... ...
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sensitiveword/SensitiveWordMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.dal.mysql.sensitiveword;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordExportReqVO;
  7 +import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordPageReqVO;
  8 +import cn.iocoder.yudao.module.system.dal.dataobject.sensitiveword.SensitiveWordDO;
  9 +import org.apache.ibatis.annotations.Mapper;
  10 +import org.apache.ibatis.annotations.Select;
  11 +
  12 +import java.util.Date;
  13 +import java.util.List;
  14 +
  15 +/**
  16 + * 敏感词 Mapper
  17 + *
  18 + * @author 永不言败
  19 + */
  20 +@Mapper
  21 +public interface SensitiveWordMapper extends BaseMapperX<SensitiveWordDO> {
  22 +
  23 + default PageResult<SensitiveWordDO> selectPage(SensitiveWordPageReqVO reqVO) {
  24 + return selectPage(reqVO, new LambdaQueryWrapperX<SensitiveWordDO>()
  25 + .likeIfPresent(SensitiveWordDO::getName, reqVO.getName())
  26 + .likeIfPresent(SensitiveWordDO::getTags, reqVO.getTag())
  27 + .eqIfPresent(SensitiveWordDO::getStatus, reqVO.getStatus())
  28 + .betweenIfPresent(SensitiveWordDO::getCreateTime, reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())
  29 + .orderByDesc(SensitiveWordDO::getId));
  30 + }
  31 +
  32 + default List<SensitiveWordDO> selectList(SensitiveWordExportReqVO reqVO) {
  33 + return selectList(new LambdaQueryWrapperX<SensitiveWordDO>()
  34 + .likeIfPresent(SensitiveWordDO::getName, reqVO.getName())
  35 + .likeIfPresent(SensitiveWordDO::getTags, reqVO.getTag())
  36 + .eqIfPresent(SensitiveWordDO::getStatus, reqVO.getStatus())
  37 + .betweenIfPresent(SensitiveWordDO::getCreateTime, reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())
  38 + .orderByDesc(SensitiveWordDO::getId));
  39 + }
  40 +
  41 + default SensitiveWordDO selectByName(String name) {
  42 + return selectOne(SensitiveWordDO::getName, name);
  43 + }
  44 +
  45 + @Select("SELECT id FROM system_sensitive_word WHERE update_time > #{maxUpdateTime} LIMIT 1")
  46 + SensitiveWordDO selectExistsByUpdateTimeAfter(Date maxUpdateTime);
  47 +}
... ...
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/sensitiveword/SensitiveWordRefreshConsumer.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.mq.consumer.sensitiveword;
  2 +
  3 +import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;
  4 +import cn.iocoder.yudao.module.system.mq.message.sensitiveword.SensitiveWordRefreshMessage;
  5 +import cn.iocoder.yudao.module.system.service.sensitiveword.SensitiveWordService;
  6 +import lombok.extern.slf4j.Slf4j;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +import javax.annotation.Resource;
  10 +
  11 +/**
  12 + * 针对 {@link SensitiveWordRefreshMessage} 的消费者
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +@Component
  17 +@Slf4j
  18 +public class SensitiveWordRefreshConsumer extends AbstractChannelMessageListener<SensitiveWordRefreshMessage> {
  19 +
  20 + @Resource
  21 + private SensitiveWordService sensitiveWordService;
  22 +
  23 + @Override
  24 + public void onMessage(SensitiveWordRefreshMessage message) {
  25 + log.info("[onMessage][收到 SensitiveWord 刷新消息]");
  26 + sensitiveWordService.initLocalCache();
  27 + }
  28 +
  29 +}
... ...
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/mq/message/sensitiveword/SensitiveWordRefreshMessage.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.mq.message.sensitiveword;
  2 +
  3 +import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage;
  4 +import lombok.Data;
  5 +import lombok.EqualsAndHashCode;
  6 +
  7 +/**
  8 + * 敏感词的刷新 Message
  9 + */
  10 +@Data
  11 +@EqualsAndHashCode(callSuper = true)
  12 +public class SensitiveWordRefreshMessage extends AbstractChannelMessage {
  13 +
  14 + @Override
  15 + public String getChannel() {
  16 + return "system.sensitive-word.refresh";
  17 + }
  18 +
  19 +}
... ...
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/mq/producer/sensitiveword/SensitiveWordProducer.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.mq.producer.sensitiveword;
  2 +
  3 +import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;
  4 +import cn.iocoder.yudao.module.system.mq.message.sensitiveword.SensitiveWordRefreshMessage;
  5 +import org.springframework.stereotype.Component;
  6 +
  7 +import javax.annotation.Resource;
  8 +
  9 +/**
  10 + * 敏感词相关的 Producer
  11 + */
  12 +@Component
  13 +public class SensitiveWordProducer {
  14 +
  15 + @Resource
  16 + private RedisMQTemplate redisMQTemplate;
  17 +
  18 + /**
  19 + * 发送 {@link SensitiveWordRefreshMessage} 消息
  20 + */
  21 + public void sendSensitiveWordRefreshMessage() {
  22 + SensitiveWordRefreshMessage message = new SensitiveWordRefreshMessage();
  23 + redisMQTemplate.send(message);
  24 + }
  25 +
  26 +}
... ...
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/sensitiveword/SensitiveWordService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.service.sensitiveword;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordCreateReqVO;
  5 +import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordExportReqVO;
  6 +import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordPageReqVO;
  7 +import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordUpdateReqVO;
  8 +import cn.iocoder.yudao.module.system.dal.dataobject.sensitiveword.SensitiveWordDO;
  9 +
  10 +import javax.validation.Valid;
  11 +import java.util.List;
  12 +import java.util.Set;
  13 +
  14 +/**
  15 + * 敏感词 Service 接口
  16 + *
  17 + * @author 永不言败
  18 + */
  19 +public interface SensitiveWordService {
  20 +
  21 + /**
  22 + * 初始化本地缓存
  23 + */
  24 + void initLocalCache();
  25 +
  26 + /**
  27 + * 创建敏感词
  28 + *
  29 + * @param createReqVO 创建信息
  30 + * @return 编号
  31 + */
  32 + Long createSensitiveWord(@Valid SensitiveWordCreateReqVO createReqVO);
  33 +
  34 + /**
  35 + * 更新敏感词
  36 + *
  37 + * @param updateReqVO 更新信息
  38 + */
  39 + void updateSensitiveWord(@Valid SensitiveWordUpdateReqVO updateReqVO);
  40 +
  41 + /**
  42 + * 删除敏感词
  43 + *
  44 + * @param id 编号
  45 + */
  46 + void deleteSensitiveWord(Long id);
  47 +
  48 + /**
  49 + * 获得敏感词
  50 + *
  51 + * @param id 编号
  52 + * @return 敏感词
  53 + */
  54 + SensitiveWordDO getSensitiveWord(Long id);
  55 +
  56 + /**
  57 + * 获得敏感词列表
  58 + *
  59 + * @return 敏感词列表
  60 + */
  61 + List<SensitiveWordDO> getSensitiveWordList();
  62 +
  63 + /**
  64 + * 获得敏感词分页
  65 + *
  66 + * @param pageReqVO 分页查询
  67 + * @return 敏感词分页
  68 + */
  69 + PageResult<SensitiveWordDO> getSensitiveWordPage(SensitiveWordPageReqVO pageReqVO);
  70 +
  71 + /**
  72 + * 获得敏感词列表, 用于 Excel 导出
  73 + *
  74 + * @param exportReqVO 查询条件
  75 + * @return 敏感词列表
  76 + */
  77 + List<SensitiveWordDO> getSensitiveWordList(SensitiveWordExportReqVO exportReqVO);
  78 +
  79 + /**
  80 + * 获得所有敏感词的标签数组
  81 + *
  82 + * @return 标签数组
  83 + */
  84 + Set<String> getSensitiveWordTags();
  85 +
  86 +}
... ...
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/sensitiveword/SensitiveWordServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.service.sensitiveword;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  6 +import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordCreateReqVO;
  7 +import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordExportReqVO;
  8 +import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordPageReqVO;
  9 +import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordUpdateReqVO;
  10 +import cn.iocoder.yudao.module.system.convert.sensitiveword.SensitiveWordConvert;
  11 +import cn.iocoder.yudao.module.system.dal.dataobject.sensitiveword.SensitiveWordDO;
  12 +import cn.iocoder.yudao.module.system.dal.mysql.sensitiveword.SensitiveWordMapper;
  13 +import cn.iocoder.yudao.module.system.mq.producer.sensitiveword.SensitiveWordProducer;
  14 +import lombok.Getter;
  15 +import lombok.extern.slf4j.Slf4j;
  16 +import org.springframework.context.annotation.Lazy;
  17 +import org.springframework.scheduling.annotation.Scheduled;
  18 +import org.springframework.stereotype.Service;
  19 +import org.springframework.validation.annotation.Validated;
  20 +
  21 +import javax.annotation.PostConstruct;
  22 +import javax.annotation.Resource;
  23 +import java.util.*;
  24 +
  25 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  26 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
  27 +
  28 +/**
  29 + * 敏感词 Service 实现类
  30 + *
  31 + * @author 永不言败
  32 + */
  33 +@Service
  34 +@Slf4j
  35 +@Validated
  36 +public class SensitiveWordServiceImpl implements SensitiveWordService {
  37 +
  38 + /**
  39 + * 定时执行 {@link #schedulePeriodicRefresh()} 的周期
  40 + * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
  41 + */
  42 + private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
  43 +
  44 + /**
  45 + * 敏感词缓存
  46 + * key:敏感词编号 {@link SensitiveWordDO#getId()}
  47 + * <p>
  48 + * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
  49 + */
  50 + @Getter
  51 + private volatile Map<Long, SensitiveWordDO> sensitiveWordCache = Collections.emptyMap();
  52 +
  53 + /**
  54 + * 敏感词标签缓存
  55 + * key:敏感词编号 {@link SensitiveWordDO#getId()}
  56 + * <p>
  57 + * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
  58 + */
  59 + @Getter
  60 + private volatile Set<String> sensitiveWordTagsCache = Collections.emptySet();
  61 +
  62 + /**
  63 + * 缓存敏感词的最大更新时间,用于后续的增量轮询,判断是否有更新
  64 + */
  65 + @Getter
  66 + private volatile Date maxUpdateTime;
  67 +
  68 + @Resource
  69 + private SensitiveWordMapper sensitiveWordMapper;
  70 +
  71 + @Resource
  72 + private SensitiveWordProducer sensitiveWordProducer;
  73 +
  74 + @Resource
  75 + @Lazy
  76 + private SensitiveWordService self;
  77 +
  78 + /**
  79 + * 初始化 {@link #sensitiveWordCache} 缓存
  80 + */
  81 + @Override
  82 + @PostConstruct
  83 + public void initLocalCache() {
  84 + // 获取敏感词列表,如果有更新
  85 + List<SensitiveWordDO> sensitiveWordList = loadSensitiveWordIfUpdate(maxUpdateTime);
  86 + if (CollUtil.isEmpty(sensitiveWordList)) {
  87 + return;
  88 + }
  89 +
  90 + // 写入 sensitiveWordTagsCache 缓存
  91 + Set<String> tags = new HashSet<>();
  92 + sensitiveWordList.forEach(word -> tags.addAll(word.getTags()));
  93 + sensitiveWordTagsCache = tags;
  94 + // 写入 sensitiveWordCache 缓存
  95 + sensitiveWordCache = CollectionUtils.convertMap(sensitiveWordList, SensitiveWordDO::getId);
  96 + maxUpdateTime = CollectionUtils.getMaxValue(sensitiveWordList, SensitiveWordDO::getUpdateTime);
  97 + log.info("[initLocalCache][初始化 敏感词 数量为 {}]", sensitiveWordList.size());
  98 + }
  99 +
  100 + @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
  101 + public void schedulePeriodicRefresh() {
  102 + self.initLocalCache();
  103 + }
  104 +
  105 + /**
  106 + * 如果敏感词发生变化,从数据库中获取最新的全量敏感词。
  107 + * 如果未发生变化,则返回空
  108 + *
  109 + * @param maxUpdateTime 当前敏感词的最大更新时间
  110 + * @return 敏感词列表
  111 + */
  112 + private List<SensitiveWordDO> loadSensitiveWordIfUpdate(Date maxUpdateTime) {
  113 + // 第一步,判断是否要更新。
  114 + // 如果更新时间为空,说明 DB 一定有新数据
  115 + if (maxUpdateTime == null) {
  116 + log.info("[loadSensitiveWordIfUpdate][首次加载全量敏感词]");
  117 + } else { // 判断数据库中是否有更新的敏感词
  118 + if (sensitiveWordMapper.selectExistsByUpdateTimeAfter(maxUpdateTime) == null) {
  119 + return null;
  120 + }
  121 + log.info("[loadSensitiveWordIfUpdate][增量加载全量敏感词]");
  122 + }
  123 + // 第二步,如果有更新,则从数据库加载所有敏感词
  124 + return sensitiveWordMapper.selectList();
  125 + }
  126 +
  127 + @Override
  128 + public Long createSensitiveWord(SensitiveWordCreateReqVO createReqVO) {
  129 + // 校验唯一性
  130 + checkSensitiveWordNameUnique(null, createReqVO.getName());
  131 + // 插入
  132 + SensitiveWordDO sensitiveWord = SensitiveWordConvert.INSTANCE.convert(createReqVO);
  133 + sensitiveWordMapper.insert(sensitiveWord);
  134 + // 发送消息,刷新缓存
  135 + sensitiveWordProducer.sendSensitiveWordRefreshMessage();
  136 + return sensitiveWord.getId();
  137 + }
  138 +
  139 + @Override
  140 + public void updateSensitiveWord(SensitiveWordUpdateReqVO updateReqVO) {
  141 + // 校验唯一性
  142 + checkSensitiveWordExists(updateReqVO.getId());
  143 + checkSensitiveWordNameUnique(updateReqVO.getId(), updateReqVO.getName());
  144 + // 更新
  145 + SensitiveWordDO updateObj = SensitiveWordConvert.INSTANCE.convert(updateReqVO);
  146 + sensitiveWordMapper.updateById(updateObj);
  147 + // 发送消息,刷新缓存
  148 + sensitiveWordProducer.sendSensitiveWordRefreshMessage();
  149 + }
  150 +
  151 + @Override
  152 + public void deleteSensitiveWord(Long id) {
  153 + // 校验存在
  154 + checkSensitiveWordExists(id);
  155 + // 删除
  156 + sensitiveWordMapper.deleteById(id);
  157 + // 发送消息,刷新缓存
  158 + sensitiveWordProducer.sendSensitiveWordRefreshMessage();
  159 + }
  160 +
  161 + private void checkSensitiveWordNameUnique(Long id, String name) {
  162 + SensitiveWordDO word = sensitiveWordMapper.selectByName(name);
  163 + if (word == null) {
  164 + return;
  165 + }
  166 + // 如果 id 为空,说明不用比较是否为相同 id 的敏感词
  167 + if (id == null) {
  168 + throw exception(SENSITIVE_WORD_EXISTS);
  169 + }
  170 + if (!word.getId().equals(id)) {
  171 + throw exception(SENSITIVE_WORD_EXISTS);
  172 + }
  173 + }
  174 +
  175 + private void checkSensitiveWordExists(Long id) {
  176 + if (sensitiveWordMapper.selectById(id) == null) {
  177 + throw exception(SENSITIVE_WORD_NOT_EXISTS);
  178 + }
  179 + }
  180 +
  181 + @Override
  182 + public SensitiveWordDO getSensitiveWord(Long id) {
  183 + return sensitiveWordMapper.selectById(id);
  184 + }
  185 +
  186 + @Override
  187 + public List<SensitiveWordDO> getSensitiveWordList() {
  188 + return sensitiveWordMapper.selectList();
  189 + }
  190 +
  191 + @Override
  192 + public PageResult<SensitiveWordDO> getSensitiveWordPage(SensitiveWordPageReqVO pageReqVO) {
  193 + return sensitiveWordMapper.selectPage(pageReqVO);
  194 + }
  195 +
  196 + @Override
  197 + public List<SensitiveWordDO> getSensitiveWordList(SensitiveWordExportReqVO exportReqVO) {
  198 + return sensitiveWordMapper.selectList(exportReqVO);
  199 + }
  200 +
  201 + @Override
  202 + public Set<String> getSensitiveWordTags() {
  203 + return sensitiveWordTagsCache;
  204 + }
  205 +
  206 +}
... ...
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/util/collection/SimpleTrie.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.util.collection;
  2 +
  3 +import java.util.*;
  4 +
  5 +/**
  6 + * 基于前缀树,实现敏感词的校验
  7 + * <p>
  8 + * 相比 Apache Common 提供的 PatriciaTrie 来说,性能可能会更加好一些。
  9 + *
  10 + * @author 芋道源码
  11 + */
  12 +@SuppressWarnings("unchecked")
  13 +public class SimpleTrie {
  14 +
  15 + /**
  16 + * 一个敏感词结束后对应的 key
  17 + */
  18 + private static final Character CHARACTER_END = '\0';
  19 +
  20 + /**
  21 + * 使用敏感词,构建的前缀树
  22 + */
  23 + private final Map<Character, Object> children;
  24 +
  25 + /**
  26 + * 基于字符串,构建前缀树
  27 + *
  28 + * @param strs 字符串数组
  29 + */
  30 + public SimpleTrie(List<String> strs) {
  31 + children = new HashMap<>();
  32 + // 构建树
  33 + Collections.sort(strs); // 排序,优先使用较短的前缀
  34 + for (String str : strs) {
  35 + Map<Character, Object> child = children;
  36 + // 遍历每个字符
  37 + for (Character c : str.toCharArray()) {
  38 + // 如果已经到达结束,就没必要在添加更长的敏感词。
  39 + // 例如说,有两个敏感词是:吃饭啊、吃饭。输入一句话是 “我要吃饭啊”,则只要匹配到 “吃饭” 这个敏感词即可。
  40 + if (child.containsKey(CHARACTER_END)) {
  41 + break;
  42 + }
  43 + if (!child.containsKey(c)) {
  44 + child.put(c, new HashMap<>());
  45 + }
  46 + child = (Map<Character, Object>) child.get(c);
  47 + }
  48 + // 结束
  49 + child.put(CHARACTER_END, null);
  50 + }
  51 + }
  52 +
  53 + /**
  54 + * 验证文本是否合法,即不包含敏感词
  55 + *
  56 + * @param text 文本
  57 + * @return 是否 ok
  58 + */
  59 + public boolean isValid(String text) {
  60 + // 遍历 text,使用每一个 [i, n) 段的字符串,使用 children 前缀树匹配,是否包含敏感词
  61 + for (int i = 0; i < text.length() - 1; i++) {
  62 + Map<Character, Object> child = (Map<Character, Object>) children.get(text.charAt(i));
  63 + if (child == null) {
  64 + continue;
  65 + }
  66 + boolean ok = recursion(text, i + 1, child);
  67 + if (!ok) {
  68 + return false;
  69 + }
  70 + }
  71 + return true;
  72 + }
  73 +
  74 + /**
  75 + * 验证文本从指定位置开始,是否包含某个敏感词
  76 + *
  77 + * @param text 文本
  78 + * @param index 开始位置
  79 + * @param child 节点(当前遍历到的)
  80 + * @return 是否包含
  81 + */
  82 + private boolean recursion(String text, int index, Map<Character, Object> child) {
  83 + if (index == text.length()) {
  84 + return true;
  85 + }
  86 + child = (Map<Character, Object>) child.get(text.charAt(index));
  87 + return child == null || !child.containsKey(CHARACTER_END) && recursion(text, ++index, child);
  88 + }
  89 +
  90 + /**
  91 + * 获得文本所包含的不合法的敏感词
  92 + *
  93 + * 注意,才当即最短匹配原则。例如说:当敏感词存在 “煞笔”,“煞笔二货 ”时,只会返回 “煞笔”。
  94 + *
  95 + * @param text 文本
  96 + * @return 匹配的敏感词
  97 + */
  98 + public List<String> validate(String text) {
  99 + Set<String> results = new HashSet<>();
  100 + for (int i = 0; i < text.length() - 1; i++) {
  101 + Character c = text.charAt(i);
  102 + Map<Character, Object> child = (Map<Character, Object>) children.get(c);
  103 + if (child == null) {
  104 + continue;
  105 + }
  106 + StringBuilder result = new StringBuilder().append(c);
  107 + boolean ok = recursionWithResult(text, i + 1, child, result);
  108 + if (!ok) {
  109 + results.add(result.toString());
  110 + }
  111 + }
  112 + return new ArrayList<>(results);
  113 + }
  114 +
  115 + /**
  116 + * 返回文本从 index 开始的敏感词,并使用 StringBuilder 参数进行返回
  117 + *
  118 + * 逻辑和 {@link #recursion(String, int, Map)} 是一致,只是多了 result 返回结果
  119 + *
  120 + * @param text 文本
  121 + * @param index 开始未知
  122 + * @param child 节点(当前遍历到的)
  123 + * @param result 返回敏感词
  124 + * @return 是否有敏感词
  125 + */
  126 + @SuppressWarnings("unchecked")
  127 + private static boolean recursionWithResult(String text, int index, Map<Character, Object> child, StringBuilder result) {
  128 + if (index == text.length()) {
  129 + return true;
  130 + }
  131 + Character c = text.charAt(index);
  132 + child = (Map<Character, Object>) child.get(c);
  133 + if (child == null) {
  134 + return true;
  135 + }
  136 + if (child.containsKey(CHARACTER_END)) {
  137 + result.append(c);
  138 + return false;
  139 + }
  140 + return recursionWithResult(text, ++index, child, result.append(c));
  141 + }
  142 +
  143 +}
... ...
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/util/package-info.java 0 → 100644
  1 +/**
  2 + * 每个模块的 util 包,放专属当前模块的 Utils 工具类
  3 + */
  4 +package cn.iocoder.yudao.module.system.util;
... ...
yudao-module-system/yudao-module-system-impl/src/test/resources/sql/clean.sql
... ... @@ -17,3 +17,4 @@ DELETE FROM &quot;system_error_code&quot;;
17 17 DELETE FROM "system_social_user";
18 18 DELETE FROM "system_tenant";
19 19 DELETE FROM "system_tenant_package";
  20 +DELETE FROM "system_sensitive_word";
... ...
yudao-module-system/yudao-module-system-impl/src/test/resources/sql/create_tables.sql
... ... @@ -426,3 +426,17 @@ CREATE TABLE IF NOT EXISTS &quot;system_tenant_package&quot; (
426 426 "deleted" bit NOT NULL DEFAULT FALSE,
427 427 PRIMARY KEY ("id")
428 428 ) COMMENT '租户套餐表';
  429 +
  430 +CREATE TABLE IF NOT EXISTS "system_sensitive_word" (
  431 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  432 + "name" varchar(255) NOT NULL,
  433 + "tags" varchar(1024) NOT NULL,
  434 + "status" bit NOT NULL DEFAULT FALSE,
  435 + "description" varchar(512),
  436 + "creator" varchar(64) DEFAULT '',
  437 + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  438 + "updater" varchar(64) DEFAULT '',
  439 + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  440 + "deleted" bit NOT NULL DEFAULT FALSE,
  441 + PRIMARY KEY ("id")
  442 +) COMMENT '系统敏感词';
... ...