Files
dax-pay/_config/scripts/gen-sensitive-word-module-part3.mjs
DaxPay Dev 357ee494ee feat(sensitive-word): 新增平台敏感词过滤能力
新增 capability-sensitive-word 模块(词库 CRUD、命中审计、AC 匹配与繁转简规范化),平台配置策略开关,并接入支付标题/商品名与商户应用门店等展示字段校验;同步 DDL、菜单与后端 i18n。
2026-07-16 09:08:37 +08:00

694 lines
26 KiB
JavaScript

/**
* 生成 service / controller / validation
*/
import fs from 'node:fs'
import path from 'node:path'
const base = path.resolve(
'daxpay-platform/daxpay-platform-capability/capability-sensitive-word',
)
const pkg = 'cn.daxpay.open.platform.capability.sensitiveword'
const jp = 'src/main/java/cn/daxpay/open/platform/capability/sensitiveword'
function w(rel, content) {
const p = path.join(base, rel)
fs.mkdirSync(path.dirname(p), { recursive: true })
fs.writeFileSync(p, content.replace(/\r\n/g, '\n'), 'utf8')
console.log('wrote', rel)
}
w(
`${jp}/service/SystemSensitiveWordService.java`,
`package ${pkg}.service;
import cn.daxpay.open.platform.capability.sensitiveword.convert.SystemSensitiveWordConvert;
import cn.daxpay.open.platform.capability.sensitiveword.dao.SystemSensitiveWordManager;
import cn.daxpay.open.platform.capability.sensitiveword.entity.SystemSensitiveWord;
import cn.daxpay.open.platform.capability.sensitiveword.enums.SensitiveWordCategoryEnum;
import cn.daxpay.open.platform.capability.sensitiveword.enums.SensitiveWordMatchModeEnum;
import cn.daxpay.open.platform.capability.sensitiveword.enums.SensitiveWordStatusEnum;
import cn.daxpay.open.platform.capability.sensitiveword.param.SystemSensitiveWordParam;
import cn.daxpay.open.platform.capability.sensitiveword.param.SystemSensitiveWordQuery;
import cn.daxpay.open.platform.capability.sensitiveword.result.SystemSensitiveWordResult;
import cn.daxpay.open.platform.common.mybatisplus.util.MpUtil;
import cn.daxpay.open.platform.core.code.CommonErrorCode;
import cn.daxpay.open.platform.core.exception.BizInfoException;
import cn.daxpay.open.platform.core.exception.DataNotExistException;
import cn.daxpay.open.platform.core.rest.param.PageParam;
import cn.daxpay.open.platform.core.rest.result.PageResult;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.stream.Collectors;
/// # 敏感词词库服务
///
@Slf4j
@Service
@RequiredArgsConstructor
public class SystemSensitiveWordService {
public static final String CACHE_NAME = "system:sensitive-word";
private final SystemSensitiveWordManager systemSensitiveWordManager;
/// 分页
public PageResult<SystemSensitiveWordResult> page(PageParam pageParam, SystemSensitiveWordQuery query) {
return MpUtil.toPageResult(systemSensitiveWordManager.page(pageParam, query));
}
/// 详情
public SystemSensitiveWordResult findById(Long id) {
return getEntity(id).toResult();
}
/// 新增
@Transactional(rollbackFor = Exception.class)
@CacheEvict(value = CACHE_NAME, allEntries = true)
public SystemSensitiveWordResult add(SystemSensitiveWordParam param) {
String word = StrUtil.trim(param.getWord());
if (systemSensitiveWordManager.existsByWord(word, null)) {
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR,
"error.common.sensitiveWord.duplicate");
}
SystemSensitiveWord entity = SystemSensitiveWordConvert.CONVERT.toEntity(param);
entity.setWord(word);
if (StrUtil.isBlank(entity.getStatus())) {
entity.setStatus(SensitiveWordStatusEnum.ENABLE.getCode());
} else {
validateStatus(entity.getStatus());
}
if (StrUtil.isBlank(entity.getMatchMode())) {
entity.setMatchMode(SensitiveWordMatchModeEnum.CONTAINS.getCode());
} else {
validateMatchMode(entity.getMatchMode());
}
if (StrUtil.isBlank(entity.getCategory())) {
entity.setCategory(SensitiveWordCategoryEnum.CUSTOM.getCode());
} else {
validateCategory(entity.getCategory());
}
if (StrUtil.isBlank(entity.getLevel())) {
entity.setLevel("reject");
}
systemSensitiveWordManager.save(entity);
return entity.toResult();
}
/// 修改
@Transactional(rollbackFor = Exception.class)
@CacheEvict(value = CACHE_NAME, allEntries = true)
public void update(SystemSensitiveWordParam param) {
SystemSensitiveWord entity = getEntity(param.getId());
String originWord = entity.getWord();
SystemSensitiveWordConvert.CONVERT.copy(param, entity);
// 词面创建后允许改,但需查重
if (StrUtil.isNotBlank(param.getWord())) {
String word = StrUtil.trim(param.getWord());
if (systemSensitiveWordManager.existsByWord(word, entity.getId())) {
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR,
"error.common.sensitiveWord.duplicate");
}
entity.setWord(word);
} else {
entity.setWord(originWord);
}
if (StrUtil.isNotBlank(param.getStatus())) {
validateStatus(param.getStatus());
}
if (StrUtil.isNotBlank(param.getMatchMode())) {
validateMatchMode(param.getMatchMode());
}
if (StrUtil.isNotBlank(param.getCategory())) {
validateCategory(param.getCategory());
}
systemSensitiveWordManager.updateById(entity);
}
/// 删除
@Transactional(rollbackFor = Exception.class)
@CacheEvict(value = CACHE_NAME, allEntries = true)
public void delete(Long id) {
getEntity(id);
systemSensitiveWordManager.deleteById(id);
}
/// 词面是否存在
public boolean existsByWord(String word, Long excludeId) {
return systemSensitiveWordManager.existsByWord(StrUtil.trim(word), excludeId);
}
/// 启用词列表(缓存)
@Cacheable(value = CACHE_NAME, key = "'enabled:list'")
public List<String> listEnabledWords() {
return systemSensitiveWordManager.listEnabled().stream()
.map(SystemSensitiveWord::getWord)
.filter(StrUtil::isNotBlank)
.map(String::trim)
.distinct()
.collect(Collectors.toList());
}
/// 启用词版本指纹(用于本地 matcher 重建)
@Cacheable(value = CACHE_NAME, key = "'enabled:version'")
public String enabledVersion() {
List<String> words = listEnabledWordsUncached();
if (words.isEmpty()) {
return "empty";
}
return DigestUtil.md5Hex(String.join("\\n", words));
}
/// 无缓存加载(重建 matcher 时与 list 一致)
public List<String> listEnabledWordsUncached() {
return systemSensitiveWordManager.listEnabled().stream()
.map(SystemSensitiveWord::getWord)
.filter(StrUtil::isNotBlank)
.map(String::trim)
.distinct()
.collect(Collectors.toList());
}
/// 按词面找启用词 ID
public Long findEnabledWordId(String word) {
if (StrUtil.isBlank(word)) {
return null;
}
return systemSensitiveWordManager.lambdaQuery()
.eq(SystemSensitiveWord::getWord, word)
.eq(SystemSensitiveWord::getStatus, SensitiveWordStatusEnum.ENABLE.getCode())
.oneOpt()
.map(SystemSensitiveWord::getId)
.orElse(null);
}
private SystemSensitiveWord getEntity(Long id) {
return systemSensitiveWordManager.findById(id)
.orElseThrow(() -> new DataNotExistException("error.common.sensitiveWord.notFound"));
}
private void validateStatus(String status) {
if (SensitiveWordStatusEnum.findByCode(status).isEmpty()) {
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR,
"error.common.sensitiveWord.statusInvalid");
}
}
private void validateMatchMode(String mode) {
if (SensitiveWordMatchModeEnum.findByCode(mode).isEmpty()) {
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR,
"error.common.sensitiveWord.matchModeInvalid");
}
}
private void validateCategory(String category) {
if (SensitiveWordCategoryEnum.findByCode(category).isEmpty()) {
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR,
"error.common.sensitiveWord.categoryInvalid");
}
}
}
`,
)
w(
`${jp}/service/SystemSensitiveWordHitService.java`,
`package ${pkg}.service;
import cn.daxpay.open.platform.capability.sensitiveword.dao.SystemSensitiveWordHitManager;
import cn.daxpay.open.platform.capability.sensitiveword.entity.SystemSensitiveWordHit;
import cn.daxpay.open.platform.capability.sensitiveword.param.SystemSensitiveWordHitQuery;
import cn.daxpay.open.platform.capability.sensitiveword.result.SystemSensitiveWordHitResult;
import cn.daxpay.open.platform.common.mybatisplus.util.MpUtil;
import cn.daxpay.open.platform.core.exception.DataNotExistException;
import cn.daxpay.open.platform.core.rest.param.PageParam;
import cn.daxpay.open.platform.core.rest.result.PageResult;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/// # 敏感词命中记录服务
///
@Slf4j
@Service
@RequiredArgsConstructor
public class SystemSensitiveWordHitService {
private final SystemSensitiveWordHitManager systemSensitiveWordHitManager;
/// 分页
public PageResult<SystemSensitiveWordHitResult> page(PageParam pageParam, SystemSensitiveWordHitQuery query) {
return MpUtil.toPageResult(systemSensitiveWordHitManager.page(pageParam, query));
}
/// 详情
public SystemSensitiveWordHitResult findById(Long id) {
return systemSensitiveWordHitManager.findById(id)
.orElseThrow(() -> new DataNotExistException("error.common.sensitiveWord.hitNotFound"))
.toResult();
}
/// 写入命中
@Transactional(rollbackFor = Exception.class)
public void record(SystemSensitiveWordHit hit) {
systemSensitiveWordHitManager.save(hit);
}
}
`,
)
w(
`${jp}/service/SensitiveWordCheckService.java`,
`package ${pkg}.service;
import cn.daxpay.open.platform.capability.auth.util.SecurityUtil;
import cn.daxpay.open.platform.capability.sensitiveword.engine.SensitiveTextNormalizer;
import cn.daxpay.open.platform.capability.sensitiveword.engine.SensitiveWordMatcher;
import cn.daxpay.open.platform.capability.sensitiveword.entity.SystemSensitiveWordHit;
import cn.daxpay.open.platform.capability.sensitiveword.enums.SensitiveWordSceneEnum;
import cn.daxpay.open.platform.capability.sensitiveword.enums.SensitiveWordSourceEnum;
import cn.daxpay.open.platform.capability.sensitiveword.policy.SensitiveWordPolicy;
import cn.daxpay.open.platform.common.request.context.RequestContextHolder;
import cn.daxpay.open.platform.common.spring.util.WebServletUtil;
import cn.daxpay.open.platform.core.code.CommonErrorCode;
import cn.daxpay.open.platform.core.exception.BizInfoException;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
/// # 敏感词运行时校验
///
/// 编程式入口;与 UI locale 无关。命中默认拒绝提交。
@Slf4j
@Service
@RequiredArgsConstructor
public class SensitiveWordCheckService {
private final SensitiveWordPolicy sensitiveWordPolicy;
private final SystemSensitiveWordService systemSensitiveWordService;
private final SystemSensitiveWordHitService systemSensitiveWordHitService;
private final SensitiveTextNormalizer sensitiveTextNormalizer;
private final SensitiveWordMatcher sensitiveWordMatcher;
/// 是否启用
public boolean isEnabled() {
return sensitiveWordPolicy.isEnabled();
}
/// 仅检测(不写审计、不抛错)
public List<String> findHits(String text) {
return findHits(text, false);
}
/// 仅检测
public List<String> findHits(String text, boolean html) {
if (!sensitiveWordPolicy.isEnabled() || StrUtil.isBlank(text)) {
return Collections.emptyList();
}
ensureMatcher();
String normalized = html
? sensitiveTextNormalizer.normalizeHtml(text)
: sensitiveTextNormalizer.normalize(text);
return sensitiveWordMatcher.findHits(normalized);
}
public boolean contains(String text) {
return CollUtil.isNotEmpty(findHits(text));
}
/// 命中则写审计并抛错
public void assertClean(String text, SensitiveWordSceneEnum scene) {
assertClean(text, scene, false);
}
/// 批量,任一命中即抛
public void assertClean(SensitiveWordSceneEnum scene, String... texts) {
if (texts == null) {
return;
}
for (String t : texts) {
assertClean(t, scene, false);
}
}
/// 命中则写审计并抛错
public void assertClean(String text, SensitiveWordSceneEnum scene, boolean html) {
if (!sensitiveWordPolicy.isEnabled() || StrUtil.isBlank(text)) {
return;
}
List<String> hits = findHits(text, html);
if (CollUtil.isEmpty(hits)) {
return;
}
String hitWord = hits.get(0);
SensitiveWordSceneEnum sc = scene == null ? SensitiveWordSceneEnum.GENERAL : scene;
if (sensitiveWordPolicy.isRecordHit()) {
try {
recordHit(text, hitWord, sc);
} catch (Exception e) {
log.warn("记录敏感词命中失败 word={}: {}", hitWord, e.getMessage());
}
}
if (sensitiveWordPolicy.isRevealWord()) {
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR,
"error.common.sensitiveWord.hit", hitWord);
}
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR,
"error.common.sensitiveWord");
}
/// 管理端试检:返回命中列表,可选写审计
public List<String> checkText(String text, boolean recordHit) {
List<String> hits = findHits(text, false);
if (recordHit && CollUtil.isNotEmpty(hits)) {
try {
recordHit(text, hits.get(0), SensitiveWordSceneEnum.MANUAL_CHECK);
} catch (Exception e) {
log.warn("试检写命中失败: {}", e.getMessage());
}
}
return hits;
}
private void ensureMatcher() {
List<String> words = systemSensitiveWordService.listEnabledWords();
String version = systemSensitiveWordService.enabledVersion();
// listEnabledWords 与 version 同缓存域,重建成本低
sensitiveWordMatcher.rebuild(words, version);
}
private void recordHit(String originalText, String hitWord, SensitiveWordSceneEnum scene) {
int maxLen = Math.max(1, sensitiveWordPolicy.contentPreviewMaxLen());
String preview = StrUtil.maxLength(StrUtil.nullToEmpty(originalText), maxLen);
Long wordId = systemSensitiveWordService.findEnabledWordId(hitWord);
Long operatorId = null;
try {
operatorId = SecurityUtil.getUserIdOrDefaultId();
if (operatorId != null && operatorId <= 0) {
operatorId = null;
}
} catch (Exception ignored) {
// 开放 API 无登录态
}
String clientIp = null;
try {
clientIp = WebServletUtil.getClientIp();
} catch (Exception ignored) {
}
String path = null;
try {
path = RequestContextHolder.getRequestUri();
} catch (Exception ignored) {
}
SystemSensitiveWordHit hit = new SystemSensitiveWordHit()
.setWordId(wordId)
.setHitWord(hitWord)
.setContentPreview(preview)
.setScene(scene.getCode())
.setSource(resolveSource())
.setOperatorId(operatorId)
.setClientIp(clientIp)
.setRequestPath(StrUtil.maxLength(path, 255));
systemSensitiveWordHitService.record(hit);
}
private String resolveSource() {
try {
String path = RequestContextHolder.getRequestUri();
if (path == null) {
return SensitiveWordSourceEnum.UNKNOWN.getCode();
}
if (path.startsWith("/admin/")) {
return SensitiveWordSourceEnum.ADMIN.getCode();
}
if (path.startsWith("/merchant/")) {
return SensitiveWordSourceEnum.MERCHANT.getCode();
}
if (path.startsWith("/app-admin/")) {
return SensitiveWordSourceEnum.APP_ADMIN.getCode();
}
if (path.startsWith("/unipay/") || path.startsWith("/gateway/")) {
return SensitiveWordSourceEnum.UNIPAY.getCode();
}
} catch (Exception ignored) {
}
return SensitiveWordSourceEnum.UNKNOWN.getCode();
}
}
`,
)
w(
`${jp}/validation/SensitiveWord.java`,
`package ${pkg}.validation;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/// # 敏感词校验注解
///
/// 用于管理端/商户端 Param 自由文本字段;开放 API 请用 [SensitiveWordCheckService]。
@Documented
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = SensitiveWordValidator.class)
public @interface SensitiveWord {
String message() default "{validation.field.sensitiveWord.rejected}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/// 是否按 HTML 剥离后再检
boolean html() default false;
}
`,
)
w(
`${jp}/validation/SensitiveWordValidator.java`,
`package ${pkg}.validation;
import cn.daxpay.open.platform.capability.sensitiveword.enums.SensitiveWordSceneEnum;
import cn.daxpay.open.platform.capability.sensitiveword.service.SensitiveWordCheckService;
import cn.hutool.core.util.StrUtil;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
/// # 敏感词 Bean Validation
///
@Component
@RequiredArgsConstructor
public class SensitiveWordValidator implements ConstraintValidator<SensitiveWord, String> {
private final SensitiveWordCheckService sensitiveWordCheckService;
private boolean html;
@Override
public void initialize(SensitiveWord annotation) {
this.html = annotation.html();
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (StrUtil.isBlank(value)) {
return true;
}
if (!sensitiveWordCheckService.isEnabled()) {
return true;
}
// 走 assertClean 以写命中审计;失败转校验错误
try {
sensitiveWordCheckService.assertClean(value, SensitiveWordSceneEnum.GENERAL, html);
return true;
} catch (Exception e) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(e.getMessage() == null
? "{validation.field.sensitiveWord.rejected}"
: e.getMessage())
.addConstraintViolation();
return false;
}
}
}
`,
)
w(
`${jp}/controller/admin/SystemSensitiveWordController.java`,
`package ${pkg}.controller.admin;
import cn.daxpay.open.platform.capability.sensitiveword.param.SensitiveWordCheckTextParam;
import cn.daxpay.open.platform.capability.sensitiveword.param.SystemSensitiveWordParam;
import cn.daxpay.open.platform.capability.sensitiveword.param.SystemSensitiveWordQuery;
import cn.daxpay.open.platform.capability.sensitiveword.result.SystemSensitiveWordResult;
import cn.daxpay.open.platform.capability.sensitiveword.service.SensitiveWordCheckService;
import cn.daxpay.open.platform.capability.sensitiveword.service.SystemSensitiveWordService;
import cn.daxpay.open.platform.core.annotation.PermCode;
import cn.daxpay.open.platform.core.code.PermCodes;
import cn.daxpay.open.platform.core.rest.Res;
import cn.daxpay.open.platform.core.rest.param.PageParam;
import cn.daxpay.open.platform.core.rest.result.PageResult;
import cn.daxpay.open.platform.core.rest.result.Result;
import cn.daxpay.open.platform.core.validation.ValidationGroup;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
/// # 敏感词词库管理(运营端)
///
@PermCode(menuCode = PermCodes.System.SensitiveWord.MENU)
@Validated
@Tag(name = "敏感词词库")
@RestController
@RequestMapping("/admin/system/sensitive-word")
@RequiredArgsConstructor
public class SystemSensitiveWordController {
private final SystemSensitiveWordService systemSensitiveWordService;
private final SensitiveWordCheckService sensitiveWordCheckService;
@PermCode(code = PermCodes.Action.VIEW)
@Operation(summary = "分页查询")
@GetMapping("/page")
public Result<PageResult<SystemSensitiveWordResult>> page(PageParam pageParam, SystemSensitiveWordQuery query) {
return Res.ok(systemSensitiveWordService.page(pageParam, query));
}
@PermCode(code = PermCodes.Action.VIEW)
@Operation(summary = "详情")
@GetMapping("/get-by-id")
public Result<SystemSensitiveWordResult> getById(@NotNull(message = "{validation.field.id.notNull}") Long id) {
return Res.ok(systemSensitiveWordService.findById(id));
}
@PermCode(code = PermCodes.Action.MANAGE)
@Operation(summary = "新增")
@PostMapping("/add")
public Result<SystemSensitiveWordResult> add(
@RequestBody @Validated(ValidationGroup.add.class) SystemSensitiveWordParam param) {
return Res.ok(systemSensitiveWordService.add(param));
}
@PermCode(code = PermCodes.Action.MANAGE)
@Operation(summary = "修改")
@PostMapping("/update")
public Result<Void> update(
@RequestBody @Validated(ValidationGroup.edit.class) SystemSensitiveWordParam param) {
systemSensitiveWordService.update(param);
return Res.ok();
}
@PermCode(code = PermCodes.Action.MANAGE)
@Operation(summary = "删除")
@PostMapping("/delete")
public Result<Void> delete(@NotNull(message = "{validation.field.id.notNull}") Long id) {
systemSensitiveWordService.delete(id);
return Res.ok();
}
@PermCode(code = PermCodes.Action.VIEW)
@Operation(summary = "词面是否存在")
@GetMapping("/exists-by-word")
public Result<Boolean> existsByWord(
@RequestParam String word,
@RequestParam(required = false) Long id) {
return Res.ok(systemSensitiveWordService.existsByWord(word, id));
}
@PermCode(code = PermCodes.Action.VIEW)
@Operation(summary = "试检文本")
@PostMapping("/check-text")
public Result<Map<String, Object>> checkText(@RequestBody @Validated SensitiveWordCheckTextParam param) {
boolean record = Boolean.TRUE.equals(param.getRecordHit());
List<String> hits = sensitiveWordCheckService.checkText(param.getText(), record);
return Res.ok(Map.of("hits", hits, "hit", !hits.isEmpty()));
}
}
`,
)
w(
`${jp}/controller/admin/SystemSensitiveWordHitController.java`,
`package ${pkg}.controller.admin;
import cn.daxpay.open.platform.capability.sensitiveword.param.SystemSensitiveWordHitQuery;
import cn.daxpay.open.platform.capability.sensitiveword.result.SystemSensitiveWordHitResult;
import cn.daxpay.open.platform.capability.sensitiveword.service.SystemSensitiveWordHitService;
import cn.daxpay.open.platform.core.annotation.PermCode;
import cn.daxpay.open.platform.core.code.PermCodes;
import cn.daxpay.open.platform.core.rest.Res;
import cn.daxpay.open.platform.core.rest.param.PageParam;
import cn.daxpay.open.platform.core.rest.result.PageResult;
import cn.daxpay.open.platform.core.rest.result.Result;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/// # 敏感词命中记录(运营端,只读)
///
@PermCode(menuCode = PermCodes.System.SensitiveWordHit.MENU)
@Validated
@Tag(name = "敏感词命中记录")
@RestController
@RequestMapping("/admin/system/sensitive-word-hit")
@RequiredArgsConstructor
public class SystemSensitiveWordHitController {
private final SystemSensitiveWordHitService systemSensitiveWordHitService;
@PermCode(code = PermCodes.Action.VIEW)
@Operation(summary = "分页查询")
@GetMapping("/page")
public Result<PageResult<SystemSensitiveWordHitResult>> page(
PageParam pageParam, SystemSensitiveWordHitQuery query) {
return Res.ok(systemSensitiveWordHitService.page(pageParam, query));
}
@PermCode(code = PermCodes.Action.VIEW)
@Operation(summary = "详情")
@GetMapping("/get-by-id")
public Result<SystemSensitiveWordHitResult> getById(
@NotNull(message = "{validation.field.id.notNull}") Long id) {
return Res.ok(systemSensitiveWordHitService.findById(id));
}
}
`,
)
console.log('part3 done')