100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > java 控制台刷屏 dll_Java 用户刷屏检测\相似字符串检测

java 控制台刷屏 dll_Java 用户刷屏检测\相似字符串检测

时间:2019-12-11 21:08:23

相关推荐

java 控制台刷屏 dll_Java 用户刷屏检测\相似字符串检测

背景

近期有几个业务方提出一需求,期望判断一个用户在短期内是否存在刷屏现象,出现后能对其做出限制,并上报。

刷屏定义:取出用户近期20条评论,如果有50%的评论是"相似"的,则认为该用户是在刷屏

相似定义:两条评论的字符串最小编辑距离 / 长串的长度 < 0.2,即两串的80%是相同的,则认为两串相似。

@Slf4j

public class SimpleBrushDetectionFilter implements ReviewFilter {

// Todo 参数可实时调

private int USER_RECENT_REVIEW_LIST_SIZE = 20;

private int SIMILARITY_THRESHOLD = 80;

private double BRUSH_THRESHOLD = 0.5;// 该值不允许低于0.5,否则会出现用户循环被ban

private int BAN_SECOND = 3600 * 24;//一天

private int LIST_EXPIRE_SECOND = 3600 * 24 * 3;//三天

@Override

public ReviewFilterModel filter(ReviewFilterModel reviewFilterModel) {

if (reviewFilterModel.isEnd()) {

return reviewFilterModel;

}

long userId = reviewFilterModel.getReviewInfo().getUserId();

if (userId <= 0) {

log.info("错误的userId {}", userId);

return reviewFilterModel;

}

BrowserRedisService banRedisInstance = BrowserRedisService

.getRedisService(RedisPrefix.REVIEW_SIMPLE_BRUSH_DETECTION_BAN);

String str = banRedisInstance.get("" + userId);

if (StrUtil.isNotBlank(str)

// BAN_SECOND的expire set非原子性。出错时需要额外判断一下

&& (System.currentTimeMillis() - Long.parseLong(str)) < BAN_SECOND * 1000) {

banReview(reviewFilterModel, userId);

return reviewFilterModel;

}

if (StrUtil.isNotBlank(str) && (System.currentTimeMillis() - Long.parseLong(str)) > BAN_SECOND * 1000) {

banRedisInstance.del("" + userId);

}

return simpleBrushDetect(reviewFilterModel);

}

private void banReview(ReviewFilterModel reviewFilterModel, long userId) {

log.info("user {} 疑似刷屏,限制发表评论", userId);

reviewFilterModel.setEnd(true);

reviewFilterModel.setPass(false);

reviewFilterModel.setReason("该用户疑似近期出现恶意刷屏,限制发表评论");

}

private ReviewFilterModel simpleBrushDetect(ReviewFilterModel reviewFilterModel) {

BrowserRedisService listRedisInstance = BrowserRedisService

.getRedisService(RedisPrefix.REVIEW_SIMPLE_BRUSH_DETECTION_LIST);

long userId = reviewFilterModel.getReviewInfo().getUserId();

List userRecentReview = listRedisInstance

.lrange("" + userId, 0, USER_RECENT_REVIEW_LIST_SIZE);

if (null == userRecentReview) {

// 将当前评论塞入队列中

listRedisInstance.rpush("" + userId, reviewFilterModel.getReviewInfo().getDocuments());

return reviewFilterModel;

}

userRecentReview.add(reviewFilterModel.getReviewInfo().getDocuments());

// 正确的暴力做法是,将20个串依次互相两两对比,但是这样复杂度太高了

// 这里采用一个取巧的方法,将20个串按字典序排序,然后依次左右对比,效果应该也可以接受

Collections.sort(userRecentReview);

int cnt = 0;

for (int i = 0; i < userRecentReview.size() - 1; i++) {

int similarity = towStringSimilarity(userRecentReview.get(i),

userRecentReview.get(i + 1));

if (similarity > SIMILARITY_THRESHOLD) {

cnt++;

}

}

if (cnt > BRUSH_THRESHOLD * USER_RECENT_REVIEW_LIST_SIZE) {

log.info("user {} 疑似刷屏,禁止发言{}秒", userId, BAN_SECOND);

BrowserRedisService banRedisInstance = BrowserRedisService

.getRedisService(RedisPrefix.REVIEW_SIMPLE_BRUSH_DETECTION_BAN);

banRedisInstance.set("" + userId, "" + System.currentTimeMillis());

banRedisInstance.expire("" + userId, BAN_SECOND);

// 为了避免用户禁言到期后再次触发逻辑,list中删除2/3的评论

listRedisInstance.ltrim("" + userId, -1, -USER_RECENT_REVIEW_LIST_SIZE / 3);

banReview(reviewFilterModel, userId);

}

// 将当前评论塞入队列中

listRedisInstance.rpush("" + userId, reviewFilterModel.getReviewInfo().getDocuments());

listRedisInstance.ltrim("" + userId, -1, -USER_RECENT_REVIEW_LIST_SIZE);

// 刷新整条list的过期时间

listRedisInstance.expire("" + userId, LIST_EXPIRE_SECOND);

return reviewFilterModel;

}

/**

* 返回两个字符串的相似度。 当某个串长度小于5的时候,认为其不构成可比性

*

* @return int [0,100]

*/

private static int towStringSimilarity(String word1, String word2) {

if (word1.length() < 5 || word2.length() < 5) {

return 0;

}

int distance = towStringMinDistance(word1, word2);

return 100

- distance / (word1.length() > word2.length() ? word1.length() : word2.length()) * 100;

}

/**

* 返回两条字符串的最短编辑距离,

*

* 即将word2转变成word1的最小操作次数。

*

* 采用二维动态规划实现,时间复杂度O(N^2)

*/

private static int towStringMinDistance(String word1, String word2) {

int m = word1.length();

int n = word2.length();

if (m == 0) {

return n;

}

if (n == 0) {

return m;

}

int[][] f = new int[m + 1][n + 1];

for (int i = 0; i <= m; i++) {

f[i][0] = i;

}

for (int j = 0; j <= n; j++) {

f[0][j] = j;

}

for (int i = 1; i <= m; i++) {

for (int j = 1; j <= n; j++) {

if (word1.charAt(i - 1) == word2.charAt(j - 1)) {

f[i][j] = f[i - 1][j - 1];

} else {

f[i][j] = min(f[i - 1][j - 1], f[i - 1][j], f[i][j - 1]) + 1;

}

}

}

return f[m][n];

}

private static int min(int a, int b, int c) {

return (a > b ? (b > c ? c : b) : (a > c ? c : a));

}

}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。