From 53201465f2197b6b3658678d6ee87aa018ac21fd Mon Sep 17 00:00:00 2001 From: zhongzb <972627721@qq.com> Date: Sun, 28 May 2023 15:29:50 +0800 Subject: [PATCH] =?UTF-8?q?feat:=201.=E9=93=BE=E6=8E=A5=E8=B7=B3=E8=BD=AC?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=202.=E5=BD=92=E5=B1=9E=E5=9C=B0=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=20fix:=201.=E4=BF=AE=E5=A4=8D=E5=9B=9E=E5=A4=8D?= =?UTF-8?q?=E8=B7=B3=E8=BD=AC=E5=8A=9F=E8=83=BD=202.=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E7=BF=BB=E9=A1=B5=E5=B7=A5=E5=85=B7=E7=B1=BB=203,=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E4=B8=8B=E7=BA=BF=E9=87=8D=E7=BD=AEip=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + docs/mallchat.sql | 5 +- mallchat-common/pom.xml | 5 + .../mallchat/common/chat/dao/MessageDao.java | 3 +- .../common/chat/domain/entity/Message.java | 9 +- .../chat/domain/entity/MessageExtra.java | 25 ++++ .../vo/response/CursorPageBaseResp.java | 11 +- .../common/common/utils/FutureUtils.java | 125 ++++++++++++++++++ .../discover/AbstractUrlTitleDiscover.java | 77 +++++++++++ .../discover/CommonUrlTitleDiscover.java | 16 +++ .../discover/PrioritizedUrlTitleDiscover.java | 34 +++++ .../utils/discover/UrlTitleDiscover.java | 38 ++++++ .../utils/discover/WxUrlTitleDiscover.java | 15 +++ .../common/user/domain/entity/User.java | 6 +- .../user/service/impl/IpServiceImpl.java | 3 + .../chat/controller/ChatController.java | 4 +- .../domain/vo/response/ChatMessageResp.java | 12 +- .../chat/service/adapter/MessageAdapter.java | 22 ++- .../chat/service/impl/ChatServiceImpl.java | 8 +- .../user/service/impl/LoginServiceImpl.java | 2 +- .../service/impl/WebSocketServiceImpl.java | 2 +- .../user/websocket/NettyWebSocketServer.java | 2 +- pom.xml | 6 + 23 files changed, 400 insertions(+), 31 deletions(-) create mode 100644 mallchat-common/src/main/java/com/abin/mallchat/common/chat/domain/entity/MessageExtra.java create mode 100644 mallchat-common/src/main/java/com/abin/mallchat/common/common/utils/FutureUtils.java create mode 100644 mallchat-common/src/main/java/com/abin/mallchat/common/common/utils/discover/AbstractUrlTitleDiscover.java create mode 100644 mallchat-common/src/main/java/com/abin/mallchat/common/common/utils/discover/CommonUrlTitleDiscover.java create mode 100644 mallchat-common/src/main/java/com/abin/mallchat/common/common/utils/discover/PrioritizedUrlTitleDiscover.java create mode 100644 mallchat-common/src/main/java/com/abin/mallchat/common/common/utils/discover/UrlTitleDiscover.java create mode 100644 mallchat-common/src/main/java/com/abin/mallchat/common/common/utils/discover/WxUrlTitleDiscover.java diff --git a/.gitignore b/.gitignore index a5b6c13..fe37234 100644 --- a/.gitignore +++ b/.gitignore @@ -67,6 +67,7 @@ CodeGenerator.java TestController.java application-my-prod.properties application-remote-prod.properties +application-my-test.properties mybatis-generator-config.xml ChannelBizRankClient.java ChannelBizBrandWallClient.java diff --git a/docs/mallchat.sql b/docs/mallchat.sql index 266c79b..211f22a 100644 --- a/docs/mallchat.sql +++ b/docs/mallchat.sql @@ -54,6 +54,7 @@ CREATE TABLE `message` ( `status` int(11) NOT NULL COMMENT '消息状态 0正常 1删除', `gap_count` int(11) NULL DEFAULT NULL COMMENT '与回复的消息间隔多少条', `type` int(11) NULL DEFAULT 1 COMMENT '消息类型 1正常文本 2.爆赞 (点赞超过10)3.危险发言(举报超5)', + `extra` json DEFAULT NULL COMMENT '扩展信息', `create_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', `update_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '修改时间', PRIMARY KEY (`id`) USING BTREE, @@ -163,10 +164,10 @@ DROP TABLE IF EXISTS `black`; CREATE TABLE `black` ( `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'id', `type` int(11) NOT NULL COMMENT '拉黑目标类型 1.ip 2uid', - `target` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '拉黑目标', + `target` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '拉黑目标', `create_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', `update_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '修改时间', PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `idx_type_target`(`type`, `target`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '黑名单' ROW_FORMAT = Dynamic; +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '黑名单' ROW_FORMAT = Dynamic; SET FOREIGN_KEY_CHECKS = 1; diff --git a/mallchat-common/pom.xml b/mallchat-common/pom.xml index 79db425..3dc8f61 100644 --- a/mallchat-common/pom.xml +++ b/mallchat-common/pom.xml @@ -24,6 +24,11 @@ cn.hutool hutool-all + + + org.jsoup + jsoup + org.mybatis diff --git a/mallchat-common/src/main/java/com/abin/mallchat/common/chat/dao/MessageDao.java b/mallchat-common/src/main/java/com/abin/mallchat/common/chat/dao/MessageDao.java index 98fbe49..e143014 100644 --- a/mallchat-common/src/main/java/com/abin/mallchat/common/chat/dao/MessageDao.java +++ b/mallchat-common/src/main/java/com/abin/mallchat/common/chat/dao/MessageDao.java @@ -55,6 +55,7 @@ public class MessageDao extends ServiceImpl { public void updateGapCount(Long id, Integer gapCount) { lambdaUpdate() .eq(Message::getId, id) - .set(Message::getGapCount, gapCount); + .set(Message::getGapCount, gapCount) + .update(); } } diff --git a/mallchat-common/src/main/java/com/abin/mallchat/common/chat/domain/entity/Message.java b/mallchat-common/src/main/java/com/abin/mallchat/common/chat/domain/entity/Message.java index ec81daf..64f0a09 100644 --- a/mallchat-common/src/main/java/com/abin/mallchat/common/chat/domain/entity/Message.java +++ b/mallchat-common/src/main/java/com/abin/mallchat/common/chat/domain/entity/Message.java @@ -1,5 +1,6 @@ package com.abin.mallchat.common.chat.domain.entity; +import com.abin.mallchat.common.user.domain.entity.IpInfo; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; @@ -8,6 +9,7 @@ import com.baomidou.mybatisplus.annotation.TableField; import java.io.Serializable; import java.util.Date; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; import lombok.*; import lombok.experimental.Accessors; @@ -21,7 +23,7 @@ import lombok.experimental.Accessors; */ @Data @EqualsAndHashCode(callSuper = false) -@TableName("message") +@TableName(value = "message",autoResultMap = true) @Builder @AllArgsConstructor @NoArgsConstructor @@ -79,6 +81,11 @@ public class Message implements Serializable { @TableField("type") private Integer type; + /** + * 最后上下线时间 + */ + @TableField(value = "extra", typeHandler = JacksonTypeHandler.class) + private MessageExtra extra; /** * 创建时间 diff --git a/mallchat-common/src/main/java/com/abin/mallchat/common/chat/domain/entity/MessageExtra.java b/mallchat-common/src/main/java/com/abin/mallchat/common/chat/domain/entity/MessageExtra.java new file mode 100644 index 0000000..56c70f0 --- /dev/null +++ b/mallchat-common/src/main/java/com/abin/mallchat/common/chat/domain/entity/MessageExtra.java @@ -0,0 +1,25 @@ +package com.abin.mallchat.common.chat.domain.entity; + +import com.abin.mallchat.common.user.domain.entity.IpDetail; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.Map; + +/** + * Description: 消息扩展属性 + * Author: abin + * Date: 2023-05-28 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class MessageExtra implements Serializable { + private static final long serialVersionUID = 1L; + //注册时的ip + private Map urlTitleMap; +} diff --git a/mallchat-common/src/main/java/com/abin/mallchat/common/common/domain/vo/response/CursorPageBaseResp.java b/mallchat-common/src/main/java/com/abin/mallchat/common/common/domain/vo/response/CursorPageBaseResp.java index 2be47a2..eede4e2 100644 --- a/mallchat-common/src/main/java/com/abin/mallchat/common/common/domain/vo/response/CursorPageBaseResp.java +++ b/mallchat-common/src/main/java/com/abin/mallchat/common/common/domain/vo/response/CursorPageBaseResp.java @@ -32,22 +32,23 @@ public class CursorPageBaseResp { @ApiModelProperty("数据列表") private List list; - public static CursorPageBaseResp init(CursorPageBaseResp cursorPage, List list) { - CursorPageBaseResp cursorPageBaseResp = new CursorPageBaseResp(); + public static CursorPageBaseResp init(CursorPageBaseResp cursorPage, List list) { + CursorPageBaseResp cursorPageBaseResp = new CursorPageBaseResp(); cursorPageBaseResp.setIsLast(cursorPage.getIsLast()); cursorPageBaseResp.setList(list); cursorPageBaseResp.setCursor(cursorPage.getCursor()); return cursorPageBaseResp; } + @JsonIgnore public Boolean isEmpty() { return CollectionUtil.isEmpty(list); } - public static CursorPageBaseResp empty() { - CursorPageBaseResp cursorPageBaseResp = new CursorPageBaseResp(); + public static CursorPageBaseResp empty() { + CursorPageBaseResp cursorPageBaseResp = new CursorPageBaseResp(); cursorPageBaseResp.setIsLast(true); - cursorPageBaseResp.setList(new ArrayList()); + cursorPageBaseResp.setList(new ArrayList()); return cursorPageBaseResp; } diff --git a/mallchat-common/src/main/java/com/abin/mallchat/common/common/utils/FutureUtils.java b/mallchat-common/src/main/java/com/abin/mallchat/common/common/utils/FutureUtils.java new file mode 100644 index 0000000..076331d --- /dev/null +++ b/mallchat-common/src/main/java/com/abin/mallchat/common/common/utils/FutureUtils.java @@ -0,0 +1,125 @@ +package com.abin.mallchat.common.common.utils; + +import lombok.extern.slf4j.Slf4j; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.function.BinaryOperator; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * 美团的CompletableFuture封装工具类,参考文章https://mp.weixin.qq.com/s/GQGidprakfticYnbVYVYGQ + */ +@Slf4j +public class FutureUtils { + /** + * 设置CF状态为失败 + */ + public static CompletableFuture failed(Throwable ex) { + CompletableFuture completableFuture = new CompletableFuture<>(); + completableFuture.completeExceptionally(ex); + return completableFuture; + } + /** + * 设置CF状态为成功 + */ + public static CompletableFuture success(T result) { + CompletableFuture completableFuture = new CompletableFuture<>(); + completableFuture.complete(result); + return completableFuture; + } + /** + * 将List> 转为 CompletableFuture> + */ + public static CompletableFuture> sequence(Collection> completableFutures) { + return CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0])) + .thenApply(v -> completableFutures.stream() + .map(CompletableFuture::join) + .collect(Collectors.toList()) + ); + } + /** + * 将List>> 转为 CompletableFuture> + * 多用于分页查询的场景 + */ + public static CompletableFuture> sequenceList(Collection>> completableFutures) { + return CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0])) + .thenApply(v -> completableFutures.stream() + .flatMap( listFuture -> listFuture.join().stream()) + .collect(Collectors.toList()) + ); + } + /* + * 将List>> 转为 CompletableFuture> + * @Param mergeFunction 自定义key冲突时的merge策略 + */ + public static CompletableFuture> sequenceMap( + Collection>> completableFutures, BinaryOperator mergeFunction) { + return CompletableFuture + .allOf(completableFutures.toArray(new CompletableFuture[0])) + .thenApply(v -> completableFutures.stream().map(CompletableFuture::join) + .flatMap(map -> map.entrySet().stream()) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, mergeFunction))); + } + /** + * 将List> 转为 CompletableFuture>,并过滤调null值 + */ + public static CompletableFuture> sequenceNonNull(Collection> completableFutures) { + return CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0])) + .thenApply(v -> completableFutures.stream() + .map(CompletableFuture::join) + .filter(Objects::nonNull) + .collect(Collectors.toList()) + ); + } + /** + * 将List>> 转为 CompletableFuture>,并过滤调null值 + * 多用于分页查询的场景 + */ + public static CompletableFuture> sequenceListNonNull(Collection>> completableFutures) { + return CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0])) + .thenApply(v -> completableFutures.stream() + .flatMap( listFuture -> listFuture.join().stream().filter(Objects::nonNull)) + .collect(Collectors.toList()) + ); + } + /** + * 将List>> 转为 CompletableFuture> + * @Param filterFunction 自定义过滤策略 + */ + public static CompletableFuture> sequence(Collection> completableFutures, + Predicate filterFunction) { + return CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0])) + .thenApply(v -> completableFutures.stream() + .map(CompletableFuture::join) + .filter(filterFunction) + .collect(Collectors.toList()) + ); + } + /** + * 将List>> 转为 CompletableFuture> + * @Param filterFunction 自定义过滤策略 + */ + public static CompletableFuture> sequenceList(Collection>> completableFutures, + Predicate filterFunction) { + return CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0])) + .thenApply(v -> completableFutures.stream() + .flatMap( listFuture -> listFuture.join().stream().filter(filterFunction)) + .collect(Collectors.toList()) + ); + } +/** + * 将CompletableFuture>的list转为 CompletableFuture>。 多个map合并为一个map。 如果key冲突,采用新的value覆盖。 + */ + public static CompletableFuture> sequenceMap( + Collection>> completableFutures) { + return CompletableFuture + .allOf(completableFutures.toArray(new CompletableFuture[0])) + .thenApply(v -> completableFutures.stream().map(CompletableFuture::join) + .flatMap(map -> map.entrySet().stream()) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> b))); + }} \ No newline at end of file diff --git a/mallchat-common/src/main/java/com/abin/mallchat/common/common/utils/discover/AbstractUrlTitleDiscover.java b/mallchat-common/src/main/java/com/abin/mallchat/common/common/utils/discover/AbstractUrlTitleDiscover.java new file mode 100644 index 0000000..d6f04a2 --- /dev/null +++ b/mallchat-common/src/main/java/com/abin/mallchat/common/common/utils/discover/AbstractUrlTitleDiscover.java @@ -0,0 +1,77 @@ +package com.abin.mallchat.common.common.utils.discover; + +import cn.hutool.core.util.ReUtil; +import cn.hutool.core.util.StrUtil; +import com.abin.mallchat.common.common.utils.FutureUtils; +import com.google.errorprone.annotations.Var; +import lombok.extern.slf4j.Slf4j; +import org.jsoup.Connection; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.springframework.data.util.Pair; + +import javax.annotation.Nullable; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.regex.Pattern; +import java.util.stream.Collector; +import java.util.stream.Collectors; + +/** + * Description: urlTitle查询抽象类 + * Author: abin + * Date: 2023-05-27 + */ +@Slf4j +public abstract class AbstractUrlTitleDiscover implements UrlTitleDiscover { + //链接识别的正则 + private static final Pattern PATTERN = Pattern.compile("((http|https)://)?(www.)?([\\w_-]+(?:(?:\\.[\\w_-]+)+))([\\w.,@?^=%&:/~+#-]*[\\w@?^=%&/~+#-])?"); + + @Nullable + @Override + public Map getContentTitleMap(String content) { + if (StrUtil.isBlank(content)) { + return new HashMap<>(); + } + List matchList = ReUtil.findAll(PATTERN, content, 0); + //并行请求 + List>> futures = matchList.stream().map(match -> CompletableFuture.supplyAsync(() -> { + String title = getUrlTitle(match); + return Objects.nonNull(title) ? Pair.of(match, title) : null; + })).collect(Collectors.toList()); + CompletableFuture>> future = FutureUtils.sequenceNonNull(futures); + //结果组装 + return future.join().stream().collect(Collectors.toMap(Pair::getFirst, Pair::getSecond, (a, b) -> a)); + } + + @Nullable + @Override + public String getUrlTitle(String url) { + Document document = getUrlDocument(assemble(url)); + if (Objects.isNull(document)) { + return null; + } + return getDocTitle(document); + } + + private String assemble(String url) { + if (!StrUtil.startWith(url, "http")) { + return "http://" + url; + } + return url; + } + + protected Document getUrlDocument(String matchUrl) { + try { + Connection connect = Jsoup.connect(matchUrl); + connect.timeout(1000); + return connect.get(); + } catch (Exception e) { + log.error("find title error:url:{}", matchUrl, e); + } + return null; + } +} diff --git a/mallchat-common/src/main/java/com/abin/mallchat/common/common/utils/discover/CommonUrlTitleDiscover.java b/mallchat-common/src/main/java/com/abin/mallchat/common/common/utils/discover/CommonUrlTitleDiscover.java new file mode 100644 index 0000000..ac38d87 --- /dev/null +++ b/mallchat-common/src/main/java/com/abin/mallchat/common/common/utils/discover/CommonUrlTitleDiscover.java @@ -0,0 +1,16 @@ +package com.abin.mallchat.common.common.utils.discover; + +import cn.hutool.core.util.StrUtil; +import org.jsoup.nodes.Document; + +/** + * Description: 通用的标题解析类 + * Author: abin + * Date: 2023-05-27 + */ +public class CommonUrlTitleDiscover extends AbstractUrlTitleDiscover { + @Override + public String getDocTitle(Document document) { + return document.title(); + } +} diff --git a/mallchat-common/src/main/java/com/abin/mallchat/common/common/utils/discover/PrioritizedUrlTitleDiscover.java b/mallchat-common/src/main/java/com/abin/mallchat/common/common/utils/discover/PrioritizedUrlTitleDiscover.java new file mode 100644 index 0000000..bd7c7e6 --- /dev/null +++ b/mallchat-common/src/main/java/com/abin/mallchat/common/common/utils/discover/PrioritizedUrlTitleDiscover.java @@ -0,0 +1,34 @@ +package com.abin.mallchat.common.common.utils.discover; + +import cn.hutool.core.util.StrUtil; +import org.jsoup.nodes.Document; +import org.springframework.core.ParameterNameDiscoverer; + +import java.util.ArrayList; +import java.util.List; + +/** + * Description: 具有优先级的title查询器 + * Author: abin + * Date: 2023-05-27 + */ +public class PrioritizedUrlTitleDiscover extends AbstractUrlTitleDiscover { + + private final List urlTitleDiscovers = new ArrayList<>(2); + + public PrioritizedUrlTitleDiscover() { + urlTitleDiscovers.add(new CommonUrlTitleDiscover()); + urlTitleDiscovers.add(new WxUrlTitleDiscover()); + } + + @Override + public String getDocTitle(Document document) { + for (UrlTitleDiscover urlTitleDiscover : urlTitleDiscovers) { + String urlTitle = urlTitleDiscover.getDocTitle(document); + if (StrUtil.isNotBlank(urlTitle)) { + return urlTitle; + } + } + return null; + } +} diff --git a/mallchat-common/src/main/java/com/abin/mallchat/common/common/utils/discover/UrlTitleDiscover.java b/mallchat-common/src/main/java/com/abin/mallchat/common/common/utils/discover/UrlTitleDiscover.java new file mode 100644 index 0000000..beba0e7 --- /dev/null +++ b/mallchat-common/src/main/java/com/abin/mallchat/common/common/utils/discover/UrlTitleDiscover.java @@ -0,0 +1,38 @@ +package com.abin.mallchat.common.common.utils.discover; + +import cn.hutool.core.date.StopWatch; +import com.google.common.base.Stopwatch; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; + +import javax.annotation.Nullable; +import javax.annotation.Signed; +import java.util.Map; + +public interface UrlTitleDiscover { + + + @Nullable + Map getContentTitleMap(String content); + + + @Nullable + String getUrlTitle(String url); + + @Nullable + String getDocTitle(Document document); + + public static void main(String[] args) {//用异步多任务查询并合并 974 //串行访问的速度1349 1291 1283 1559 + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + String longStr = "这是一个很长的字符串再来 www.github.com,其中包含一个URL www.baidu.com,, 一个带有端口号的URL http://www.jd.com:80, 一个带有路径的URL http://mallchat.cn, 还有美团技术文章https://mp.weixin.qq.com/s/hwTf4bDck9_tlFpgVDeIKg "; + PrioritizedUrlTitleDiscover discover =new PrioritizedUrlTitleDiscover(); + Map contentTitleMap = discover.getContentTitleMap(longStr); + System.out.println(contentTitleMap); +// +// Jsoup.connect("http:// www.github.com"); + stopWatch.stop(); + long cost = stopWatch.getTotalTimeMillis(); + System.out.println(cost); + }//{http://mallchat.cn=MallChat, www.baidu.com=百度一下,你就知道, https://mp.weixin.qq.com/s/hwTf4bDck9_tlFpgVDeIKg=超大规模数据库集群保稳系列之二:数据库攻防演练建设实践, http://www.jd.com:80=京东(JD.COM)-正品低价、品质保障、配送及时、轻松购物!} +} diff --git a/mallchat-common/src/main/java/com/abin/mallchat/common/common/utils/discover/WxUrlTitleDiscover.java b/mallchat-common/src/main/java/com/abin/mallchat/common/common/utils/discover/WxUrlTitleDiscover.java new file mode 100644 index 0000000..29b1172 --- /dev/null +++ b/mallchat-common/src/main/java/com/abin/mallchat/common/common/utils/discover/WxUrlTitleDiscover.java @@ -0,0 +1,15 @@ +package com.abin.mallchat.common.common.utils.discover; + +import org.jsoup.nodes.Document; + +/** + * Description: 针对微信公众号文章的标题获取类 + * Author: abin + * Date: 2023-05-27 + */ +public class WxUrlTitleDiscover extends AbstractUrlTitleDiscover { + @Override + public String getDocTitle(Document document) { + return document.getElementsByAttributeValue("property", "og:title").attr("content"); + } +} diff --git a/mallchat-common/src/main/java/com/abin/mallchat/common/user/domain/entity/User.java b/mallchat-common/src/main/java/com/abin/mallchat/common/user/domain/entity/User.java index e4f4993..1eda7b4 100644 --- a/mallchat-common/src/main/java/com/abin/mallchat/common/user/domain/entity/User.java +++ b/mallchat-common/src/main/java/com/abin/mallchat/common/user/domain/entity/User.java @@ -10,6 +10,7 @@ import com.baomidou.mybatisplus.annotation.TableField; import java.io.Serializable; import java.util.Date; +import java.util.Objects; import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; import lombok.*; @@ -99,11 +100,10 @@ public class User implements Serializable { @TableField("update_time") private Date updateTime; - public IpInfo getIpInfo() { + public void refreshIp(String ip) { if (ipInfo == null) { ipInfo = new IpInfo(); } - return ipInfo; + ipInfo.refreshIp(ip); } - } diff --git a/mallchat-common/src/main/java/com/abin/mallchat/common/user/service/impl/IpServiceImpl.java b/mallchat-common/src/main/java/com/abin/mallchat/common/user/service/impl/IpServiceImpl.java index c41fca5..3868051 100644 --- a/mallchat-common/src/main/java/com/abin/mallchat/common/user/service/impl/IpServiceImpl.java +++ b/mallchat-common/src/main/java/com/abin/mallchat/common/user/service/impl/IpServiceImpl.java @@ -45,6 +45,9 @@ public class IpServiceImpl implements IpService, DisposableBean { executor.execute(() -> { User user = userDao.getById(uid); IpInfo ipInfo = user.getIpInfo(); + if (Objects.isNull(ipInfo)) { + return; + } String ip = ipInfo.needRefreshIp(); if (StrUtil.isBlank(ip)) { return; diff --git a/mallchat-custom-server/src/main/java/com/abin/mallchat/custom/chat/controller/ChatController.java b/mallchat-custom-server/src/main/java/com/abin/mallchat/custom/chat/controller/ChatController.java index d23af39..16f3220 100644 --- a/mallchat-custom-server/src/main/java/com/abin/mallchat/custom/chat/controller/ChatController.java +++ b/mallchat-custom-server/src/main/java/com/abin/mallchat/custom/chat/controller/ChatController.java @@ -27,6 +27,8 @@ import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import java.util.Map; import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** *

@@ -66,7 +68,7 @@ public class ChatController { return userCache.getBlackMap().get(BlackTypeEnum.UID.getType()); } - @GetMapping("/public/member/statistic") + @GetMapping("public/member/statistic/") @ApiOperation("群成员人数统计") public ApiResult getMemberStatistic() { return ApiResult.success(chatService.getMemberStatistic()); diff --git a/mallchat-custom-server/src/main/java/com/abin/mallchat/custom/chat/domain/vo/response/ChatMessageResp.java b/mallchat-custom-server/src/main/java/com/abin/mallchat/custom/chat/domain/vo/response/ChatMessageResp.java index fc011da..ce5a89f 100644 --- a/mallchat-custom-server/src/main/java/com/abin/mallchat/custom/chat/domain/vo/response/ChatMessageResp.java +++ b/mallchat-custom-server/src/main/java/com/abin/mallchat/custom/chat/domain/vo/response/ChatMessageResp.java @@ -1,18 +1,12 @@ package com.abin.mallchat.custom.chat.domain.vo.response; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import lombok.experimental.Accessors; - -import java.time.LocalDateTime; import java.util.Date; -import java.util.List; +import java.util.Map; /** * Description: 消息 @@ -38,6 +32,8 @@ public class ChatMessageResp { private Long uid; @ApiModelProperty("头像") private String avatar; + @ApiModelProperty("归属地") + private String locPlace; @ApiModelProperty("徽章标识,如果没有展示null") private Badge badge; } @@ -50,6 +46,8 @@ public class ChatMessageResp { private Date sendTime; @ApiModelProperty("消息内容") private String content; + @ApiModelProperty("消息链接映射") + private Map urlTitleMap; @ApiModelProperty("消息类型 1正常文本 2.爆赞 (点赞超过10)3.危险发言(举报超5)") private Integer type; @ApiModelProperty("消息标记") diff --git a/mallchat-custom-server/src/main/java/com/abin/mallchat/custom/chat/service/adapter/MessageAdapter.java b/mallchat-custom-server/src/main/java/com/abin/mallchat/custom/chat/service/adapter/MessageAdapter.java index 22c9f64..9acd0c0 100644 --- a/mallchat-custom-server/src/main/java/com/abin/mallchat/custom/chat/service/adapter/MessageAdapter.java +++ b/mallchat-custom-server/src/main/java/com/abin/mallchat/custom/chat/service/adapter/MessageAdapter.java @@ -2,11 +2,15 @@ package com.abin.mallchat.custom.chat.service.adapter; import cn.hutool.core.bean.BeanUtil; import com.abin.mallchat.common.chat.domain.entity.Message; +import com.abin.mallchat.common.chat.domain.entity.MessageExtra; import com.abin.mallchat.common.chat.domain.entity.MessageMark; import com.abin.mallchat.common.chat.domain.enums.MessageMarkTypeEnum; import com.abin.mallchat.common.chat.domain.enums.MessageStatusEnum; import com.abin.mallchat.common.chat.domain.enums.MessageTypeEnum; import com.abin.mallchat.common.common.domain.enums.YesOrNoEnum; +import com.abin.mallchat.common.common.utils.discover.PrioritizedUrlTitleDiscover; +import com.abin.mallchat.common.user.domain.entity.IpDetail; +import com.abin.mallchat.common.user.domain.entity.IpInfo; import com.abin.mallchat.common.user.domain.entity.ItemConfig; import com.abin.mallchat.common.user.domain.entity.User; import com.abin.mallchat.custom.chat.domain.vo.request.ChatMessageReq; @@ -23,24 +27,32 @@ import java.util.stream.Collectors; * Date: 2023-03-26 */ public class MessageAdapter { - public static final int CAN_CALLBACK_GAP_COUNT = 100; + public static final int CAN_CALLBACK_GAP_COUNT = 50; + private static final PrioritizedUrlTitleDiscover URL_TITLE_DISCOVER = new PrioritizedUrlTitleDiscover(); public static Message buildMsgSave(ChatMessageReq request, Long uid) { + return Message.builder() .replyMsgId(request.getReplyMsgId()) .content(request.getContent()) .fromUid(uid) .roomId(request.getRoomId()) .status(MessageStatusEnum.NORMAL.getStatus()) + .extra(buildExtra(request)) .build(); } + private static MessageExtra buildExtra(ChatMessageReq request) { + Map contentTitleMap = URL_TITLE_DISCOVER.getContentTitleMap(request.getContent()); + return MessageExtra.builder().urlTitleMap(contentTitleMap).build(); + } + public static List buildMsgResp(List messages, Map replyMap, Map userMap, List msgMark, Long receiveUid, Map itemMap) { Map> markMap = msgMark.stream().collect(Collectors.groupingBy(MessageMark::getMsgId)); return messages.stream().map(a -> { ChatMessageResp resp = new ChatMessageResp(); - resp.setFromUser(buildFromUser(userMap.get(a.getFromUid()),itemMap)); + resp.setFromUser(buildFromUser(userMap.get(a.getFromUid()), itemMap)); resp.setMessage(buildMessage(a, replyMap, userMap, markMap.getOrDefault(a.getId(), new ArrayList<>()), receiveUid)); return resp; }) @@ -52,6 +64,7 @@ public class MessageAdapter { ChatMessageResp.Message messageVO = new ChatMessageResp.Message(); BeanUtil.copyProperties(message, messageVO); messageVO.setSendTime(message.getCreateTime()); + messageVO.setUrlTitleMap(Optional.ofNullable(message.getExtra()).map(MessageExtra::getUrlTitleMap).orElse(null)); Message replyMessage = replyMap.get(message.getReplyMsgId()); //回复消息 if (Objects.nonNull(replyMessage)) { @@ -85,9 +98,10 @@ public class MessageAdapter { ChatMessageResp.UserInfo userInfo = new ChatMessageResp.UserInfo(); userInfo.setUsername(fromUser.getName()); userInfo.setAvatar(fromUser.getAvatar()); + userInfo.setLocPlace(Optional.ofNullable(fromUser.getIpInfo()).map(IpInfo::getUpdateIpDetail).map(IpDetail::getCity).orElse(null)); userInfo.setUid(fromUser.getId()); - if(Objects.nonNull(fromUser.getItemId())){ - ChatMessageResp.Badge badge =new ChatMessageResp.Badge(); + if (Objects.nonNull(fromUser.getItemId())) { + ChatMessageResp.Badge badge = new ChatMessageResp.Badge(); ItemConfig itemConfig = itemMap.get(fromUser.getItemId()); badge.setImg(itemConfig.getImg()); badge.setDescribe(itemConfig.getDescribe()); diff --git a/mallchat-custom-server/src/main/java/com/abin/mallchat/custom/chat/service/impl/ChatServiceImpl.java b/mallchat-custom-server/src/main/java/com/abin/mallchat/custom/chat/service/impl/ChatServiceImpl.java index 001c26f..92afcb0 100644 --- a/mallchat-custom-server/src/main/java/com/abin/mallchat/custom/chat/service/impl/ChatServiceImpl.java +++ b/mallchat-custom-server/src/main/java/com/abin/mallchat/custom/chat/service/impl/ChatServiceImpl.java @@ -16,6 +16,7 @@ import com.abin.mallchat.common.common.domain.vo.request.CursorPageBaseReq; import com.abin.mallchat.common.common.domain.vo.response.CursorPageBaseResp; import com.abin.mallchat.common.common.exception.BusinessException; import com.abin.mallchat.common.common.utils.AssertUtil; +import com.abin.mallchat.common.common.utils.discover.PrioritizedUrlTitleDiscover; import com.abin.mallchat.common.user.dao.UserDao; import com.abin.mallchat.common.user.domain.entity.ItemConfig; import com.abin.mallchat.common.user.domain.entity.User; @@ -76,8 +77,6 @@ public class ChatServiceImpl implements ChatService { /** * 发送消息 - * - * @param request */ @Override @Transactional @@ -90,6 +89,7 @@ public class ChatServiceImpl implements ChatService { AssertUtil.equal(replyMsg.getRoomId(), request.getRoomId(), "只能回复相同会话内的消息"); } + //同步获取消息的跳转链接标题 Message insert = MessageAdapter.buildMsgSave(request, uid); messageDao.save(insert); //如果有回复消息 @@ -224,10 +224,10 @@ public class ChatServiceImpl implements ChatService { Set uidSet = Stream.concat(replyMap.values().stream().map(Message::getFromUid), messages.stream().map(Message::getFromUid)).collect(Collectors.toSet()); userMap = userCache.getUserInfoBatch(uidSet); //批量查询item信息 - itemMap = userMap.values().stream().map(User::getItemId).distinct().filter(Objects::nonNull).map(itemCache::getById).collect(Collectors.toMap(ItemConfig::getId,Function.identity())); + itemMap = userMap.values().stream().map(User::getItemId).distinct().filter(Objects::nonNull).map(itemCache::getById).collect(Collectors.toMap(ItemConfig::getId, Function.identity())); //查询消息标志 List msgMark = messageMarkDao.getValidMarkByMsgIdBatch(messages.stream().map(Message::getId).collect(Collectors.toList())); - return MessageAdapter.buildMsgResp(messages, replyMap, userMap, msgMark, receiveUid,itemMap); + return MessageAdapter.buildMsgResp(messages, replyMap, userMap, msgMark, receiveUid, itemMap); } } diff --git a/mallchat-custom-server/src/main/java/com/abin/mallchat/custom/user/service/impl/LoginServiceImpl.java b/mallchat-custom-server/src/main/java/com/abin/mallchat/custom/user/service/impl/LoginServiceImpl.java index 1c203ed..14f916a 100644 --- a/mallchat-custom-server/src/main/java/com/abin/mallchat/custom/user/service/impl/LoginServiceImpl.java +++ b/mallchat-custom-server/src/main/java/com/abin/mallchat/custom/user/service/impl/LoginServiceImpl.java @@ -45,7 +45,7 @@ public class LoginServiceImpl implements LoginService { return false; } String key = RedisKey.getKey(RedisKey.USER_TOKEN_STRING, uid); - String realToken = redisUtils.getStr(key); + String realToken = RedisUtils.getStr(key); return token.equals(realToken);//有可能token失效了,需要校验是不是和最新token一致 } diff --git a/mallchat-custom-server/src/main/java/com/abin/mallchat/custom/user/service/impl/WebSocketServiceImpl.java b/mallchat-custom-server/src/main/java/com/abin/mallchat/custom/user/service/impl/WebSocketServiceImpl.java index cb72515..cc1ddb4 100644 --- a/mallchat-custom-server/src/main/java/com/abin/mallchat/custom/user/service/impl/WebSocketServiceImpl.java +++ b/mallchat-custom-server/src/main/java/com/abin/mallchat/custom/user/service/impl/WebSocketServiceImpl.java @@ -157,7 +157,7 @@ public class WebSocketServiceImpl implements WebSocketService { boolean online = userCache.isOnline(user.getId()); if (!online) { user.setLastOptTime(new Date()); - user.getIpInfo().refreshIp(NettyUtil.getAttr(channel, NettyUtil.IP)); + user.refreshIp(NettyUtil.getAttr(channel, NettyUtil.IP)); applicationEventPublisher.publishEvent(new UserOnlineEvent(this, user)); } } diff --git a/mallchat-custom-server/src/main/java/com/abin/mallchat/custom/user/websocket/NettyWebSocketServer.java b/mallchat-custom-server/src/main/java/com/abin/mallchat/custom/user/websocket/NettyWebSocketServer.java index 9d44f2d..4c29c67 100644 --- a/mallchat-custom-server/src/main/java/com/abin/mallchat/custom/user/websocket/NettyWebSocketServer.java +++ b/mallchat-custom-server/src/main/java/com/abin/mallchat/custom/user/websocket/NettyWebSocketServer.java @@ -67,7 +67,7 @@ public class NettyWebSocketServer { protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline pipeline = socketChannel.pipeline(); //30秒客户端没有向服务器发送心跳则关闭连接 - pipeline.addLast(new IdleStateHandler(30, 0, 0)); +// pipeline.addLast(new IdleStateHandler(30, 0, 0)); // 因为使用http协议,所以需要使用http的编码器,解码器 pipeline.addLast(new HttpServerCodec()); // 以块方式写,添加 chunkedWriter 处理器 diff --git a/pom.xml b/pom.xml index eab49bf..4521335 100644 --- a/pom.xml +++ b/pom.xml @@ -44,6 +44,7 @@ 4.1.76.Final 4.4.0 3.4.0 + 1.15.3 @@ -53,6 +54,11 @@ mallchat-common ${mallchat-common.version} + + org.jsoup + jsoup + ${jsoup.version} + com.baomidou mybatis-plus-boot-starter