如果有人在你的论坛、博客,乱留言、乱回复,怎么办?
作者:小傅哥
博客:https://bugstack.cn
沉淀、分享、成長(zhǎng),讓自己和他人都能有所收獲!??
哈嘍,大家好我是技術(shù)UP主小傅哥。
常聽(tīng)到一句話:你很難賺到你認(rèn)知以外的錢??,屁!不是很難,是壓根賺不到。 你以為要是你做也能做,但其實(shí)除了你能看見(jiàn)的以外,還有很多東西都不知道。
我看過(guò)不少小伙伴自己上線過(guò)帶有評(píng)論功能的博客,或是能進(jìn)行通信的聊天室。但最后都沒(méi)運(yùn)營(yíng)多久就關(guān)停了,除了能花錢解決的服務(wù)器成本,還有是自身的研發(fā)的系統(tǒng)流程不夠健全。其中非常重要的一點(diǎn)是輿情敏感內(nèi)容的審核,如果你做這類應(yīng)用的處理,一定要對(duì)接上相應(yīng)的內(nèi)容安全審核。
那么,接下來(lái)小傅哥就給大家分享下,如何對(duì)接內(nèi)容安全審核,并在 DDD 分層結(jié)構(gòu)下實(shí)現(xiàn)一個(gè)對(duì)應(yīng)的規(guī)則過(guò)濾服務(wù)。
文末提供了「星球:碼農(nóng)會(huì)鎖」??優(yōu)惠加入方式,以及本節(jié)課程的代碼地址。項(xiàng)目演示地址:https://gaga.plus
一、場(chǎng)景說(shuō)明
在本節(jié)小傅哥會(huì)通過(guò) DDD 分層架構(gòu)設(shè)計(jì),開發(fā)出一個(gè)敏感詞、內(nèi)容安全審核過(guò)濾操作的規(guī)則處理器。在這個(gè)過(guò)程大家可以學(xué)習(xí)到 DDD 分層調(diào)用流程、規(guī)則模型的搭建、敏感詞和內(nèi)容審核的使用。
如圖,上半部分是業(yè)務(wù)流程,下半部分是 DDD 分層結(jié)構(gòu)中的實(shí)現(xiàn)。
- 業(yè)務(wù)流程上,以用戶發(fā)送的提交給服務(wù)端的內(nèi)容進(jìn)行審核過(guò)濾,優(yōu)先使用敏感詞進(jìn)行替換單詞組。過(guò)濾后過(guò)內(nèi)容審核,一般各個(gè)云平臺(tái)都有提供內(nèi)容審核的接口,如;京東云、百度云、騰訊云都有提供。一般價(jià)格在
0.0015 元/條 - 系統(tǒng)實(shí)現(xiàn)上,以 DDD 分層架構(gòu)實(shí)現(xiàn)一個(gè)內(nèi)容審核的流程。app 配置組件和啟動(dòng)應(yīng)用、trigger 提供 http 調(diào)用、domain 編寫核心邏輯和流程、infrastructure 提供 dao 的基礎(chǔ)操作。
二、內(nèi)容審核 - SDK 使用
一般輿情內(nèi)容審核分為兩種,一種是靜態(tài)配置數(shù)據(jù)的 SDK 組件,也叫敏感詞過(guò)濾。另外一種是實(shí)時(shí)動(dòng)態(tài)的由各個(gè)第三方提供的內(nèi)容審核接口服務(wù)。這類的就是前面提到的,在各個(gè)云平臺(tái)都有提供。
這里小傅哥先帶著大家做下最基本的調(diào)用案例,之后再基于 DDD 工程實(shí)現(xiàn)整個(gè)代碼開發(fā)。
1. 敏感詞
地址:https://github.com/houbb/sensitive-word - 開源的敏感詞庫(kù)組件
<dependency>
<groupId>com.github.houbb</groupId>
<artifactId>sensitive-word</artifactId>
<version>0.8.0</version>
</dependency>
案例代碼
@Test
public void test_sensitive_word() {
boolean contains = sensitiveWordBs.contains("小傅哥喜歡燒烤臭毛蛋,豆包愛(ài)吃粑粑,如果想吃訂購(gòu)請(qǐng)打電話:13900901878");
log.info("是否被敏感詞攔截:{}", contains);
}
@Test
public void test_sensitive_word_findAll() {
List<String> list = sensitiveWordBs.findAll("小傅哥喜歡燒烤臭毛蛋,豆包愛(ài)吃粑粑,如果想吃訂購(gòu)請(qǐng)打電話:13900901878");
log.info("測(cè)試結(jié)果:{}", JSON.toJSONString(list));
}
@Test
public void test_sensitive_word_replace() {
String replace = sensitiveWordBs.replace("小傅哥喜歡燒烤臭毛蛋,豆包愛(ài)吃粑粑,如果想吃訂購(gòu)請(qǐng)打電話:13900901878");
log.info("測(cè)試結(jié)果:{}", replace);
}
- 敏感詞組件提供了大量的風(fēng)險(xiǎn)詞過(guò)濾,同時(shí)可以基于組件的文檔完成自定義敏感詞的增改刪減操作。
本文在工程中已提供 - 敏感詞組件提供了判斷、查找、過(guò)濾操作。還有你可以把檢測(cè)到的敏感詞替換為
*或者空格。
2. 內(nèi)容審核
- 京東云:https://www.jdcloud.com/cn/products/censor
- 百度云:https://ai.baidu.com/censoring#/strategylist
- 騰訊云:https://cloud.tencent.com/product/tms
這里小傅哥以其中的一個(gè)百度云為例,為大家展示內(nèi)容安全審核的使用。
<!-- 百度內(nèi)容審核 https://mvnrepository.com/artifact/com.baidu.aip/java-sdk -->
<dependency>
<groupId>com.baidu.aip</groupId>
<artifactId>java-sdk</artifactId>
<version>4.16.17</version>
</dependency>
2.1 配置應(yīng)用
- 先領(lǐng)取免費(fèi)的調(diào)用次數(shù),之后創(chuàng)建應(yīng)用。創(chuàng)建應(yīng)用后就可以獲得連接信息;appid、apikey、secretkey
- 另外是策略配置,如果你在過(guò)濾中不需要檢測(cè)用戶發(fā)的應(yīng)用營(yíng)銷信息,那么是可以不檢測(cè)的。
2.2 測(cè)試服務(wù)
//設(shè)置APPID/AK/SK
public static final String APP_ID = "{APP_ID}";
public static final String API_KEY = "{API_KEY}";
public static final String SECRET_KEY = "{SECRET_KEY}";
private AipContentCensor client;
@Before
public void init() {
client = new AipContentCensor(APP_ID, API_KEY, SECRET_KEY);
// 可選:設(shè)置網(wǎng)絡(luò)連接參數(shù)
client.setConnectionTimeoutInMillis(2000);
client.setSocketTimeoutInMillis(60000);
}
@Test
public void test_textCensorUserDefined() throws JSONException {
for (int i = 0; i < 1; i++) {
JSONObject jsonObject = client.textCensorUserDefined("小傅哥喜歡燒烤臭毛蛋,豆包愛(ài)吃粑粑,如果想吃訂購(gòu)請(qǐng)打電話:13900901878");
if (!jsonObject.isNull("error_code")) {
log.info("測(cè)試結(jié)果:{}", jsonObject.get("error_code"));
} else {
log.info("測(cè)試結(jié)果:{}", jsonObject.toString());
}
}
}
測(cè)試結(jié)果
13:41:16.393 [main] INFO com.baidu.aip.client.BaseClient - get access_token success. current state: STATE_AIP_AUTH_OK
13:41:16.396 [main] DEBUG com.baidu.aip.client.BaseClient - current state after check priviledge: STATE_TRUE_AIP_USER
13:41:16.495 [main] INFO cn.bugstack.x.api.test.BaiduAipContentCensorTest - 測(cè)試結(jié)果:{"conclusion":"合規(guī)","log_id":17046060767025067,"isHitMd5":false,"conclusionType":1}
- 應(yīng)為過(guò)濾掉了營(yíng)銷信息,比如手機(jī)號(hào)。那么就會(huì)返回
合規(guī)
三、應(yīng)用實(shí)現(xiàn) - DDD 架構(gòu)
做了以上的基本調(diào)用案例以后,我們來(lái)看下在系統(tǒng)中怎么運(yùn)用這些基礎(chǔ)功能完成業(yè)務(wù)訴求。
1. 工程結(jié)構(gòu)
- docs 下提供了 docker 安裝 mysql 以及初始化數(shù)據(jù)庫(kù)配置的腳本。因?yàn)楸疚牡陌咐梢詽M足你在數(shù)據(jù)庫(kù)中增加敏感詞配置。
- app 是應(yīng)用的啟動(dòng)層,如上我們所需的敏感詞和內(nèi)容審核,都在app層下配置啟動(dòng)處理。
- domain 領(lǐng)域?qū)油ㄟ^(guò)策略+工廠,實(shí)現(xiàn)規(guī)則過(guò)濾服務(wù)。
2. 數(shù)據(jù)庫(kù)表
- 在docs 提供了數(shù)據(jù)庫(kù)初始化的腳本語(yǔ)句,你可以導(dǎo)入到自己的數(shù)據(jù)庫(kù),或者使用 docker 腳本安裝測(cè)試。—— 注意已經(jīng)安裝過(guò) mysql 占用了 3306 端口的話,記得修改 docker 腳本安裝 mysql 的端口。
- 配置到數(shù)據(jù)庫(kù)中的敏感詞方便管理和使用,為了性能考慮也可以考慮使用 redis 做一層緩存。
3. 配置加載
3.1 敏感詞初始化
@Configuration
public class SensitiveWordConfig {
@Bean
public SensitiveWordBs sensitiveWordBs(IWordDeny wordDeny, IWordAllow wordAllow) {
return SensitiveWordBs.newInstance()
.wordDeny(wordDeny)
.wordAllow(wordAllow)
.ignoreCase(true)
.ignoreWidth(true)
.ignoreNumStyle(true)
.ignoreChineseStyle(true)
.ignoreEnglishStyle(true)
.ignoreRepeat(false)
.enableNumCheck(true)
.enableEmailCheck(true)
.enableUrlCheck(true)
.enableWordCheck(true)
.numCheckLen(1024)
.init();
}
@Bean
public IWordDeny wordDeny(ISensitiveWordDao sensitiveWordDao) {
return new IWordDeny() {
@Override
public List<String> deny() {
return sensitiveWordDao.queryValidSensitiveWordConfig("deny");
}
};
}
@Bean
public IWordAllow wordAllow(ISensitiveWordDao sensitiveWordDao) {
return new IWordAllow() {
@Override
public List<String> allow() {
return sensitiveWordDao.queryValidSensitiveWordConfig("allow");
}
};
}
}
- wordDeny、wordAllow 是兩個(gè)自定義的攔截和放行的敏感詞列表,這里小傅哥設(shè)計(jì)從數(shù)據(jù)庫(kù)中查詢。可以方便動(dòng)態(tài)的維護(hù)。
3.2 內(nèi)容安全初始化
# 內(nèi)容安全
baidu:
aip:
app_id: 46573000
api_key: XKOalQOgDBUrvgLBplvu****
secret_key: kwRh1bEhETYWpq9thzyySdFDPKUk****
- 自定義一個(gè)配置文件類 AipContentCensorConfigProperties
@Bean
public AipContentCensor aipContentCensor(AipContentCensorConfigProperties properties) {
AipContentCensor client = new AipContentCensor(properties.getApp_id(), properties.getApi_key(), properties.getSecret_key());
client.setConnectionTimeoutInMillis(2000);
client.setSocketTimeoutInMillis(60000);
return client;
}
- 這里我們來(lái)統(tǒng)一創(chuàng)建 AipContentCensor 對(duì)象,用于有需要使用的地方處理內(nèi)容審核。
4. 規(guī)則實(shí)現(xiàn)
源碼: cn.bugstack.xfg.dev.tech.domain.service.IRuleLogicFilter
public interface IRuleLogicFilter {
RuleActionEntity<RuleMatterEntity> filter(RuleMatterEntity ruleMatterEntity);
}
- 定義一個(gè)統(tǒng)一的規(guī)則過(guò)濾接口
4.1 敏感詞
@Slf4j
@Component
@LogicStrategy(logicMode = DefaultLogicFactory.LogicModel.SENSITIVE_WORD)
public class SensitiveWordFilter implements IRuleLogicFilter {
@Resource
private SensitiveWordBs words;
@Override
public RuleActionEntity<RuleMatterEntity> filter(RuleMatterEntity ruleMatterEntity) {
// 敏感詞過(guò)濾
String content = ruleMatterEntity.getContent();
String replace = words.replace(content);
// 返回結(jié)果
return RuleActionEntity.<RuleMatterEntity>builder()
.type(LogicCheckTypeVO.SUCCESS)
.data(RuleMatterEntity.builder().content(replace).build())
.build();
}
}
4.2 安全內(nèi)容
@Slf4j
@Component
@LogicStrategy(logicMode = DefaultLogicFactory.LogicModel.CONTENT_SECURITY)
public class ContentSecurityFilter implements IRuleLogicFilter {
@Resource
private AipContentCensor aipContentCensor;
@Override
public RuleActionEntity<RuleMatterEntity> filter(RuleMatterEntity ruleMatterEntity) {
JSONObject jsonObject = aipContentCensor.textCensorUserDefined(ruleMatterEntity.getContent());
if (!jsonObject.isNull("conclusion") && "不合規(guī)".equals(jsonObject.get("conclusion"))) {
return RuleActionEntity.<RuleMatterEntity>builder()
.type(LogicCheckTypeVO.REFUSE)
.data(RuleMatterEntity.builder().content("內(nèi)容不合規(guī)").build())
.build();
}
// 返回結(jié)果
return RuleActionEntity.<RuleMatterEntity>builder()
.type(LogicCheckTypeVO.SUCCESS)
.data(ruleMatterEntity)
.build();
}
}
5. 工廠使用
public class DefaultLogicFactory {
public Map<String, IRuleLogicFilter> logicFilterMap = new ConcurrentHashMap<>();
public DefaultLogicFactory(List<IRuleLogicFilter> logicFilters) {
logicFilters.forEach(logic -> {
LogicStrategy strategy = AnnotationUtils.findAnnotation(logic.getClass(), LogicStrategy.class);
if (null != strategy) {
logicFilterMap.put(strategy.logicMode().getCode(), logic);
}
});
}
public RuleActionEntity<RuleMatterEntity> doCheckLogic(RuleMatterEntity ruleMatterEntity, LogicModel... logics) {
RuleActionEntity<RuleMatterEntity> entity = null;
for (LogicModel model : logics) {
entity = logicFilterMap.get(model.code).filter(ruleMatterEntity);
if (!LogicCheckTypeVO.SUCCESS.equals(entity.getType())) return entity;
ruleMatterEntity = entity.getData();
}
return entity != null ? entity :
RuleActionEntity.<RuleMatterEntity>builder()
.type(LogicCheckTypeVO.SUCCESS)
.data(ruleMatterEntity)
.build();
}
}
- 定義出規(guī)則的使用工廠,通過(guò)構(gòu)造函數(shù)的方式注入已經(jīng)實(shí)現(xiàn)了接口 IRuleLogicFilter 的 N 個(gè)規(guī)則,注入到 Map 中
Map<String, IRuleLogicFilter> logicFilterMap - doCheckLogic 根據(jù)入?yún)?lái)過(guò)濾需要處理的規(guī)則。這里可以看到每過(guò)濾一個(gè)規(guī)則都會(huì)把參數(shù)繼續(xù)傳遞給下一個(gè)規(guī)則繼續(xù)篩選。
有點(diǎn)像層層過(guò)篩子的感覺(jué)
四、測(cè)試驗(yàn)證
- 測(cè)試前確保已經(jīng)初始化了庫(kù)表
docs/dev-ops/sql/xfg-dev-tech-content-moderation.sql -
application-dev.yml配置百度內(nèi)容安全參數(shù)和數(shù)據(jù)庫(kù)連接參數(shù)。
1. 功能測(cè)試
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class RuleLogicTest {
@Resource
private DefaultLogicFactory defaultLogicFactory;
@Test
public void test() {
RuleActionEntity<RuleMatterEntity> entity = defaultLogicFactory.doCheckLogic(
RuleMatterEntity.builder().content("小傅哥喜歡燒烤臭毛蛋,豆包愛(ài)吃粑粑,如果想吃訂購(gòu)請(qǐng)打電話:13900901878").build(),
DefaultLogicFactory.LogicModel.SENSITIVE_WORD,
DefaultLogicFactory.LogicModel.CONTENT_SECURITY
);
log.info("測(cè)試結(jié)果:{}", JSON.toJSONString(entity));
}
}
測(cè)試結(jié)果
24-01-07.14:17:16.988 [main ] INFO BaseClient - get access_token success. current state: STATE_AIP_AUTH_OK
24-01-07.14:17:17.328 [main ] INFO RuleLogicTest - 測(cè)試結(jié)果:{"data":{"content":"小傅哥喜歡燒烤***,豆包愛(ài)吃**,如果想吃訂購(gòu)請(qǐng)打電話:13900901878"},"type":"SUCCESS"}
2. 接口測(cè)試
@RequestMapping(value = "sensitive/rule", method = RequestMethod.GET)
public String rule(String content) {
try {
log.info("內(nèi)容審核開始 content: {}", content);
RuleActionEntity<RuleMatterEntity> entity = defaultLogicFactory.doCheckLogic(RuleMatterEntity.builder().content(content).build(),
DefaultLogicFactory.LogicModel.SENSITIVE_WORD,
DefaultLogicFactory.LogicModel.CONTENT_SECURITY
);
log.info("內(nèi)容審核完成 content: {}", entity.getData());
return JSON.toJSONString(entity);
} catch (Exception e) {
log.error("內(nèi)容審核異常 content: {}", content, e);
return "Err!";
}
}
接口:http://localhost:8091/api/v1/content/sensitive/rule?content=小傅哥喜歡燒烤臭毛蛋,豆包愛(ài)吃粑粑,如果想吃訂購(gòu)請(qǐng)打電話:13900901878
- 那么現(xiàn)在就可以對(duì)內(nèi)容進(jìn)行審核過(guò)濾了。
六、推薦閱讀
- 商品下單支付場(chǎng)景,DDD設(shè)計(jì)實(shí)現(xiàn)「支付寶沙箱」
- MVC2DDD - 架構(gòu)重構(gòu)
- Mock 單元測(cè)試&插件生成測(cè)試代碼
- 《大營(yíng)銷平臺(tái)系統(tǒng)》—— 小傅哥第8個(gè)項(xiàng)目,前后端 + Dev-Ops 的全棧式綜合編程實(shí)戰(zhàn)DDD項(xiàng)目!
總結(jié)
以上是生活随笔為你收集整理的如果有人在你的论坛、博客,乱留言、乱回复,怎么办?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Numpy计算近邻表时间对比
- 下一篇: 简单介绍JDK、JRE、JVM三者区别