# Conflicts:
#	yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/errorcode/config/ErrorCodeProperties.java
#	yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/errorcode/ErrorCodeApi.java
#	yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/errorcode/dto/ErrorCodeAutoGenerateReqDTO.java
#	yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/errorcode/ErrorCodeApiImpl.java
#	yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/sensitiveword/SensitiveWordApiImpl.java
#	yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/errorcode/ErrorCodeController.java
#	yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/errorcode/vo/ErrorCodeRespVO.java
#	yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/errorcode/vo/ErrorCodeSaveReqVO.java
#	yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sensitiveword/SensitiveWordController.java
#	yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sensitiveword/vo/SensitiveWordSaveVO.java
#	yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/errorcode/ErrorCodeService.java
#	yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/errorcode/ErrorCodeServiceImpl.java
#	yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sensitiveword/SensitiveWordService.java
#	yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sensitiveword/SensitiveWordServiceImpl.java
#	yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/errorcode/ErrorCodeServiceTest.java
#	yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sensitiveword/SensitiveWordServiceImplTest.java
This commit is contained in:
YunaiV 2024-04-22 19:56:17 +08:00
commit a27c040a8f
42 changed files with 3 additions and 2635 deletions

View File

@ -6,74 +6,24 @@ import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstant
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* {@link ServiceException} 工具类
*
* 目的在于格式化异常信息提示
* 考虑到 String.format 在参数不正确时会报错因此使用 {} 作为占位符并使用 {@link #doFormat(int, String, Object...)} 方法来格式化
*
* 因为 {@link #MESSAGES} 里面默认是没有异常信息提示的模板的所以需要使用方自己初始化进去目前想到的有几种方式
*
* 1. 异常提示信息写在枚举类中例如说cn.iocoder.oceans.user.api.constants.ErrorCodeEnum + ServiceExceptionConfiguration
* 2. 异常提示信息写在 .properties 等等配置文件
* 3. 异常提示信息写在 Apollo 等等配置中心中从而实现可动态刷新
* 4. 异常提示信息存储在 db 等等数据库中从而实现可动态刷新
*/
@Slf4j
public class ServiceExceptionUtil {
/**
* 错误码提示模板
*/
private static final ConcurrentMap<Integer, String> MESSAGES = new ConcurrentHashMap<>();
public static void putAll(Map<Integer, String> messages) {
ServiceExceptionUtil.MESSAGES.putAll(messages);
}
public static void put(Integer code, String message) {
ServiceExceptionUtil.MESSAGES.put(code, message);
}
public static void delete(Integer code, String message) {
ServiceExceptionUtil.MESSAGES.remove(code, message);
}
// ========== ServiceException 的集成 ==========
public static ServiceException exception(ErrorCode errorCode) {
String messagePattern = MESSAGES.getOrDefault(errorCode.getCode(), errorCode.getMsg());
return exception0(errorCode.getCode(), messagePattern);
return exception0(errorCode.getCode(), errorCode.getMsg());
}
public static ServiceException exception(ErrorCode errorCode, Object... params) {
String messagePattern = MESSAGES.getOrDefault(errorCode.getCode(), errorCode.getMsg());
return exception0(errorCode.getCode(), messagePattern, params);
}
/**
* 创建指定编号的 ServiceException 的异常
*
* @param code 编号
* @return 异常
*/
public static ServiceException exception(Integer code) {
return exception0(code, MESSAGES.get(code));
}
/**
* 创建指定编号的 ServiceException 的异常
*
* @param code 编号
* @param params 消息提示的占位符对应的参数
* @return 异常
*/
public static ServiceException exception(Integer code, Object... params) {
return exception0(code, MESSAGES.get(code), params);
return exception0(errorCode.getCode(), errorCode.getMsg(), params);
}
public static ServiceException exception0(Integer code, String messagePattern, Object... params) {

View File

@ -1,30 +0,0 @@
package cn.iocoder.yudao.framework.errorcode.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
* 错误码的配置属性类
*
* @author dlyan
*/
@ConfigurationProperties("yudao.error-code")
@Data
@Validated
public class ErrorCodeProperties {
/**
* 是否开启
*/
private Boolean enable = true;
/**
* 错误码枚举类
*/
@NotNull(message = "错误码枚举类不能为空")
private List<String> constantsClassList;
}

View File

@ -1,39 +0,0 @@
package cn.iocoder.yudao.framework.errorcode.config;
import cn.iocoder.yudao.framework.errorcode.core.generator.ErrorCodeAutoGenerator;
import cn.iocoder.yudao.framework.errorcode.core.generator.ErrorCodeAutoGeneratorImpl;
import cn.iocoder.yudao.framework.errorcode.core.loader.ErrorCodeLoader;
import cn.iocoder.yudao.framework.errorcode.core.loader.ErrorCodeLoaderImpl;
import cn.iocoder.yudao.module.system.api.errorcode.ErrorCodeApi;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* 错误码配置类
*
* @author 芋道源码
*/
@AutoConfiguration
@ConditionalOnProperty(prefix = "yudao.error-code", value = "enable", matchIfMissing = true) // 允许使用 yudao.error-code.enable=false 禁用访问日志
@EnableConfigurationProperties(ErrorCodeProperties.class)
@EnableScheduling // 开启调度任务的功能因为 ErrorCodeRemoteLoader 通过定时刷新错误码
public class YudaoErrorCodeAutoConfiguration {
@Bean
public ErrorCodeAutoGenerator errorCodeAutoGenerator(@Value("${spring.application.name}") String applicationName,
ErrorCodeProperties errorCodeProperties,
ErrorCodeApi errorCodeApi) {
return new ErrorCodeAutoGeneratorImpl(applicationName, errorCodeProperties.getConstantsClassList(), errorCodeApi);
}
@Bean
public ErrorCodeLoader errorCodeLoader(@Value("${spring.application.name}") String applicationName,
ErrorCodeApi errorCodeApi) {
return new ErrorCodeLoaderImpl(applicationName, errorCodeApi);
}
}

View File

@ -1,15 +0,0 @@
package cn.iocoder.yudao.framework.errorcode.core.generator;
/**
* 错误码的自动生成器
*
* @author dylan
*/
public interface ErrorCodeAutoGenerator {
/**
* 将配置类到错误码写入数据库
*/
void execute();
}

View File

@ -1,108 +0,0 @@
package cn.iocoder.yudao.framework.errorcode.core.generator;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.module.system.api.errorcode.ErrorCodeApi;
import cn.iocoder.yudao.module.system.api.errorcode.dto.ErrorCodeAutoGenerateReqDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* ErrorCodeAutoGenerator 的实现类
* 目的是扫描指定的 {@link #constantsClassList} 写入到 system 服务中
*
* @author dylan
*/
@RequiredArgsConstructor
@Slf4j
public class ErrorCodeAutoGeneratorImpl implements ErrorCodeAutoGenerator {
/**
* 应用分组
*/
private final String applicationName;
/**
* 错误码枚举类
*/
private final List<String> constantsClassList;
/**
* 错误码 Api
*/
private final ErrorCodeApi errorCodeApi;
@Override
@EventListener(ApplicationReadyEvent.class)
@Async // 异步保证项目的启动过程毕竟非关键流程
public void execute() {
// 第一步解析错误码
List<ErrorCodeAutoGenerateReqDTO> autoGenerateDTOs = parseErrorCode();
log.info("[execute][解析到错误码数量为 ({}) 个]", autoGenerateDTOs.size());
// 第二步写入到 system 服务
try {
errorCodeApi.autoGenerateErrorCodeList(autoGenerateDTOs);
log.info("[execute][写入到 system 组件完成]");
} catch (Exception ex) {
log.error("[execute][写入到 system 组件失败({})]", ExceptionUtil.getRootCauseMessage(ex));
}
}
/**
* 解析 constantsClassList 变量转换成错误码数组
*
* @return 错误码数组
*/
private List<ErrorCodeAutoGenerateReqDTO> parseErrorCode() {
// 校验 errorCodeConstantsClass 参数
if (CollUtil.isEmpty(constantsClassList)) {
log.info("[execute][未配置 yudao.error-code.constants-class-list 配置项,不进行自动写入到 system 服务中]");
return new ArrayList<>();
}
// 解析错误码
List<ErrorCodeAutoGenerateReqDTO> autoGenerateDTOs = new ArrayList<>();
constantsClassList.forEach(constantsClass -> {
try {
// 解析错误码枚举类
Class<?> errorCodeConstantsClazz = ClassUtil.loadClass(constantsClass);
// 解析错误码
autoGenerateDTOs.addAll(parseErrorCode(errorCodeConstantsClazz));
} catch (Exception ex) {
log.warn("[parseErrorCode][constantsClass({}) 加载失败({})]", constantsClass,
ExceptionUtil.getRootCauseMessage(ex));
}
});
return autoGenerateDTOs;
}
/**
* 解析错误码类获得错误码数组
*
* @return 错误码数组
*/
private List<ErrorCodeAutoGenerateReqDTO> parseErrorCode(Class<?> constantsClass) {
List<ErrorCodeAutoGenerateReqDTO> autoGenerateDTOs = new ArrayList<>();
Arrays.stream(constantsClass.getFields()).forEach(field -> {
if (field.getType() != ErrorCode.class) {
return;
}
// 转换成 ErrorCodeAutoGenerateReqDTO 对象
ErrorCode errorCode = (ErrorCode) ReflectUtil.getFieldValue(constantsClass, field);
autoGenerateDTOs.add(new ErrorCodeAutoGenerateReqDTO().setApplicationName(applicationName)
.setCode(errorCode.getCode()).setMessage(errorCode.getMsg()));
});
return autoGenerateDTOs;
}
}

View File

@ -1,34 +0,0 @@
package cn.iocoder.yudao.framework.errorcode.core.loader;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
/**
* 错误码加载器
*
* 注意错误码最终加载到 {@link ServiceExceptionUtil} MESSAGES 变量中
*
* @author dlyan
*/
public interface ErrorCodeLoader {
/**
* 添加错误码
*
* @param code 错误码的编号
* @param msg 错误码的提示
*/
default void putErrorCode(Integer code, String msg) {
ServiceExceptionUtil.put(code, msg);
}
/**
* 刷新错误码
*/
void refreshErrorCodes();
/**
* 加载错误码
*/
void loadErrorCodes();
}

View File

@ -1,82 +0,0 @@
package cn.iocoder.yudao.framework.errorcode.core.loader;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.module.system.api.errorcode.ErrorCodeApi;
import cn.iocoder.yudao.module.system.api.errorcode.dto.ErrorCodeRespDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import java.time.LocalDateTime;
import java.util.List;
/**
* ErrorCodeLoader 的实现类 infra 的数据库中加载错误码
*
* 考虑到错误码会刷新所以按照 {@link #REFRESH_ERROR_CODE_PERIOD} 频率增量加载错误码
*
* @author dlyan
*/
@RequiredArgsConstructor
@Slf4j
public class ErrorCodeLoaderImpl implements ErrorCodeLoader {
/**
* 刷新错误码的频率单位毫秒
*/
private static final int REFRESH_ERROR_CODE_PERIOD = 60 * 1000;
/**
* 应用分组
*/
private final String applicationName;
/**
* 错误码 Api
*/
private final ErrorCodeApi errorCodeApi;
/**
* 缓存错误码的最大更新时间用于后续的增量轮询判断是否有更新
*/
private LocalDateTime maxUpdateTime;
@Override
@EventListener(ApplicationReadyEvent.class)
@Async // 异步保证项目的启动过程毕竟非关键流程
public void loadErrorCodes() {
loadErrorCodes0();
}
@Override
@Scheduled(fixedDelay = REFRESH_ERROR_CODE_PERIOD, initialDelay = REFRESH_ERROR_CODE_PERIOD)
public void refreshErrorCodes() {
loadErrorCodes0();
}
private void loadErrorCodes0() {
try {
// 加载错误码
List<ErrorCodeRespDTO> errorCodeRespDTOs = errorCodeApi.getErrorCodeList(applicationName, maxUpdateTime);
if (CollUtil.isEmpty(errorCodeRespDTOs)) {
return;
}
log.info("[loadErrorCodes0][加载到 ({}) 个错误码]", errorCodeRespDTOs.size());
// 刷新错误码的缓存
errorCodeRespDTOs.forEach(errorCodeRespDTO -> {
// 写入到错误码的缓存
putErrorCode(errorCodeRespDTO.getCode(), errorCodeRespDTO.getMessage());
// 记录下更新时间方便增量更新
maxUpdateTime = DateUtils.max(maxUpdateTime, errorCodeRespDTO.getUpdateTime());
});
} catch (Exception ex) {
log.error("[loadErrorCodes0][加载错误码失败({})]", ExceptionUtil.getRootCauseMessage(ex));
}
}
}

View File

@ -1,10 +0,0 @@
/**
* 错误码 ErrorCode 的自动配置功能提供如下功能
*
* 1. 远程读取项目启动时 system-service 服务读取数据库中的 ErrorCode 错误码实现错误码的提水可配置
* 2. 自动更新管理员在管理后台修数据库中的 ErrorCode 错误码时项目自动从 system-service 服务加载最新的 ErrorCode 错误码
* 3. 自动写入项目启动时将项目本地的错误码写到 system-server 服务中方便管理员在管理后台编辑
*
* @author 芋道源码
*/
package cn.iocoder.yudao.framework.errorcode;

View File

@ -3,5 +3,4 @@ cn.iocoder.yudao.framework.jackson.config.YudaoJacksonAutoConfiguration
cn.iocoder.yudao.framework.swagger.config.YudaoSwaggerAutoConfiguration
cn.iocoder.yudao.framework.web.config.YudaoWebAutoConfiguration
cn.iocoder.yudao.framework.xss.config.YudaoXssAutoConfiguration
cn.iocoder.yudao.framework.banner.config.YudaoBannerAutoConfiguration
cn.iocoder.yudao.framework.errorcode.config.YudaoErrorCodeAutoConfiguration
cn.iocoder.yudao.framework.banner.config.YudaoBannerAutoConfiguration

View File

@ -1,35 +0,0 @@
package cn.iocoder.yudao.module.system.api.errorcode;
import cn.iocoder.yudao.module.system.api.errorcode.dto.ErrorCodeAutoGenerateReqDTO;
import cn.iocoder.yudao.module.system.api.errorcode.dto.ErrorCodeRespDTO;
import javax.validation.Valid;
import java.time.LocalDateTime;
import java.util.List;
/**
* 错误码 Api 接口
*
* @author 芋道源码
*/
public interface ErrorCodeApi {
/**
* 自动创建错误码
*
* @param autoGenerateDTOs 错误码信息
*/
void autoGenerateErrorCodeList(@Valid List<ErrorCodeAutoGenerateReqDTO> autoGenerateDTOs);
/**
* 增量获得错误码数组
*
* 如果 minUpdateTime 为空时则获取所有错误码
*
* @param applicationName 应用名
* @param minUpdateTime 最小更新时间
* @return 错误码数组
*/
List<ErrorCodeRespDTO> getErrorCodeList(String applicationName, LocalDateTime minUpdateTime);
}

View File

@ -1,34 +0,0 @@
package cn.iocoder.yudao.module.system.api.errorcode.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
/**
* 错误码自动生成 DTO
*
* @author dylan
*/
@Data
@Accessors(chain = true)
public class ErrorCodeAutoGenerateReqDTO {
/**
* 应用名
*/
@NotNull(message = "应用名不能为空")
private String applicationName;
/**
* 错误码编码
*/
@NotNull(message = "错误码编码不能为空")
private Integer code;
/**
* 错误码错误提示
*/
@NotEmpty(message = "错误码错误提示不能为空")
private String message;
}

View File

@ -1,28 +0,0 @@
package cn.iocoder.yudao.module.system.api.errorcode.dto;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 错误码的 Response DTO
*
* @author 芋道源码
*/
@Data
public class ErrorCodeRespDTO {
/**
* 错误码编码
*/
private Integer code;
/**
* 错误码错误提示
*/
private String message;
/**
* 更新时间
*/
private LocalDateTime updateTime;
}

View File

@ -1,30 +0,0 @@
package cn.iocoder.yudao.module.system.api.sensitiveword;
import java.util.List;
/**
* 敏感词 API 接口
*
* @author 永不言败
*/
public interface SensitiveWordApi {
/**
* 获得文本所包含的不合法的敏感词数组
*
* @param text 文本
* @param tags 标签数组
* @return 不合法的敏感词数组
*/
List<String> validateText(String text, List<String> tags);
/**
* 判断文本是否包含敏感词
*
* @param text 文本
* @param tags 表述数组
* @return 是否包含
*/
boolean isTextValid(String text, List<String> tags);
}

View File

@ -115,10 +115,6 @@ public interface ErrorCodeConstants {
ErrorCode TENANT_PACKAGE_USED = new ErrorCode(1_002_016_001, "租户正在使用该套餐,请给租户重新设置套餐后再尝试删除");
ErrorCode TENANT_PACKAGE_DISABLE = new ErrorCode(1_002_016_002, "名字为【{}】的租户套餐已被禁用");
// ========== 错误码模块 1-002-017-000 ==========
ErrorCode ERROR_CODE_NOT_EXISTS = new ErrorCode(1_002_017_000, "错误码不存在");
ErrorCode ERROR_CODE_DUPLICATE = new ErrorCode(1_002_017_001, "已经存在编码为【{}】的错误码");
// ========== 社交用户 1-002-018-000 ==========
ErrorCode SOCIAL_USER_AUTH_FAILURE = new ErrorCode(1_002_018_000, "社交授权失败,原因是:{}");
ErrorCode SOCIAL_USER_NOT_FOUND = new ErrorCode(1_002_018_001, "社交授权失败,找不到对应的用户");
@ -127,10 +123,6 @@ public interface ErrorCodeConstants {
ErrorCode SOCIAL_CLIENT_NOT_EXISTS = new ErrorCode(1_002_018_201, "社交客户端不存在");
ErrorCode SOCIAL_CLIENT_UNIQUE = new ErrorCode(1_002_018_202, "社交客户端已存在配置");
// ========== 系统敏感词 1-002-019-000 =========
ErrorCode SENSITIVE_WORD_NOT_EXISTS = new ErrorCode(1_002_019_000, "系统敏感词在所有标签中都不存在");
ErrorCode SENSITIVE_WORD_EXISTS = new ErrorCode(1_002_019_001, "系统敏感词已在标签中存在");
// ========== OAuth2 客户端 1-002-020-000 =========
ErrorCode OAUTH2_CLIENT_NOT_EXISTS = new ErrorCode(1_002_020_000, "OAuth2 客户端不存在");
ErrorCode OAUTH2_CLIENT_EXISTS = new ErrorCode(1_002_020_001, "OAuth2 客户端编号已存在");

View File

@ -1,39 +0,0 @@
package cn.iocoder.yudao.module.system.enums.errorcode;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* 错误码的类型枚举
*
* @author dylan
*/
@AllArgsConstructor
@Getter
public enum ErrorCodeTypeEnum implements IntArrayValuable {
/**
* 自动生成
*/
AUTO_GENERATION(1),
/**
* 手动编辑
*/
MANUAL_OPERATION(2);
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ErrorCodeTypeEnum::getType).toArray();
/**
* 类型
*/
private final Integer type;
@Override
public int[] array() {
return ARRAYS;
}
}

View File

@ -1,33 +0,0 @@
package cn.iocoder.yudao.module.system.api.errorcode;
import cn.iocoder.yudao.module.system.api.errorcode.dto.ErrorCodeAutoGenerateReqDTO;
import cn.iocoder.yudao.module.system.api.errorcode.dto.ErrorCodeRespDTO;
import cn.iocoder.yudao.module.system.service.errorcode.ErrorCodeService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
/**
* 错误码 Api 实现类
*
* @author 芋道源码
*/
@Service
public class ErrorCodeApiImpl implements ErrorCodeApi {
@Resource
private ErrorCodeService errorCodeService;
@Override
public void autoGenerateErrorCodeList(List<ErrorCodeAutoGenerateReqDTO> autoGenerateDTOs) {
errorCodeService.autoGenerateErrorCodes(autoGenerateDTOs);
}
@Override
public List<ErrorCodeRespDTO> getErrorCodeList(String applicationName, LocalDateTime minUpdateTime) {
return errorCodeService.getErrorCodeList(applicationName, minUpdateTime);
}
}

View File

@ -1,29 +0,0 @@
package cn.iocoder.yudao.module.system.api.sensitiveword;
import cn.iocoder.yudao.module.system.service.sensitiveword.SensitiveWordService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
* 敏感词 API 实现类
*
* @author 永不言败
*/
@Service
public class SensitiveWordApiImpl implements SensitiveWordApi {
@Resource
private SensitiveWordService sensitiveWordService;
@Override
public List<String> validateText(String text, List<String> tags) {
return sensitiveWordService.validateText(text, tags);
}
@Override
public boolean isTextValid(String text, List<String> tags) {
return sensitiveWordService.isTextValid(text, tags);
}
}

View File

@ -1,13 +0,0 @@
### 创建错误码
POST {{baseUrl}}/inra/error-code/create
Authorization: Bearer {{token}}
Content-Type: application/json
tenant-id: {{adminTenentId}}
{
"code": 200,
"message": "成功",
"group": "test",
"type": 1
}

View File

@ -1,93 +0,0 @@
package cn.iocoder.yudao.module.system.controller.admin.errorcode;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.module.system.controller.admin.errorcode.vo.ErrorCodePageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.errorcode.vo.ErrorCodeRespVO;
import cn.iocoder.yudao.module.system.controller.admin.errorcode.vo.ErrorCodeSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.errorcode.ErrorCodeDO;
import cn.iocoder.yudao.module.system.service.errorcode.ErrorCodeService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.List;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 错误码")
@RestController
@RequestMapping("/system/error-code")
@Validated
public class ErrorCodeController {
@Resource
private ErrorCodeService errorCodeService;
@PostMapping("/create")
@Operation(summary = "创建错误码")
@PreAuthorize("@ss.hasPermission('system:error-code:create')")
public CommonResult<Long> createErrorCode(@Valid @RequestBody ErrorCodeSaveReqVO createReqVO) {
return success(errorCodeService.createErrorCode(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新错误码")
@PreAuthorize("@ss.hasPermission('system:error-code:update')")
public CommonResult<Boolean> updateErrorCode(@Valid @RequestBody ErrorCodeSaveReqVO updateReqVO) {
errorCodeService.updateErrorCode(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除错误码")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('system:error-code:delete')")
public CommonResult<Boolean> deleteErrorCode(@RequestParam("id") Long id) {
errorCodeService.deleteErrorCode(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得错误码")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system:error-code:query')")
public CommonResult<ErrorCodeRespVO> getErrorCode(@RequestParam("id") Long id) {
ErrorCodeDO errorCode = errorCodeService.getErrorCode(id);
return success(BeanUtils.toBean(errorCode, ErrorCodeRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得错误码分页")
@PreAuthorize("@ss.hasPermission('system:error-code:query')")
public CommonResult<PageResult<ErrorCodeRespVO>> getErrorCodePage(@Valid ErrorCodePageReqVO pageVO) {
PageResult<ErrorCodeDO> pageResult = errorCodeService.getErrorCodePage(pageVO);
return success(BeanUtils.toBean(pageResult, ErrorCodeRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出错误码 Excel")
@PreAuthorize("@ss.hasPermission('system:error-code:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportErrorCodeExcel(@Valid ErrorCodePageReqVO exportReqVO,
HttpServletResponse response) throws IOException {
exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<ErrorCodeDO> list = errorCodeService.getErrorCodePage(exportReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "错误码.xls", "数据", ErrorCodeRespVO.class,
BeanUtils.toBean(list, ErrorCodeRespVO.class));
}
}

View File

@ -1,36 +0,0 @@
package cn.iocoder.yudao.module.system.controller.admin.errorcode.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 错误码分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ErrorCodePageReqVO extends PageParam {
@Schema(description = "错误码类型,参见 ErrorCodeTypeEnum 枚举类", example = "1")
private Integer type;
@Schema(description = "应用名", example = "dashboard")
private String applicationName;
@Schema(description = "错误码编码", example = "1234")
private Integer code;
@Schema(description = "错误码错误提示", example = "帅气")
private String message;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@Schema(description = "创建时间")
private LocalDateTime[] createTime;
}

View File

@ -1,48 +0,0 @@
package cn.iocoder.yudao.module.system.controller.admin.errorcode.vo;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.iocoder.yudao.module.system.enums.DictTypeConstants;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 错误码 Response VO")
@Data
@ExcelIgnoreUnannotated
public class ErrorCodeRespVO {
@Schema(description = "错误码编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@ExcelProperty("错误码编号")
private Long id;
@Schema(description = "错误码类型,参见 ErrorCodeTypeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty(value = "错误码类型", converter = DictConvert.class)
@DictFormat(DictTypeConstants.ERROR_CODE_TYPE)
private Integer type;
@Schema(description = "应用名", requiredMode = Schema.RequiredMode.REQUIRED, example = "dashboard")
@ExcelProperty("应用名")
private String applicationName;
@Schema(description = "错误码编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1234")
@ExcelProperty("错误码编码")
private Integer code;
@Schema(description = "错误码错误提示", requiredMode = Schema.RequiredMode.REQUIRED, example = "帅气")
@ExcelProperty("错误码错误提示")
private String message;
@Schema(description = "备注", example = "哈哈哈")
@ExcelProperty("备注")
private String memo;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@ -1,30 +0,0 @@
package cn.iocoder.yudao.module.system.controller.admin.errorcode.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotNull;
@Schema(description = "管理后台 - 错误码创建/修改 Request VO")
@Data
public class ErrorCodeSaveReqVO {
@Schema(description = "错误码编号", example = "1024")
private Long id;
@Schema(description = "应用名", requiredMode = Schema.RequiredMode.REQUIRED, example = "dashboard")
@NotNull(message = "应用名不能为空")
private String applicationName;
@Schema(description = "错误码编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1234")
@NotNull(message = "错误码编码不能为空")
private Integer code;
@Schema(description = "错误码错误提示", requiredMode = Schema.RequiredMode.REQUIRED, example = "帅气")
@NotNull(message = "错误码错误提示不能为空")
private String message;
@Schema(description = "备注", example = "哈哈哈")
private String memo;
}

View File

@ -1,4 +0,0 @@
### 请求 /system/sensitive-word/validate-text 接口 => 成功
GET {{baseUrl}}/system/sensitive-word/validate-text?text=XXX&tags=短信&tags=蔬菜
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}

View File

@ -1,108 +0,0 @@
package cn.iocoder.yudao.module.system.controller.admin.sensitiveword;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordRespVO;
import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordSaveVO;
import cn.iocoder.yudao.module.system.dal.dataobject.sensitiveword.SensitiveWordDO;
import cn.iocoder.yudao.module.system.service.sensitiveword.SensitiveWordService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 敏感词")
@RestController
@RequestMapping("/system/sensitive-word")
@Validated
public class SensitiveWordController {
@Resource
private SensitiveWordService sensitiveWordService;
@PostMapping("/create")
@Operation(summary = "创建敏感词")
@PreAuthorize("@ss.hasPermission('system:sensitive-word:create')")
public CommonResult<Long> createSensitiveWord(@Valid @RequestBody SensitiveWordSaveVO createReqVO) {
return success(sensitiveWordService.createSensitiveWord(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新敏感词")
@PreAuthorize("@ss.hasPermission('system:sensitive-word:update')")
public CommonResult<Boolean> updateSensitiveWord(@Valid @RequestBody SensitiveWordSaveVO updateReqVO) {
sensitiveWordService.updateSensitiveWord(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除敏感词")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('system:sensitive-word:delete')")
public CommonResult<Boolean> deleteSensitiveWord(@RequestParam("id") Long id) {
sensitiveWordService.deleteSensitiveWord(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得敏感词")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system:sensitive-word:query')")
public CommonResult<SensitiveWordRespVO> getSensitiveWord(@RequestParam("id") Long id) {
SensitiveWordDO sensitiveWord = sensitiveWordService.getSensitiveWord(id);
return success(BeanUtils.toBean(sensitiveWord, SensitiveWordRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得敏感词分页")
@PreAuthorize("@ss.hasPermission('system:sensitive-word:query')")
public CommonResult<PageResult<SensitiveWordRespVO>> getSensitiveWordPage(@Valid SensitiveWordPageReqVO pageVO) {
PageResult<SensitiveWordDO> pageResult = sensitiveWordService.getSensitiveWordPage(pageVO);
return success(BeanUtils.toBean(pageResult, SensitiveWordRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出敏感词 Excel")
@PreAuthorize("@ss.hasPermission('system:sensitive-word:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportSensitiveWordExcel(@Valid SensitiveWordPageReqVO exportReqVO,
HttpServletResponse response) throws IOException {
exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<SensitiveWordDO> list = sensitiveWordService.getSensitiveWordPage(exportReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "敏感词.xls", "数据", SensitiveWordRespVO.class,
BeanUtils.toBean(list, SensitiveWordRespVO.class));
}
@GetMapping("/get-tags")
@Operation(summary = "获取所有敏感词的标签数组")
@PreAuthorize("@ss.hasPermission('system:sensitive-word:query')")
public CommonResult<Set<String>> getSensitiveWordTagSet() {
return success(sensitiveWordService.getSensitiveWordTagSet());
}
@GetMapping("/validate-text")
@Operation(summary = "获得文本所包含的不合法的敏感词数组")
public CommonResult<List<String>> validateText(@RequestParam("text") String text,
@RequestParam(value = "tags", required = false) List<String> tags) {
return success(sensitiveWordService.validateText(text, tags));
}
}

View File

@ -1,33 +0,0 @@
package cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 敏感词分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SensitiveWordPageReqVO extends PageParam {
@Schema(description = "敏感词", example = "敏感词")
private String name;
@Schema(description = "标签", example = "短信,评论")
private String tag;
@Schema(description = "状态,参见 CommonStatusEnum 枚举类", example = "1")
private Integer status;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@Schema(description = "创建时间")
private LocalDateTime[] createTime;
}

View File

@ -1,45 +0,0 @@
package cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.iocoder.yudao.framework.excel.core.convert.JsonConvert;
import cn.iocoder.yudao.module.system.enums.DictTypeConstants;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 敏感词 Response VO")
@Data
public class SensitiveWordRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty("编号")
private Long id;
@Schema(description = "敏感词", requiredMode = Schema.RequiredMode.REQUIRED, example = "敏感词")
@ExcelProperty("敏感词")
private String name;
@Schema(description = "标签", requiredMode = Schema.RequiredMode.REQUIRED, example = "短信,评论")
@ExcelProperty(value = "标签", converter = JsonConvert.class)
private List<String> tags;
@Schema(description = "状态,参见 CommonStatusEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty(value = "状态", converter = DictConvert.class)
@DictFormat(DictTypeConstants.COMMON_STATUS)
private Integer status;
@Schema(description = "描述", example = "污言秽语")
@ExcelProperty("描述")
private String description;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@ -1,31 +0,0 @@
package cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.util.List;
@Schema(description = "管理后台 - 敏感词创建/修改 Request VO")
@Data
public class SensitiveWordSaveVO {
@Schema(description = "编号", example = "1")
private Long id;
@Schema(description = "敏感词", requiredMode = Schema.RequiredMode.REQUIRED, example = "敏感词")
@NotNull(message = "敏感词不能为空")
private String name;
@Schema(description = "标签", requiredMode = Schema.RequiredMode.REQUIRED, example = "短信,评论")
@NotNull(message = "标签不能为空")
private List<String> tags;
@Schema(description = "状态,参见 CommonStatusEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "状态不能为空")
private Integer status;
@Schema(description = "描述", example = "污言秽语")
private String description;
}

View File

@ -1,52 +0,0 @@
package cn.iocoder.yudao.module.system.dal.dataobject.errorcode;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.system.enums.errorcode.ErrorCodeTypeEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* 错误码表
*
* @author 芋道源码
*/
@TableName(value = "system_error_code")
@KeySequence("system_error_code_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ErrorCodeDO extends BaseDO {
/**
* 错误码编号自增
*/
@TableId
private Long id;
/**
* 错误码类型
*
* 枚举 {@link ErrorCodeTypeEnum}
*/
private Integer type;
/**
* 应用名
*/
private String applicationName;
/**
* 错误码编码
*/
private Integer code;
/**
* 错误码错误提示
*/
private String message;
/**
* 错误码备注
*/
private String memo;
}

View File

@ -1,58 +0,0 @@
package cn.iocoder.yudao.module.system.dal.dataobject.sensitiveword;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.framework.mybatis.core.type.StringListTypeHandler;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.util.List;
/**
* 敏感词 DO
*
* @author 永不言败
*/
@TableName(value = "system_sensitive_word", autoResultMap = true)
@KeySequence("system_sensitive_word_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SensitiveWordDO extends BaseDO {
/**
* 编号
*/
@TableId
private Long id;
/**
* 敏感词
*/
private String name;
/**
* 描述
*/
private String description;
/**
* 标签数组
*
* 用于实现不同的业务场景下需要使用不同标签的敏感词
* 例如说tag 有短信论坛两种敏感词 "推广" 在短信下是敏感词在论坛下不是敏感词
* 此时我们会存储一条敏感词记录它的 name "推广"tag 为短信
*/
@TableField(typeHandler = StringListTypeHandler.class)
private List<String> tags;
/**
* 状态
*
* 枚举 {@link CommonStatusEnum}
*/
private Integer status;
}

View File

@ -1,40 +0,0 @@
package cn.iocoder.yudao.module.system.dal.mysql.errorcode;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.system.controller.admin.errorcode.vo.ErrorCodePageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.errorcode.ErrorCodeDO;
import org.apache.ibatis.annotations.Mapper;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
@Mapper
public interface ErrorCodeMapper extends BaseMapperX<ErrorCodeDO> {
default PageResult<ErrorCodeDO> selectPage(ErrorCodePageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<ErrorCodeDO>()
.eqIfPresent(ErrorCodeDO::getType, reqVO.getType())
.likeIfPresent(ErrorCodeDO::getApplicationName, reqVO.getApplicationName())
.eqIfPresent(ErrorCodeDO::getCode, reqVO.getCode())
.likeIfPresent(ErrorCodeDO::getMessage, reqVO.getMessage())
.betweenIfPresent(ErrorCodeDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(ErrorCodeDO::getCode));
}
default List<ErrorCodeDO> selectListByCodes(Collection<Integer> codes) {
return selectList(ErrorCodeDO::getCode, codes);
}
default ErrorCodeDO selectByCode(Integer code) {
return selectOne(ErrorCodeDO::getCode, code);
}
default List<ErrorCodeDO> selectListByApplicationNameAndUpdateTimeGt(String applicationName, LocalDateTime minUpdateTime) {
return selectList(new LambdaQueryWrapperX<ErrorCodeDO>().eq(ErrorCodeDO::getApplicationName, applicationName)
.gtIfPresent(ErrorCodeDO::getUpdateTime, minUpdateTime));
}
}

View File

@ -1,36 +0,0 @@
package cn.iocoder.yudao.module.system.dal.mysql.sensitiveword;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordPageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.sensitiveword.SensitiveWordDO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.time.LocalDateTime;
/**
* 敏感词 Mapper
*
* @author 永不言败
*/
@Mapper
public interface SensitiveWordMapper extends BaseMapperX<SensitiveWordDO> {
default PageResult<SensitiveWordDO> selectPage(SensitiveWordPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<SensitiveWordDO>()
.likeIfPresent(SensitiveWordDO::getName, reqVO.getName())
.likeIfPresent(SensitiveWordDO::getTags, reqVO.getTag())
.eqIfPresent(SensitiveWordDO::getStatus, reqVO.getStatus())
.betweenIfPresent(SensitiveWordDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(SensitiveWordDO::getId));
}
default SensitiveWordDO selectByName(String name) {
return selectOne(SensitiveWordDO::getName, name);
}
@Select("SELECT COUNT(*) FROM system_sensitive_word WHERE update_time > #{maxUpdateTime}")
Long selectCountByUpdateTimeGt(LocalDateTime maxTime);
}

View File

@ -1,77 +0,0 @@
package cn.iocoder.yudao.module.system.service.errorcode;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.api.errorcode.dto.ErrorCodeAutoGenerateReqDTO;
import cn.iocoder.yudao.module.system.api.errorcode.dto.ErrorCodeRespDTO;
import cn.iocoder.yudao.module.system.controller.admin.errorcode.vo.ErrorCodePageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.errorcode.vo.ErrorCodeSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.errorcode.ErrorCodeDO;
import javax.validation.Valid;
import java.time.LocalDateTime;
import java.util.List;
/**
* 错误码 Service 接口
*
* @author 芋道源码
*/
public interface ErrorCodeService {
/**
* 自动创建错误码
*
* @param autoGenerateDTOs 错误码信息
*/
void autoGenerateErrorCodes(@Valid List<ErrorCodeAutoGenerateReqDTO> autoGenerateDTOs);
/**
* 增量获得错误码数组
*
* 如果 minUpdateTime 为空时则获取所有错误码
*
* @param applicationName 应用名
* @param minUpdateTime 最小更新时间
* @return 错误码数组
*/
List<ErrorCodeRespDTO> getErrorCodeList(String applicationName, LocalDateTime minUpdateTime);
/**
* 创建错误码
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createErrorCode(@Valid ErrorCodeSaveReqVO createReqVO);
/**
* 更新错误码
*
* @param updateReqVO 更新信息
*/
void updateErrorCode(@Valid ErrorCodeSaveReqVO updateReqVO);
/**
* 删除错误码
*
* @param id 编号
*/
void deleteErrorCode(Long id);
/**
* 获得错误码
*
* @param id 编号
* @return 错误码
*/
ErrorCodeDO getErrorCode(Long id);
/**
* 获得错误码分页
*
* @param pageReqVO 分页查询
* @return 错误码分页
*/
PageResult<ErrorCodeDO> getErrorCodePage(ErrorCodePageReqVO pageReqVO);
}

View File

@ -1,167 +0,0 @@
package cn.iocoder.yudao.module.system.service.errorcode;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.system.api.errorcode.dto.ErrorCodeAutoGenerateReqDTO;
import cn.iocoder.yudao.module.system.api.errorcode.dto.ErrorCodeRespDTO;
import cn.iocoder.yudao.module.system.controller.admin.errorcode.vo.ErrorCodePageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.errorcode.vo.ErrorCodeSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.errorcode.ErrorCodeDO;
import cn.iocoder.yudao.module.system.dal.mysql.errorcode.ErrorCodeMapper;
import cn.iocoder.yudao.module.system.enums.errorcode.ErrorCodeTypeEnum;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.ERROR_CODE_DUPLICATE;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.ERROR_CODE_NOT_EXISTS;
/**
* 错误码 Service 实现类
*
* @author dlyan
*/
@Service
@Validated
@Slf4j
public class ErrorCodeServiceImpl implements ErrorCodeService {
@Resource
private ErrorCodeMapper errorCodeMapper;
@Override
public Long createErrorCode(ErrorCodeSaveReqVO createReqVO) {
// 校验 code 重复
validateCodeDuplicate(createReqVO.getCode(), null);
// 插入
ErrorCodeDO errorCode = BeanUtils.toBean(createReqVO, ErrorCodeDO.class)
.setType(ErrorCodeTypeEnum.MANUAL_OPERATION.getType());
errorCodeMapper.insert(errorCode);
// 返回
return errorCode.getId();
}
@Override
public void updateErrorCode(ErrorCodeSaveReqVO updateReqVO) {
// 校验存在
validateErrorCodeExists(updateReqVO.getId());
// 校验 code 重复
validateCodeDuplicate(updateReqVO.getCode(), updateReqVO.getId());
// 更新
ErrorCodeDO updateObj = BeanUtils.toBean(updateReqVO, ErrorCodeDO.class)
.setType(ErrorCodeTypeEnum.MANUAL_OPERATION.getType());
errorCodeMapper.updateById(updateObj);
}
@Override
public void deleteErrorCode(Long id) {
// 校验存在
validateErrorCodeExists(id);
// 删除
errorCodeMapper.deleteById(id);
}
/**
* 校验错误码的唯一字段是否重复
*
* 是否存在相同编码的错误码
*
* @param code 错误码编码
* @param id 错误码编号
*/
@VisibleForTesting
public void validateCodeDuplicate(Integer code, Long id) {
ErrorCodeDO errorCodeDO = errorCodeMapper.selectByCode(code);
if (errorCodeDO == null) {
return;
}
// 如果 id 为空说明不用比较是否为相同 id 的错误码
if (id == null) {
throw exception(ERROR_CODE_DUPLICATE);
}
if (!errorCodeDO.getId().equals(id)) {
throw exception(ERROR_CODE_DUPLICATE);
}
}
@VisibleForTesting
void validateErrorCodeExists(Long id) {
if (errorCodeMapper.selectById(id) == null) {
throw exception(ERROR_CODE_NOT_EXISTS);
}
}
@Override
public ErrorCodeDO getErrorCode(Long id) {
return errorCodeMapper.selectById(id);
}
@Override
public PageResult<ErrorCodeDO> getErrorCodePage(ErrorCodePageReqVO pageReqVO) {
return errorCodeMapper.selectPage(pageReqVO);
}
@Override
@Transactional
public void autoGenerateErrorCodes(List<ErrorCodeAutoGenerateReqDTO> autoGenerateDTOs) {
if (CollUtil.isEmpty(autoGenerateDTOs)) {
return;
}
// 获得错误码
List<ErrorCodeDO> errorCodeDOs = errorCodeMapper.selectListByCodes(
convertSet(autoGenerateDTOs, ErrorCodeAutoGenerateReqDTO::getCode));
Map<Integer, ErrorCodeDO> errorCodeDOMap = convertMap(errorCodeDOs, ErrorCodeDO::getCode);
// 遍历 autoGenerateBOs 数组逐个插入或更新考虑到每次量级不大就不走批量了
autoGenerateDTOs.forEach(autoGenerateDTO -> {
ErrorCodeDO errorCode = errorCodeDOMap.get(autoGenerateDTO.getCode());
// 不存在则进行新增
if (errorCode == null) {
errorCode = BeanUtils.toBean(autoGenerateDTO, ErrorCodeDO.class)
.setType(ErrorCodeTypeEnum.AUTO_GENERATION.getType());
errorCodeMapper.insert(errorCode);
return;
}
// 存在则进行更新更新有三个前置条件
// 条件 1. 只更新自动生成的错误码 Type ErrorCodeTypeEnum.AUTO_GENERATION
if (!ErrorCodeTypeEnum.AUTO_GENERATION.getType().equals(errorCode.getType())) {
return;
}
// 条件 2. 分组 applicationName 必须匹配避免存在错误码冲突的情况
if (!autoGenerateDTO.getApplicationName().equals(errorCode.getApplicationName())) {
log.error("[autoGenerateErrorCodes][自动创建({}/{}) 错误码失败,数据库中已经存在({}/{})]",
autoGenerateDTO.getCode(), autoGenerateDTO.getApplicationName(),
errorCode.getCode(), errorCode.getApplicationName());
return;
}
// 条件 3. 错误提示语存在差异
if (autoGenerateDTO.getMessage().equals(errorCode.getMessage())) {
return;
}
// 最终匹配进行更新
errorCodeMapper.updateById(new ErrorCodeDO().setId(errorCode.getId()).setMessage(autoGenerateDTO.getMessage()));
});
}
@Override
public List<ErrorCodeRespDTO> getErrorCodeList(String applicationName, LocalDateTime minUpdateTime) {
List<ErrorCodeDO> list = errorCodeMapper.selectListByApplicationNameAndUpdateTimeGt(
applicationName, minUpdateTime);
return BeanUtils.toBean(list, ErrorCodeRespDTO.class);
}
}

View File

@ -1,89 +0,0 @@
package cn.iocoder.yudao.module.system.service.sensitiveword;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordSaveVO;
import cn.iocoder.yudao.module.system.dal.dataobject.sensitiveword.SensitiveWordDO;
import javax.validation.Valid;
import java.util.List;
import java.util.Set;
/**
* 敏感词 Service 接口
*
* @author 永不言败
*/
public interface SensitiveWordService {
/**
* 创建敏感词
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createSensitiveWord(@Valid SensitiveWordSaveVO createReqVO);
/**
* 更新敏感词
*
* @param updateReqVO 更新信息
*/
void updateSensitiveWord(@Valid SensitiveWordSaveVO updateReqVO);
/**
* 删除敏感词
*
* @param id 编号
*/
void deleteSensitiveWord(Long id);
/**
* 获得敏感词
*
* @param id 编号
* @return 敏感词
*/
SensitiveWordDO getSensitiveWord(Long id);
/**
* 获得敏感词列表
*
* @return 敏感词列表
*/
List<SensitiveWordDO> getSensitiveWordList();
/**
* 获得敏感词分页
*
* @param pageReqVO 分页查询
* @return 敏感词分页
*/
PageResult<SensitiveWordDO> getSensitiveWordPage(SensitiveWordPageReqVO pageReqVO);
/**
* 获得所有敏感词的标签数组
*
* @return 标签数组
*/
Set<String> getSensitiveWordTagSet();
/**
* 获得文本所包含的不合法的敏感词数组
*
* @param text 文本
* @param tags 标签数组
* @return 不合法的敏感词数组
*/
List<String> validateText(String text, List<String> tags);
/**
* 判断文本是否包含敏感词
*
* @param text 文本
* @param tags 标签数组
* @return 是否包含敏感词
*/
boolean isTextValid(String text, List<String> tags);
}

View File

@ -1,262 +0,0 @@
package cn.iocoder.yudao.module.system.service.sensitiveword;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordSaveVO;
import cn.iocoder.yudao.module.system.dal.dataobject.sensitiveword.SensitiveWordDO;
import cn.iocoder.yudao.module.system.dal.mysql.sensitiveword.SensitiveWordMapper;
import cn.iocoder.yudao.module.system.util.collection.SimpleTrie;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.validation.annotation.Validated;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMaxValue;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SENSITIVE_WORD_EXISTS;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SENSITIVE_WORD_NOT_EXISTS;
/**
* 敏感词 Service 实现类
*
* @author 永不言败
*/
@Service
@Slf4j
@Validated
public class SensitiveWordServiceImpl implements SensitiveWordService {
/**
* 是否开启敏感词功能
*/
public static Boolean ENABLED = false;
/**
* 敏感词列表缓存
*/
@Getter
private volatile List<SensitiveWordDO> sensitiveWordCache = Collections.emptyList();
/**
* 敏感词标签缓存
* key敏感词编号 {@link SensitiveWordDO#getId()}
* <p>
* 这里声明 volatile 修饰的原因是每次刷新时直接修改指向
*/
@Getter
private volatile Set<String> sensitiveWordTagsCache = Collections.emptySet();
@Resource
private SensitiveWordMapper sensitiveWordMapper;
/**
* 默认的敏感词的字典树包含所有敏感词
*/
@Getter
private volatile SimpleTrie defaultSensitiveWordTrie = new SimpleTrie(Collections.emptySet());
/**
* 标签与敏感词的字段数的映射
*/
@Getter
private volatile Map<String, SimpleTrie> tagSensitiveWordTries = Collections.emptyMap();
/**
* 初始化缓存
*/
@PostConstruct
public void initLocalCache() {
if (!ENABLED) {
return;
}
// 第一步查询数据
List<SensitiveWordDO> sensitiveWords = sensitiveWordMapper.selectList();
log.info("[initLocalCache][缓存敏感词,数量为:{}]", sensitiveWords.size());
// 第二步构建缓存
// 写入 sensitiveWordTagsCache 缓存
Set<String> tags = new HashSet<>();
sensitiveWords.forEach(word -> tags.addAll(word.getTags()));
sensitiveWordTagsCache = tags;
sensitiveWordCache = sensitiveWords;
// 写入 defaultSensitiveWordTrietagSensitiveWordTries 缓存
initSensitiveWordTrie(sensitiveWords);
}
private void initSensitiveWordTrie(List<SensitiveWordDO> wordDOs) {
// 过滤禁用的敏感词
wordDOs = filterList(wordDOs, word -> word.getStatus().equals(CommonStatusEnum.ENABLE.getStatus()));
// 初始化默认的 defaultSensitiveWordTrie
this.defaultSensitiveWordTrie = new SimpleTrie(CollectionUtils.convertList(wordDOs, SensitiveWordDO::getName));
// 初始化 tagSensitiveWordTries
Multimap<String, String> tagWords = HashMultimap.create();
for (SensitiveWordDO word : wordDOs) {
if (CollUtil.isEmpty(word.getTags())) {
continue;
}
word.getTags().forEach(tag -> tagWords.put(tag, word.getName()));
}
// 添加到 tagSensitiveWordTries
Map<String, SimpleTrie> tagSensitiveWordTries = new HashMap<>();
tagWords.asMap().forEach((tag, words) -> tagSensitiveWordTries.put(tag, new SimpleTrie(words)));
this.tagSensitiveWordTries = tagSensitiveWordTries;
}
/**
* 通过定时任务轮询刷新缓存
*
* 目的多节点部署时通过轮询通知所有节点进行刷新
*/
@Scheduled(initialDelay = 60, fixedRate = 60, timeUnit = TimeUnit.SECONDS)
public void refreshLocalCache() {
// 情况一如果缓存里没有数据则直接刷新缓存
if (CollUtil.isEmpty(sensitiveWordCache)) {
initLocalCache();
return;
}
// 情况二如果缓存里数据则通过 updateTime 判断是否有数据变更有变更则刷新缓存
LocalDateTime maxTime = getMaxValue(sensitiveWordCache, SensitiveWordDO::getUpdateTime);
if (sensitiveWordMapper.selectCountByUpdateTimeGt(maxTime) > 0) {
initLocalCache();
}
}
@Override
public Long createSensitiveWord(SensitiveWordSaveVO createReqVO) {
// 校验唯一性
validateSensitiveWordNameUnique(null, createReqVO.getName());
// 插入
SensitiveWordDO sensitiveWord = BeanUtils.toBean(createReqVO, SensitiveWordDO.class);
sensitiveWordMapper.insert(sensitiveWord);
// 刷新缓存
initLocalCache();
return sensitiveWord.getId();
}
@Override
public void updateSensitiveWord(SensitiveWordSaveVO updateReqVO) {
// 校验唯一性
validateSensitiveWordExists(updateReqVO.getId());
validateSensitiveWordNameUnique(updateReqVO.getId(), updateReqVO.getName());
// 更新
SensitiveWordDO updateObj = BeanUtils.toBean(updateReqVO, SensitiveWordDO.class);
sensitiveWordMapper.updateById(updateObj);
// 刷新缓存
initLocalCache();
}
@Override
public void deleteSensitiveWord(Long id) {
// 校验存在
validateSensitiveWordExists(id);
// 删除
sensitiveWordMapper.deleteById(id);
// 刷新缓存
initLocalCache();
}
private void validateSensitiveWordNameUnique(Long id, String name) {
SensitiveWordDO word = sensitiveWordMapper.selectByName(name);
if (word == null) {
return;
}
// 如果 id 为空说明不用比较是否为相同 id 的敏感词
if (id == null) {
throw exception(SENSITIVE_WORD_EXISTS);
}
if (!word.getId().equals(id)) {
throw exception(SENSITIVE_WORD_EXISTS);
}
}
private void validateSensitiveWordExists(Long id) {
if (sensitiveWordMapper.selectById(id) == null) {
throw exception(SENSITIVE_WORD_NOT_EXISTS);
}
}
@Override
public SensitiveWordDO getSensitiveWord(Long id) {
return sensitiveWordMapper.selectById(id);
}
@Override
public List<SensitiveWordDO> getSensitiveWordList() {
return sensitiveWordMapper.selectList();
}
@Override
public PageResult<SensitiveWordDO> getSensitiveWordPage(SensitiveWordPageReqVO pageReqVO) {
return sensitiveWordMapper.selectPage(pageReqVO);
}
@Override
public Set<String> getSensitiveWordTagSet() {
return sensitiveWordTagsCache;
}
@Override
public List<String> validateText(String text, List<String> tags) {
Assert.isTrue(ENABLED, "敏感词功能未开启,请将 ENABLED 设置为 true");
// 无标签时默认所有
if (CollUtil.isEmpty(tags)) {
return defaultSensitiveWordTrie.validate(text);
}
// 有标签的情况
Set<String> result = new HashSet<>();
tags.forEach(tag -> {
SimpleTrie trie = tagSensitiveWordTries.get(tag);
if (trie == null) {
return;
}
result.addAll(trie.validate(text));
});
return new ArrayList<>(result);
}
@Override
public boolean isTextValid(String text, List<String> tags) {
Assert.isTrue(ENABLED, "敏感词功能未开启,请将 ENABLED 设置为 true");
// 无标签时默认所有
if (CollUtil.isEmpty(tags)) {
return defaultSensitiveWordTrie.isValid(text);
}
// 有标签的情况
for (String tag : tags) {
SimpleTrie trie = tagSensitiveWordTries.get(tag);
if (trie == null) {
continue;
}
// 如果有一个标签不合法则返回 false 不合法
if (!trie.isValid(text)) {
return false;
}
}
return true;
}
}

View File

@ -1,152 +0,0 @@
package cn.iocoder.yudao.module.system.util.collection;
import cn.hutool.core.collection.CollUtil;
import java.util.*;
/**
* 基于前缀树实现敏感词的校验
* <p>
* 相比 Apache Common 提供的 PatriciaTrie 来说性能可能会更加好一些
*
* @author 芋道源码
*/
@SuppressWarnings("unchecked")
public class SimpleTrie {
/**
* 一个敏感词结束后对应的 key
*/
private static final Character CHARACTER_END = '\0';
/**
* 使用敏感词构建的前缀树
*/
private final Map<Character, Object> children;
/**
* 基于字符串构建前缀树
*
* @param strs 字符串数组
*/
public SimpleTrie(Collection<String> strs) {
// 排序优先使用较短的前缀
strs = CollUtil.sort(strs, String::compareTo);
// 构建树
children = new HashMap<>();
for (String str : strs) {
Map<Character, Object> child = children;
// 遍历每个字符
for (Character c : str.toCharArray()) {
// 如果已经到达结束就没必要在添加更长的敏感词
// 例如说有两个敏感词是吃饭啊吃饭输入一句话是 我要吃饭啊则只要匹配到 吃饭 这个敏感词即可
if (child.containsKey(CHARACTER_END)) {
break;
}
if (!child.containsKey(c)) {
child.put(c, new HashMap<>());
}
child = (Map<Character, Object>) child.get(c);
}
// 结束
child.put(CHARACTER_END, null);
}
}
/**
* 验证文本是否合法即不包含敏感词
*
* @param text 文本
* @return 是否 true-合法 false-不合法
*/
public boolean isValid(String text) {
// 遍历 text使用每一个 [i, n) 段的字符串使用 children 前缀树匹配是否包含敏感词
for (int i = 0; i < text.length(); i++) {
Map<Character, Object> child = (Map<Character, Object>) children.get(text.charAt(i));
if (child == null) {
continue;
}
boolean ok = recursion(text, i + 1, child);
if (!ok) {
return false;
}
}
return true;
}
/**
* 验证文本从指定位置开始是否不包含某个敏感词
*
* @param text 文本
* @param index 开始位置
* @param child 节点当前遍历到的
* @return 是否不包含 true-不包含 false-包含
*/
private boolean recursion(String text, int index, Map<Character, Object> child) {
if (child.containsKey(CHARACTER_END)) {
return false;
}
if (index == text.length()) {
return true;
}
child = (Map<Character, Object>) child.get(text.charAt(index));
return child == null || !child.containsKey(CHARACTER_END) && recursion(text, ++index, child);
}
/**
* 获得文本所包含的不合法的敏感词
*
* 注意才当即最短匹配原则例如说当敏感词存在 煞笔煞笔二货 只会返回 煞笔
*
* @param text 文本
* @return 匹配的敏感词
*/
public List<String> validate(String text) {
Set<String> results = new HashSet<>();
for (int i = 0; i < text.length(); i++) {
Character c = text.charAt(i);
Map<Character, Object> child = (Map<Character, Object>) children.get(c);
if (child == null) {
continue;
}
StringBuilder result = new StringBuilder().append(c);
boolean ok = recursionWithResult(text, i + 1, child, result);
if (!ok) {
results.add(result.toString());
}
}
return new ArrayList<>(results);
}
/**
* 返回文本从 index 开始的敏感词并使用 StringBuilder 参数进行返回
*
* 逻辑和 {@link #recursion(String, int, Map)} 是一致只是多了 result 返回结果
*
* @param text 文本
* @param index 开始未知
* @param child 节点当前遍历到的
* @param result 返回敏感词
* @return 是否有敏感词
*/
@SuppressWarnings("unchecked")
private static boolean recursionWithResult(String text, int index, Map<Character, Object> child, StringBuilder result) {
if (child.containsKey(CHARACTER_END)) {
return false;
}
if (index == text.length()) {
return true;
}
Character c = text.charAt(index);
child = (Map<Character, Object>) child.get(c);
if (child == null) {
return true;
}
if (child.containsKey(CHARACTER_END)) {
result.append(c);
return false;
}
return recursionWithResult(text, ++index, child, result.append(c));
}
}

View File

@ -1,308 +0,0 @@
package cn.iocoder.yudao.module.system.service.errorcode;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.system.api.errorcode.dto.ErrorCodeAutoGenerateReqDTO;
import cn.iocoder.yudao.module.system.api.errorcode.dto.ErrorCodeRespDTO;
import cn.iocoder.yudao.module.system.controller.admin.errorcode.vo.ErrorCodePageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.errorcode.vo.ErrorCodeSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.errorcode.ErrorCodeDO;
import cn.iocoder.yudao.module.system.dal.mysql.errorcode.ErrorCodeMapper;
import cn.iocoder.yudao.module.system.enums.errorcode.ErrorCodeTypeEnum;
import org.assertj.core.util.Lists;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.function.Consumer;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.ERROR_CODE_DUPLICATE;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.ERROR_CODE_NOT_EXISTS;
import static org.junit.jupiter.api.Assertions.*;
@Import(ErrorCodeServiceImpl.class)
public class ErrorCodeServiceTest extends BaseDbUnitTest {
@Resource
private ErrorCodeServiceImpl errorCodeService;
@Resource
private ErrorCodeMapper errorCodeMapper;
@Test
public void testCreateErrorCode_success() {
// 准备参数
ErrorCodeSaveReqVO reqVO = randomPojo(ErrorCodeSaveReqVO.class)
.setId(null); // 防止 id 被赋值
// 调用
Long errorCodeId = errorCodeService.createErrorCode(reqVO);
// 断言
assertNotNull(errorCodeId);
// 校验记录的属性是否正确
ErrorCodeDO errorCode = errorCodeMapper.selectById(errorCodeId);
assertPojoEquals(reqVO, errorCode, "id");
assertEquals(ErrorCodeTypeEnum.MANUAL_OPERATION.getType(), errorCode.getType());
}
@Test
public void testUpdateErrorCode_success() {
// mock 数据
ErrorCodeDO dbErrorCode = randomErrorCodeDO();
errorCodeMapper.insert(dbErrorCode);// @Sql: 先插入出一条存在的数据
// 准备参数
ErrorCodeSaveReqVO reqVO = randomPojo(ErrorCodeSaveReqVO.class, o -> {
o.setId(dbErrorCode.getId()); // 设置更新的 ID
});
// 调用
errorCodeService.updateErrorCode(reqVO);
// 校验是否更新正确
ErrorCodeDO errorCode = errorCodeMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, errorCode);
assertEquals(ErrorCodeTypeEnum.MANUAL_OPERATION.getType(), errorCode.getType());
}
@Test
public void testDeleteErrorCode_success() {
// mock 数据
ErrorCodeDO dbErrorCode = randomErrorCodeDO();
errorCodeMapper.insert(dbErrorCode);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbErrorCode.getId();
// 调用
errorCodeService.deleteErrorCode(id);
// 校验数据不存在了
assertNull(errorCodeMapper.selectById(id));
}
@Test
public void testGetErrorCodePage() {
// mock 数据
ErrorCodeDO dbErrorCode = initGetErrorCodePage();
// 准备参数
ErrorCodePageReqVO reqVO = new ErrorCodePageReqVO();
reqVO.setType(ErrorCodeTypeEnum.AUTO_GENERATION.getType());
reqVO.setApplicationName("tu");
reqVO.setCode(1);
reqVO.setMessage("ma");
reqVO.setCreateTime(buildBetweenTime(2020, 11, 1, 2020, 11, 30));
// 调用
PageResult<ErrorCodeDO> pageResult = errorCodeService.getErrorCodePage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbErrorCode, pageResult.getList().get(0));
}
/**
* 初始化 getErrorCodePage 方法的测试数据
*/
private ErrorCodeDO initGetErrorCodePage() {
ErrorCodeDO dbErrorCode = randomErrorCodeDO(o -> { // 等会查询到
o.setType(ErrorCodeTypeEnum.AUTO_GENERATION.getType());
o.setApplicationName("tudou");
o.setCode(1);
o.setMessage("yuanma");
o.setCreateTime(buildTime(2020, 11, 11));
});
errorCodeMapper.insert(dbErrorCode);
// 测试 type 不匹配
errorCodeMapper.insert(cloneIgnoreId(dbErrorCode, o -> o.setType(ErrorCodeTypeEnum.MANUAL_OPERATION.getType())));
// 测试 applicationName 不匹配
errorCodeMapper.insert(cloneIgnoreId(dbErrorCode, o -> o.setApplicationName("yuan")));
// 测试 code 不匹配
errorCodeMapper.insert(cloneIgnoreId(dbErrorCode, o -> o.setCode(2)));
// 测试 message 不匹配
errorCodeMapper.insert(cloneIgnoreId(dbErrorCode, o -> o.setMessage("nai")));
// 测试 createTime 不匹配
errorCodeMapper.insert(cloneIgnoreId(dbErrorCode, o -> o.setCreateTime(buildTime(2020, 12, 12))));
return dbErrorCode;
}
@Test
public void testValidateCodeDuplicate_codeDuplicateForCreate() {
// 准备参数
Integer code = randomInteger();
// mock 数据
errorCodeMapper.insert(randomErrorCodeDO(o -> o.setCode(code)));
// 调用校验异常
assertServiceException(() -> errorCodeService.validateCodeDuplicate(code, null),
ERROR_CODE_DUPLICATE);
}
@Test
public void testValidateCodeDuplicate_codeDuplicateForUpdate() {
// 准备参数
Long id = randomLongId();
Integer code = randomInteger();
// mock 数据
errorCodeMapper.insert(randomErrorCodeDO(o -> o.setCode(code)));
// 调用校验异常
assertServiceException(() -> errorCodeService.validateCodeDuplicate(code, id),
ERROR_CODE_DUPLICATE);
}
@Test
public void testValidateErrorCodeExists_notExists() {
assertServiceException(() -> errorCodeService.validateErrorCodeExists(null),
ERROR_CODE_NOT_EXISTS);
}
/**
* 情况 1错误码不存在的情况
*/
@Test
public void testAutoGenerateErrorCodes_01() {
// 准备参数
ErrorCodeAutoGenerateReqDTO generateReqDTO = randomPojo(ErrorCodeAutoGenerateReqDTO.class);
// mock 方法
// 调用
errorCodeService.autoGenerateErrorCodes(Lists.newArrayList(generateReqDTO));
// 断言
ErrorCodeDO errorCode = errorCodeMapper.selectOne(null);
assertPojoEquals(generateReqDTO, errorCode);
assertEquals(ErrorCodeTypeEnum.AUTO_GENERATION.getType(), errorCode.getType());
}
/**
* 情况 2.1错误码存在但是是 ErrorCodeTypeEnum.MANUAL_OPERATION 类型
*/
@Test
public void testAutoGenerateErrorCodes_021() {
// mock 数据
ErrorCodeDO dbErrorCode = randomErrorCodeDO(o -> o.setType(ErrorCodeTypeEnum.MANUAL_OPERATION.getType()));
errorCodeMapper.insert(dbErrorCode);
// 准备参数
ErrorCodeAutoGenerateReqDTO generateReqDTO = randomPojo(ErrorCodeAutoGenerateReqDTO.class,
o -> o.setCode(dbErrorCode.getCode()));
// mock 方法
// 调用
errorCodeService.autoGenerateErrorCodes(Lists.newArrayList(generateReqDTO));
// 断言相等说明不会更新
ErrorCodeDO errorCode = errorCodeMapper.selectById(dbErrorCode.getId());
assertPojoEquals(dbErrorCode, errorCode);
}
/**
* 情况 2.2错误码存在但是是 applicationName 不匹配
*/
@Test
public void testAutoGenerateErrorCodes_022() {
// mock 数据
ErrorCodeDO dbErrorCode = randomErrorCodeDO(o -> o.setType(ErrorCodeTypeEnum.AUTO_GENERATION.getType()));
errorCodeMapper.insert(dbErrorCode);
// 准备参数
ErrorCodeAutoGenerateReqDTO generateReqDTO = randomPojo(ErrorCodeAutoGenerateReqDTO.class,
o -> o.setCode(dbErrorCode.getCode()).setApplicationName(randomString()));
// mock 方法
// 调用
errorCodeService.autoGenerateErrorCodes(Lists.newArrayList(generateReqDTO));
// 断言相等说明不会更新
ErrorCodeDO errorCode = errorCodeMapper.selectById(dbErrorCode.getId());
assertPojoEquals(dbErrorCode, errorCode);
}
/**
* 情况 2.3错误码存在但是是 message 相同
*/
@Test
public void testAutoGenerateErrorCodes_023() {
// mock 数据
ErrorCodeDO dbErrorCode = randomErrorCodeDO(o -> o.setType(ErrorCodeTypeEnum.AUTO_GENERATION.getType()));
errorCodeMapper.insert(dbErrorCode);
// 准备参数
ErrorCodeAutoGenerateReqDTO generateReqDTO = randomPojo(ErrorCodeAutoGenerateReqDTO.class,
o -> o.setCode(dbErrorCode.getCode()).setApplicationName(dbErrorCode.getApplicationName())
.setMessage(dbErrorCode.getMessage()));
// mock 方法
// 调用
errorCodeService.autoGenerateErrorCodes(Lists.newArrayList(generateReqDTO));
// 断言相等说明不会更新
ErrorCodeDO errorCode = errorCodeMapper.selectById(dbErrorCode.getId());
assertPojoEquals(dbErrorCode, errorCode);
}
/**
* 情况 2.3错误码存在但是是 message 不同则进行更新
*/
@Test
public void testAutoGenerateErrorCodes_024() {
// mock 数据
ErrorCodeDO dbErrorCode = randomErrorCodeDO(o -> o.setType(ErrorCodeTypeEnum.AUTO_GENERATION.getType()));
errorCodeMapper.insert(dbErrorCode);
// 准备参数
ErrorCodeAutoGenerateReqDTO generateReqDTO = randomPojo(ErrorCodeAutoGenerateReqDTO.class,
o -> o.setCode(dbErrorCode.getCode()).setApplicationName(dbErrorCode.getApplicationName()));
// mock 方法
// 调用
errorCodeService.autoGenerateErrorCodes(Lists.newArrayList(generateReqDTO));
// 断言匹配
ErrorCodeDO errorCode = errorCodeMapper.selectById(dbErrorCode.getId());
assertPojoEquals(generateReqDTO, errorCode);
}
@Test
public void testGetErrorCode() {
// 准备参数
ErrorCodeDO errorCodeDO = randomErrorCodeDO();
errorCodeMapper.insert(errorCodeDO);
// mock 方法
Long id = errorCodeDO.getId();
// 调用
ErrorCodeDO dbErrorCode = errorCodeService.getErrorCode(id);
// 断言
assertPojoEquals(errorCodeDO, dbErrorCode);
}
@Test
public void testGetErrorCodeList() {
// 准备参数
ErrorCodeDO errorCodeDO01 = randomErrorCodeDO(
o -> o.setApplicationName("yunai_server").setUpdateTime(buildTime(2022, 1, 10)));
errorCodeMapper.insert(errorCodeDO01);
ErrorCodeDO errorCodeDO02 = randomErrorCodeDO(
o -> o.setApplicationName("yunai_server").setUpdateTime(buildTime(2022, 1, 12)));
errorCodeMapper.insert(errorCodeDO02);
// mock 方法
String applicationName = "yunai_server";
LocalDateTime minUpdateTime = buildTime(2022, 1, 11);
// 调用
List<ErrorCodeRespDTO> errorCodeList = errorCodeService.getErrorCodeList(applicationName, minUpdateTime);
// 断言
assertEquals(1, errorCodeList.size());
assertPojoEquals(errorCodeDO02, errorCodeList.get(0));
}
// ========== 随机对象 ==========
@SafeVarargs
private static ErrorCodeDO randomErrorCodeDO(Consumer<ErrorCodeDO>... consumers) {
Consumer<ErrorCodeDO> consumer = (o) -> {
o.setType(randomEle(ErrorCodeTypeEnum.values()).getType()); // 保证 key 的范围
};
return randomPojo(ErrorCodeDO.class, ArrayUtils.append(consumer, consumers));
}
}

View File

@ -1,303 +0,0 @@
package cn.iocoder.yudao.module.system.service.sensitiveword;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordSaveVO;
import cn.iocoder.yudao.module.system.dal.dataobject.sensitiveword.SensitiveWordDO;
import cn.iocoder.yudao.module.system.dal.mysql.sensitiveword.SensitiveWordMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SENSITIVE_WORD_NOT_EXISTS;
import static java.util.Collections.singletonList;
import static org.junit.jupiter.api.Assertions.*;
/**
* {@link SensitiveWordServiceImpl} 的单元测试类
*
* @author 永不言败
*/
@Import(SensitiveWordServiceImpl.class)
public class SensitiveWordServiceImplTest extends BaseDbUnitTest {
@Resource
private SensitiveWordServiceImpl sensitiveWordService;
@Resource
private SensitiveWordMapper sensitiveWordMapper;
@BeforeEach
public void setUp() {
SensitiveWordServiceImpl.ENABLED = true;
}
@Test
public void testInitLocalCache() {
SensitiveWordDO wordDO1 = randomPojo(SensitiveWordDO.class, o -> o.setName("傻瓜")
.setTags(singletonList("论坛")).setStatus(CommonStatusEnum.ENABLE.getStatus()));
sensitiveWordMapper.insert(wordDO1);
SensitiveWordDO wordDO2 = randomPojo(SensitiveWordDO.class, o -> o.setName("笨蛋")
.setTags(singletonList("蔬菜")).setStatus(CommonStatusEnum.ENABLE.getStatus()));
sensitiveWordMapper.insert(wordDO2);
SensitiveWordDO wordDO3 = randomPojo(SensitiveWordDO.class, o -> o.setName("")
.setTags(singletonList("测试")).setStatus(CommonStatusEnum.ENABLE.getStatus()));
sensitiveWordMapper.insert(wordDO3);
SensitiveWordDO wordDO4 = randomPojo(SensitiveWordDO.class, o -> o.setName("白痴")
.setTags(singletonList("测试")).setStatus(CommonStatusEnum.ENABLE.getStatus()));
sensitiveWordMapper.insert(wordDO4);
// 调用
sensitiveWordService.initLocalCache();
// 断言 sensitiveWordTagsCache 缓存
assertEquals(SetUtils.asSet("论坛", "蔬菜", "测试"), sensitiveWordService.getSensitiveWordTagSet());
// 断言 sensitiveWordCache
assertEquals(4, sensitiveWordService.getSensitiveWordCache().size());
assertPojoEquals(wordDO1, sensitiveWordService.getSensitiveWordCache().get(0));
assertPojoEquals(wordDO2, sensitiveWordService.getSensitiveWordCache().get(1));
assertPojoEquals(wordDO3, sensitiveWordService.getSensitiveWordCache().get(2));
// 断言 tagSensitiveWordTries 缓存
assertNotNull(sensitiveWordService.getDefaultSensitiveWordTrie());
assertEquals(3, sensitiveWordService.getTagSensitiveWordTries().size());
assertNotNull(sensitiveWordService.getTagSensitiveWordTries().get("论坛"));
assertNotNull(sensitiveWordService.getTagSensitiveWordTries().get("蔬菜"));
assertNotNull(sensitiveWordService.getTagSensitiveWordTries().get("测试"));
}
@Test
public void testRefreshLocalCache() {
// mock 数据
SensitiveWordDO wordDO1 = randomPojo(SensitiveWordDO.class, o -> o.setName("傻瓜")
.setTags(singletonList("论坛")).setStatus(CommonStatusEnum.ENABLE.getStatus()));
wordDO1.setUpdateTime(LocalDateTime.now());
sensitiveWordMapper.insert(wordDO1);
sensitiveWordService.initLocalCache();
// mock 数据
SensitiveWordDO wordDO2 = randomPojo(SensitiveWordDO.class, o -> o.setName("笨蛋")
.setTags(singletonList("蔬菜")).setStatus(CommonStatusEnum.ENABLE.getStatus()));
wordDO2.setUpdateTime(LocalDateTimeUtils.addTime(Duration.ofMinutes(1))); // 避免时间相同
sensitiveWordMapper.insert(wordDO2);
// 调用
sensitiveWordService.refreshLocalCache();
// 断言 sensitiveWordTagsCache 缓存
assertEquals(SetUtils.asSet("论坛", "蔬菜"), sensitiveWordService.getSensitiveWordTagSet());
// 断言 sensitiveWordCache
assertEquals(2, sensitiveWordService.getSensitiveWordCache().size());
assertPojoEquals(wordDO1, sensitiveWordService.getSensitiveWordCache().get(0));
assertPojoEquals(wordDO2, sensitiveWordService.getSensitiveWordCache().get(1));
// 断言 tagSensitiveWordTries 缓存
assertNotNull(sensitiveWordService.getDefaultSensitiveWordTrie());
assertEquals(2, sensitiveWordService.getTagSensitiveWordTries().size());
assertNotNull(sensitiveWordService.getTagSensitiveWordTries().get("论坛"));
assertNotNull(sensitiveWordService.getTagSensitiveWordTries().get("蔬菜"));
}
@Test
public void testCreateSensitiveWord_success() {
// 准备参数
SensitiveWordSaveVO reqVO = randomPojo(SensitiveWordSaveVO.class)
.setId(null); // 防止 id 被赋值
// 调用
Long sensitiveWordId = sensitiveWordService.createSensitiveWord(reqVO);
// 断言
assertNotNull(sensitiveWordId);
// 校验记录的属性是否正确
SensitiveWordDO sensitiveWord = sensitiveWordMapper.selectById(sensitiveWordId);
assertPojoEquals(reqVO, sensitiveWord, "id");
}
@Test
public void testUpdateSensitiveWord_success() {
// mock 数据
SensitiveWordDO dbSensitiveWord = randomPojo(SensitiveWordDO.class);
sensitiveWordMapper.insert(dbSensitiveWord);// @Sql: 先插入出一条存在的数据
// 准备参数
SensitiveWordSaveVO reqVO = randomPojo(SensitiveWordSaveVO.class, o -> {
o.setId(dbSensitiveWord.getId()); // 设置更新的 ID
});
// 调用
sensitiveWordService.updateSensitiveWord(reqVO);
// 校验是否更新正确
SensitiveWordDO sensitiveWord = sensitiveWordMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, sensitiveWord);
}
@Test
public void testUpdateSensitiveWord_notExists() {
// 准备参数
SensitiveWordSaveVO reqVO = randomPojo(SensitiveWordSaveVO.class);
// 调用, 并断言异常
assertServiceException(() -> sensitiveWordService.updateSensitiveWord(reqVO), SENSITIVE_WORD_NOT_EXISTS);
}
@Test
public void testDeleteSensitiveWord_success() {
// mock 数据
SensitiveWordDO dbSensitiveWord = randomPojo(SensitiveWordDO.class);
sensitiveWordMapper.insert(dbSensitiveWord);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbSensitiveWord.getId();
// 调用
sensitiveWordService.deleteSensitiveWord(id);
// 校验数据不存在了
assertNull(sensitiveWordMapper.selectById(id));
}
@Test
public void testDeleteSensitiveWord_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> sensitiveWordService.deleteSensitiveWord(id), SENSITIVE_WORD_NOT_EXISTS);
}
@Test
public void testGetSensitiveWord() {
// mock 数据
SensitiveWordDO sensitiveWord = randomPojo(SensitiveWordDO.class);
sensitiveWordMapper.insert(sensitiveWord);
// 准备参数
Long id = sensitiveWord.getId();
// 调用
SensitiveWordDO dbSensitiveWord = sensitiveWordService.getSensitiveWord(id);
// 断言
assertPojoEquals(sensitiveWord, dbSensitiveWord);
}
@Test
public void testGetSensitiveWordList() {
// mock 数据
SensitiveWordDO sensitiveWord01 = randomPojo(SensitiveWordDO.class);
sensitiveWordMapper.insert(sensitiveWord01);
SensitiveWordDO sensitiveWord02 = randomPojo(SensitiveWordDO.class);
sensitiveWordMapper.insert(sensitiveWord02);
// 调用
List<SensitiveWordDO> list = sensitiveWordService.getSensitiveWordList();
// 断言
assertEquals(2, list.size());
assertEquals(sensitiveWord01, list.get(0));
assertEquals(sensitiveWord02, list.get(1));
}
@Test
public void testGetSensitiveWordPage() {
// mock 数据
SensitiveWordDO dbSensitiveWord = randomPojo(SensitiveWordDO.class, o -> { // 等会查询到
o.setName("笨蛋");
o.setTags(Arrays.asList("论坛", "蔬菜"));
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setCreateTime(buildTime(2022, 2, 8));
});
sensitiveWordMapper.insert(dbSensitiveWord);
// 测试 name 不匹配
sensitiveWordMapper.insert(cloneIgnoreId(dbSensitiveWord, o -> o.setName("傻瓜")));
// 测试 tags 不匹配
sensitiveWordMapper.insert(cloneIgnoreId(dbSensitiveWord, o -> o.setTags(Arrays.asList("短信", "日用品"))));
// 测试 createTime 不匹配
sensitiveWordMapper.insert(cloneIgnoreId(dbSensitiveWord, o -> o.setCreateTime(buildTime(2022, 2, 16))));
// 准备参数
SensitiveWordPageReqVO reqVO = new SensitiveWordPageReqVO();
reqVO.setName("");
reqVO.setTag("论坛");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setCreateTime(buildBetweenTime(2022, 2, 1, 2022, 2, 12));
// 调用
PageResult<SensitiveWordDO> pageResult = sensitiveWordService.getSensitiveWordPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbSensitiveWord, pageResult.getList().get(0));
}
@Test
public void testValidateText_noTag() {
testInitLocalCache();
// 准备参数
String text = "你是傻瓜,你是笨蛋";
// 调用
List<String> result = sensitiveWordService.validateText(text, null);
// 断言
assertEquals(Arrays.asList("傻瓜", "笨蛋"), result);
// 准备参数
String text2 = "你是傻瓜,你是笨蛋,你是白";
// 调用
List<String> result2 = sensitiveWordService.validateText(text2, null);
// 断言
assertEquals(Arrays.asList("傻瓜", "笨蛋",""), result2);
}
@Test
public void testValidateText_hasTag() {
testInitLocalCache();
// 准备参数
String text = "你是傻瓜,你是笨蛋";
// 调用
List<String> result = sensitiveWordService.validateText(text, singletonList("论坛"));
// 断言
assertEquals(singletonList("傻瓜"), result);
// 准备参数
String text2 = "你是白";
// 调用
List<String> result2 = sensitiveWordService.validateText(text2, singletonList("测试"));
// 断言
assertEquals(singletonList(""), result2);
}
@Test
public void testIsTestValid_noTag() {
testInitLocalCache();
// 准备参数
String text = "你是傻瓜,你是笨蛋";
// 调用断言
assertFalse(sensitiveWordService.isTextValid(text, null));
// 准备参数
String text2 = "你是白";
// 调用断言
assertFalse(sensitiveWordService.isTextValid(text2, null));
}
@Test
public void testIsTestValid_hasTag() {
testInitLocalCache();
// 准备参数
String text = "你是傻瓜,你是笨蛋";
// 调用断言
assertFalse(sensitiveWordService.isTextValid(text, singletonList("论坛")));
// 准备参数
String text2 = "你是白";
// 调用断言
assertFalse(sensitiveWordService.isTextValid(text2, singletonList("测试")));
}
}

View File

@ -16,13 +16,11 @@ DELETE FROM "system_sms_channel";
DELETE FROM "system_sms_template";
DELETE FROM "system_sms_log";
DELETE FROM "system_sms_code";
DELETE FROM "system_error_code";
DELETE FROM "system_social_client";
DELETE FROM "system_social_user";
DELETE FROM "system_social_user_bind";
DELETE FROM "system_tenant";
DELETE FROM "system_tenant_package";
DELETE FROM "system_sensitive_word";
DELETE FROM "system_oauth2_client";
DELETE FROM "system_oauth2_approve";
DELETE FROM "system_oauth2_access_token";

View File

@ -332,21 +332,6 @@ CREATE TABLE IF NOT EXISTS "system_sms_code" (
PRIMARY KEY ("id")
) COMMENT '短信日志';
CREATE TABLE IF NOT EXISTS "system_error_code" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"type" tinyint NOT NULL DEFAULT '0',
"application_name" varchar(50) NOT NULL,
"code" int NOT NULL DEFAULT '0',
"message" varchar(512) NOT NULL DEFAULT '',
"memo" varchar(512) DEFAULT '',
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '错误码表';
CREATE TABLE IF NOT EXISTS "system_social_client" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"name" varchar(255) NOT NULL,
@ -431,20 +416,6 @@ CREATE TABLE IF NOT EXISTS "system_tenant_package" (
PRIMARY KEY ("id")
) COMMENT '租户套餐表';
CREATE TABLE IF NOT EXISTS "system_sensitive_word" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"name" varchar(255) NOT NULL,
"tags" varchar(1024) NOT NULL,
"status" bit NOT NULL DEFAULT FALSE,
"description" varchar(512),
"creator" varchar(64) DEFAULT '',
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '系统敏感词';
CREATE TABLE IF NOT EXISTS "system_oauth2_client" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"client_id" varchar NOT NULL,

View File

@ -170,7 +170,6 @@ logging:
cn.iocoder.yudao.module.pay.dal.mysql: debug
cn.iocoder.yudao.module.pay.dal.mysql.notify.PayNotifyTaskMapper: INFO # 配置 JobLogMapper 的日志级别为 info
cn.iocoder.yudao.module.system.dal.mysql: debug
cn.iocoder.yudao.module.system.dal.mysql.sensitiveword.SensitiveWordMapper: INFO # 配置 SensitiveWordMapper 的日志级别为 info
cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper: INFO # 配置 SmsChannelMapper 的日志级别为 info
cn.iocoder.yudao.module.tool.dal.mysql: debug
cn.iocoder.yudao.module.member.dal.mysql: debug
@ -227,8 +226,6 @@ yudao:
refund-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/refund # 支付渠道的【退款】回调地址
access-log: # 访问日志的配置项
enable: false
error-code: # 错误码相关配置项
enable: false
demo: false # 关闭演示模式
tencent-lbs-key: TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E # QQ 地图的密钥 https://lbs.qq.com/service/staticV2/staticGuide/staticDoc

View File

@ -181,14 +181,6 @@ yudao:
base-package: ${yudao.info.base-package}
db-schemas: ${spring.datasource.dynamic.datasource.master.name}
front-type: 10 # 前端模版的类型,参见 CodegenFrontTypeEnum 枚举类
error-code: # 错误码相关配置项
constants-class-list:
- cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants
- cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants
- cn.iocoder.yudao.module.member.enums.ErrorCodeConstants
- cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants
- cn.iocoder.yudao.module.system.enums.ErrorCodeConstants
- cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants
tenant: # 多租户相关配置项
enable: true
ignore-urls: