Selaa lähdekoodia

责任链handle实现

wangqi49 6 päivää sitten
vanhempi
commit
17df1ec649
22 muutettua tiedostoa jossa 540 lisäystä ja 38 poistoa
  1. 1 0
      pom.xml
  2. 13 11
      resources/database-sql/webchat-ugc.sql
  3. 6 0
      webchat-common/pom.xml
  4. 6 0
      webchat-common/src/main/java/com/webchat/common/constants/MomentConstants.java
  5. 6 1
      webchat-common/src/main/java/com/webchat/common/enums/LocalCacheKey.java
  6. 76 0
      webchat-common/src/main/java/com/webchat/common/util/IPAddressUtil.java
  7. 164 0
      webchat-common/src/main/java/com/webchat/common/util/web/UrlAnalysisUtil.java
  8. 22 0
      webchat-domain/src/main/java/com/webchat/domain/dto/UrlAnalysisResultDTO.java
  9. 33 0
      webchat-domain/src/main/java/com/webchat/domain/vo/response/IpLocationResponseVO.java
  10. 2 0
      webchat-domain/src/main/java/com/webchat/domain/vo/response/moment/MomentLinkVO.java
  11. 2 0
      webchat-domain/src/main/java/com/webchat/domain/vo/response/moment/MomentMediaVO.java
  12. 7 0
      webchat-domain/src/main/java/com/webchat/domain/vo/response/moment/MomentVO.java
  13. 14 1
      webchat-ugc/src/main/java/com/webchat/ugc/messaegqueue/service/MomentPublishConsumeService.java
  14. 6 0
      webchat-ugc/src/main/java/com/webchat/ugc/repository/entity/MomentEntity.java
  15. 42 5
      webchat-ugc/src/main/java/com/webchat/ugc/service/chain/MomentImageHandler.java
  16. 6 4
      webchat-ugc/src/main/java/com/webchat/ugc/service/chain/MomentIpAddressHandler.java
  17. 30 4
      webchat-ugc/src/main/java/com/webchat/ugc/service/chain/MomentLinkHandler.java
  18. 84 3
      webchat-ugc/src/main/java/com/webchat/ugc/service/chain/MomentRefreshHandler.java
  19. 4 4
      webchat-ugc/src/main/java/com/webchat/ugc/service/chain/MomentReviewHandler.java
  20. 7 1
      webchat-ugc/src/main/java/com/webchat/ugc/service/chain/MomentVideoHandler.java
  21. 2 1
      webchat-ugc/src/main/java/com/webchat/ugc/service/moment/MomentService.java
  22. 7 3
      webchat-ugc/src/main/java/com/webchat/ugc/service/moment/MomentTimeLineService.java

+ 1 - 0
pom.xml

@@ -89,6 +89,7 @@
             <scope>provided</scope>
         </dependency>
 
+
         <!-- guava -->
         <dependency>
             <groupId>com.google.guava</groupId>

+ 13 - 11
resources/database-sql/webchat-ugc.sql

@@ -56,17 +56,19 @@ CREATE TABLE webchat_ugc.`web_chat_red_packet_record` (
 
 -- webchat朋友圈动态核心数据表
 CREATE TABLE webchat_ugc.`web_chat_moment` (
-       `ID` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID',
-       `author` char(100) NOT NULL COMMENT '动态作者 ',
-       `content` varchar(300) DEFAULT NULL COMMENT '正文(纯文本)',
-       `status` int(4) NOT NULL DEFAULT 1 COMMENT '状态',
-       `include_images` tinyint(1) DEFAULT 0 COMMENT '是否包含图片,冗余字段',
-       `include_video` tinyint(1) DEFAULT 0 COMMENT '是否包含图片,冗余字段',
-       `include_link` tinyint(1) DEFAULT 0 COMMENT '是否包含连接,冗余字段',
-       `ip_address` varchar(100) DEFAULT NULL COMMENT 'IP归属地',
-       `CREATE_DATE` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-       `UPDATE_BY` char(100) DEFAULT NULL COMMENT '更新人',
-       `UPDATE_DATE` datetime DEFAULT NULL COMMENT '更新时间',
+        `ID` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID',
+        `author` char(100) NOT NULL COMMENT '动态作者 ',
+        `content` varchar(300) DEFAULT NULL COMMENT '正文(纯文本)',
+        `status` int(4) NOT NULL DEFAULT 1 COMMENT '状态',
+        `include_images` tinyint(1) DEFAULT 0 COMMENT '是否包含图片,冗余字段',
+        `include_video` tinyint(1) DEFAULT 0 COMMENT '是否包含图片,冗余字段',
+        `include_link` tinyint(1) DEFAULT 0 COMMENT '是否包含连接,冗余字段',
+        `ip` char(30) DEFAULT NULL COMMENT 'IP',
+        `ip_address` varchar(100) DEFAULT NULL COMMENT 'IP归属地',
+        `review_score` int(4) DEFAULT NULL COMMENT '大模型机审内容质量分',
+        `CREATE_DATE` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+        `UPDATE_BY` char(100) DEFAULT NULL COMMENT '更新人',
+        `UPDATE_DATE` datetime DEFAULT NULL COMMENT '更新时间',
        PRIMARY KEY (`ID`),
        KEY `INDEX_AUTHOR_STATUS` (`author`, `status`)
 ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='webchat朋友圈动态核心数据表';

+ 6 - 0
webchat-common/pom.xml

@@ -104,5 +104,11 @@
             <artifactId>core-renderer</artifactId>
             <version>R8</version>
         </dependency>
+        <!-- parse DOM -->
+        <dependency>
+            <groupId>org.jsoup</groupId>
+            <artifactId>jsoup</artifactId>
+            <version>1.13.1</version>
+        </dependency>
     </dependencies>
 </project>

+ 6 - 0
webchat-common/src/main/java/com/webchat/common/constants/MomentConstants.java

@@ -34,4 +34,10 @@ public class MomentConstants {
         private String typeName;
     }
 
+    public static MomentStatusEnum getStatusByReviewScore(Integer score) {
+        if (score == null) {
+            return null;
+        }
+        return score > 0 ? MomentStatusEnum.PUBLISHED : MomentStatusEnum.REJECT;
+    }
 }

+ 6 - 1
webchat-common/src/main/java/com/webchat/common/enums/LocalCacheKey.java

@@ -29,7 +29,12 @@ public enum LocalCacheKey {
     /***
      * 用户自定义导航
      */
-    USER_NAV;
+    USER_NAV,
+
+    /**
+     * IP归属地解析本地缓存
+     */
+    IP_LOCATION;
 
     public String getKey() {
         return this.name();

+ 76 - 0
webchat-common/src/main/java/com/webchat/common/util/IPAddressUtil.java

@@ -0,0 +1,76 @@
+package com.webchat.common.util;
+
+import com.alibaba.fastjson.JSONObject;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.webchat.common.enums.LocalCacheKey;
+import com.webchat.domain.vo.response.IpLocationResponseVO;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @Author: 程序员七七
+ * @Date: 2.5.22 3:59 下午
+ */
+@Slf4j
+public class IPAddressUtil {
+
+    /**
+     * IP 归属地解析API(免费)
+     *
+     */
+    private static final String IP_ADDRESS_API = "https://whois.pconline.com.cn/ipJson.jsp?ip=%s&json=true";
+
+    private static final Cache<String, IpLocationResponseVO> IP_LOCAL_CACHE;
+
+    static {
+        IP_LOCAL_CACHE = CacheBuilder.newBuilder()
+                .softValues()
+                .maximumSize(500L)
+                .expireAfterWrite(600L, TimeUnit.SECONDS)
+                .build();
+    }
+
+    public static IpLocationResponseVO location(String ip) {
+
+        if (StringUtils.isBlank(ip)) {
+            return null;
+        }
+
+        String localCacheKey = LocalCacheKey.IP_LOCATION.name().concat("_").concat(ip);
+        IpLocationResponseVO localCacheVal = IP_LOCAL_CACHE.getIfPresent(localCacheKey);
+        if (localCacheVal != null) {
+            log.info("{} 规则归属地解析成功 - FROM LOCAL CACHE:{}", ip, JsonUtil.toJsonString(localCacheVal));
+            return localCacheVal;
+        }
+        // 通过API查询
+        IpLocationResponseVO ipLocationResponseVO = locationFromApi(ip);
+        // 加入本地缓存
+        IP_LOCAL_CACHE.put(localCacheKey, ipLocationResponseVO);
+        log.info("{} 规则归属地解析成功 - FROM API:{}", ip, JsonUtil.toJsonString(ipLocationResponseVO));
+        return ipLocationResponseVO;
+    }
+
+    private static IpLocationResponseVO locationFromApi(String ip) {
+        IpLocationResponseVO ipLocationResponseVO = IpLocationResponseVO.builder().build();
+        String url = String.format(IP_ADDRESS_API, ip);
+        try {
+            String response = HttpClientUtil.getObjectFromUrl(url, String.class);
+            if (StringUtils.isBlank(response)) {
+                return ipLocationResponseVO;
+            }
+            JSONObject responseJson = JSONObject.parseObject(response);
+            return IpLocationResponseVO.builder()
+                    .country(responseJson.getString("country"))
+                    .province(responseJson.getString("pro"))
+                    .city(responseJson.getString("city"))
+                    .district(responseJson.getString("region"))
+                    .build();
+        } catch (Exception e) {
+            log.error("IP LOCATION analysis error. ip:{}", ip, e);
+        }
+        return ipLocationResponseVO;
+    }
+}

+ 164 - 0
webchat-common/src/main/java/com/webchat/common/util/web/UrlAnalysisUtil.java

@@ -0,0 +1,164 @@
+package com.webchat.common.util.web;
+
+import com.google.common.collect.Sets;
+import com.webchat.domain.dto.UrlAnalysisResultDTO;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.jsoup.Connection;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Element;
+import org.jsoup.select.Elements;
+
+
+import java.util.HashSet;
+import java.util.Set;
+
+
+@Slf4j
+public class UrlAnalysisUtil {
+
+
+    /**
+     * 网址解析
+     *
+     * @param url
+     * @return
+     */
+    public static UrlAnalysisResultDTO analyze(String url) {
+        UrlAnalysisResultDTO result = new UrlAnalysisResultDTO();
+        try {
+            Connection conn = Jsoup.connect(url);
+            // 解析ICON
+            result.setIcon(analysisIcon(conn));
+            // 解析标题
+            result.setTitle(analysisTitle(conn));
+            // 解析关键词
+            result.setKeywords(analysisKeywords(conn));
+            // 解析概述
+            result.setDescription(analysisDescription(conn));
+            // 解析首图
+            result.setFirstImage(analysisFirstImage(conn));
+        } catch (Exception e) {
+            log.error("URL ANALYSIS ERROR. url:{}", url, e);
+        }
+        return result;
+    }
+
+    /***
+     * 解析标题
+     * @param conn
+     * @return
+     */
+    private static String analysisTitle(Connection conn) {
+        if (conn == null) {
+            return "";
+        }
+        try {
+            return conn.get().title();
+        } catch (Exception e) {
+            return "";
+        }
+    }
+
+    /***
+     * 解析概述
+     * @param conn
+     * @return
+     */
+    private static String analysisDescription(Connection conn) {
+        if (conn == null) {
+            return "";
+        }
+        try {
+            Elements elements = conn.get().getElementsByTag("meta");
+            for (Element element : elements) {
+                if (element.attr("name").contains("description") || element.attr("property").contains("description")) {
+                    return element.attr("content");
+                }
+            }
+        } catch (Exception e) {
+            return "";
+        }
+        return "";
+    }
+
+    /***
+     * 解析ICON
+     * @param conn
+     * @return
+     */
+    private static String analysisIcon(Connection conn) {
+        if (conn == null) {
+            return null;
+        }
+        try {
+            Elements elements = conn.get().getElementsByTag("link");
+            for (Element element : elements) {
+                if (element.attr("rel").contains("icon")) {
+                    if (StringUtils.isBlank(element.attr("href"))) {
+                        continue;
+                    }
+                    return element.attr("abs:href");
+                }
+            }
+        } catch (Exception e) {
+            return null;
+        }
+        return null;
+    }
+
+    /***
+     * 解析关键词
+     * @param conn
+     * @return
+     */
+    private static Set<String> analysisKeywords(Connection conn) {
+        Set<String> keywords = new HashSet<>();
+        if (conn == null) {
+            return keywords;
+        }
+        try {
+            Elements elements = conn.get().getElementsByTag("meta");
+            for (Element element : elements) {
+                if (element.attr("name").contains("keywords")) {
+                    String keywordsStr = element.attr("content");
+                    if (StringUtils.isNotBlank(keywordsStr)) {
+                        return Sets.newHashSet(keywordsStr.split(","));
+                    }
+                }
+            }
+        } catch (Exception e) {
+            return keywords;
+        }
+        return keywords;
+    }
+
+    /***
+     * 解析首图
+     * @param conn
+     * @return
+     */
+    private static String analysisFirstImage(Connection conn) {
+        if (conn == null) {
+            return "";
+        }
+        try {
+            Elements elements = conn.get().getElementsByTag("meta");
+            for (Element element : elements) {
+                if (element.attr("name").contains("image") || element.attr("itemprop").contains("image")) {
+                    return element.attr("abs:content");
+                }
+            }
+            Elements imgElements = conn.get().getElementsByTag("img");
+            for (Element img : imgElements) {
+                if (StringUtils.isBlank(img.attr("src"))) {
+                    continue;
+                }
+                return img.attr("abs:src");
+            }
+        } catch (Exception e) {
+            return "";
+        }
+        return "";
+    }
+}

+ 22 - 0
webchat-domain/src/main/java/com/webchat/domain/dto/UrlAnalysisResultDTO.java

@@ -0,0 +1,22 @@
+package com.webchat.domain.dto;
+
+
+import lombok.Data;
+
+import java.util.Set;
+
+@Data
+public class UrlAnalysisResultDTO {
+
+    private String url;
+
+    private String icon;
+
+    private String firstImage;
+
+    private String title;
+
+    private String description;
+
+    private Set<String> keywords;
+}

+ 33 - 0
webchat-domain/src/main/java/com/webchat/domain/vo/response/IpLocationResponseVO.java

@@ -0,0 +1,33 @@
+package com.webchat.domain.vo.response;
+
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * @Author: 程序员七七
+ * @Date: 2.5.22 3:46 下午
+ */
+@Data
+@Builder
+public class IpLocationResponseVO {
+
+    /***
+     * 国家
+     */
+    private String country;
+
+    /***
+     * 省份
+     */
+    private String province;
+
+    /***
+     * 市
+     */
+    private String city;
+
+    /***
+     * 区
+     */
+    private String district;
+}

+ 2 - 0
webchat-domain/src/main/java/com/webchat/domain/vo/response/moment/MomentLinkVO.java

@@ -5,6 +5,8 @@ import lombok.Data;
 @Data
 public class MomentLinkVO {
 
+    private Long id;
+
     private String resource;
 
     private String title;

+ 2 - 0
webchat-domain/src/main/java/com/webchat/domain/vo/response/moment/MomentMediaVO.java

@@ -5,6 +5,8 @@ import lombok.Data;
 @Data
 public class MomentMediaVO {
 
+    private Long id;
+
     private String resource;
 
     private Integer width;

+ 7 - 0
webchat-domain/src/main/java/com/webchat/domain/vo/response/moment/MomentVO.java

@@ -25,11 +25,18 @@ public class MomentVO {
 
     private MomentLinkVO link;
 
+    private String ip;
+
     private String ipAddress;
 
     private Boolean includeImage;
     private Boolean includeVideo;
     private Boolean includeLink;
 
+    /**
+     * 内容审核质量分
+     */
+    private Integer reviewScore;
+
     private long publishTime;
 }

+ 14 - 1
webchat-ugc/src/main/java/com/webchat/ugc/messaegqueue/service/MomentPublishConsumeService.java

@@ -36,6 +36,16 @@ public class MomentPublishConsumeService {
     @Autowired
     private MomentTimeLineService momentTimeLineService;
 
+
+    /**
+     * 写扩散策略,内容审核质量分超过5,朋友可见,反之仅发帖子人自己可见(降级策略)
+     * ps: 基于score可以实现分段策略,比如小红数作品评分在:
+     * 10 ~ 40 丢初级流量池
+     * 41 ~ 80 丢千人流量池
+     * 81 ~ 100 丢万人流量池
+     */
+    private static final int WRITE_TIME_LINE_SCORE = 5;
+
     /**
      * 动态发布后置处理
      * @param dto
@@ -67,7 +77,10 @@ public class MomentPublishConsumeService {
         /**
          * 写扩散(把当前动态写入到所有粉丝时间线DB、Redis)
          */
-        momentTimeLineService.addTimeLine(dto.getAuthor(), dto.getId(), dto.getPublishTime());
+        Integer reviewScore = dto.getReviewScore();
+        // 大模型内容审核结果质量分
+        boolean writeFriends = reviewScore != null && reviewScore > WRITE_TIME_LINE_SCORE;
+        momentTimeLineService.addTimeLine(dto.getAuthor(), dto.getId(), dto.getPublishTime(), writeFriends);
     }
 
 }

+ 6 - 0
webchat-ugc/src/main/java/com/webchat/ugc/repository/entity/MomentEntity.java

@@ -62,12 +62,18 @@ public class MomentEntity {
     @Column(name = "include_link")
     private boolean includeLink;
 
+    @Column(name = "ip", length = 30)
+    private String ip;
+
     /**
      * IP归属地
      */
     @Column(name = "ip_address", length = 100)
     private String ipAddress;
 
+    @Column(name = "review_score", length = 100)
+    private Integer reviewScore;
+
     @Column(name = "create_date")
     private Date createDate;
 

+ 42 - 5
webchat-ugc/src/main/java/com/webchat/ugc/service/chain/MomentImageHandler.java

@@ -1,10 +1,22 @@
 package com.webchat.ugc.service.chain;
 
+import com.webchat.domain.vo.response.moment.MomentMediaVO;
 import com.webchat.domain.vo.response.moment.MomentVO;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
 import org.springframework.stereotype.Component;
 
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.List;
 
+
+/**
+ * 提取朋友圈图片媒体资源的宽高尺寸等基础信息
+ */
 @Slf4j
 @Component
 public class MomentImageHandler implements MomentPublishHandler {
@@ -12,11 +24,36 @@ public class MomentImageHandler implements MomentPublishHandler {
 
     @Override
     public void handle(MomentVO moment, MomentPublishHandlerChain chain) {
-
-
-
-
-
+        if (!ObjectUtils.equals(moment.getIncludeImage(), true)) {
+            chain.handle(moment, chain);
+            return;
+        }
+        List<MomentMediaVO> images = moment.getImages();
+        for (MomentMediaVO image : images) {
+            this.doHandleImage(image);
+        }
         chain.handle(moment, chain);
     }
+
+    /**
+     * 提取网络图片资源宽高、大小等信息
+     *
+     * @param imageVO
+     */
+    private void doHandleImage(MomentMediaVO imageVO) {
+        try {
+            URL url = new URL(imageVO.getResource());
+            BufferedImage image = ImageIO.read(url.openStream());
+            if (image != null) {
+                imageVO.setWidth(image.getWidth());
+                imageVO.setHeight(image.getHeight());
+            }
+            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+            conn.setRequestMethod("HEAD");
+            long size = conn.getContentLengthLong();
+            imageVO.setSize(size);
+        } catch (Exception e) {
+            log.error("doHandleImage error", e);
+        }
+    }
 }

+ 6 - 4
webchat-ugc/src/main/java/com/webchat/ugc/service/chain/MomentIpAddressHandler.java

@@ -1,5 +1,7 @@
 package com.webchat.ugc.service.chain;
 
+import com.webchat.common.util.IPAddressUtil;
+import com.webchat.domain.vo.response.IpLocationResponseVO;
 import com.webchat.domain.vo.response.moment.MomentVO;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
@@ -13,10 +15,10 @@ public class MomentIpAddressHandler implements MomentPublishHandler {
     @Override
     public void handle(MomentVO moment, MomentPublishHandlerChain chain) {
 
-
-        
-
-
+        IpLocationResponseVO address = IPAddressUtil.location(moment.getIp());
+        if (address != null) {
+            moment.setIpAddress(address.getProvince());
+        }
         chain.handle(moment, chain);
     }
 }

+ 30 - 4
webchat-ugc/src/main/java/com/webchat/ugc/service/chain/MomentLinkHandler.java

@@ -1,9 +1,18 @@
 package com.webchat.ugc.service.chain;
 
+import com.webchat.common.util.web.UrlAnalysisUtil;
+import com.webchat.domain.dto.UrlAnalysisResultDTO;
+import com.webchat.domain.vo.response.moment.MomentLinkVO;
 import com.webchat.domain.vo.response.moment.MomentVO;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.stereotype.Component;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
 
 @Slf4j
 @Component
@@ -13,10 +22,27 @@ public class MomentLinkHandler implements MomentPublishHandler {
     @Override
     public void handle(MomentVO moment, MomentPublishHandlerChain chain) {
 
-
-        
-
-
+        if (!ObjectUtils.equals(moment.getIncludeLink(), true)) {
+            chain.handle(moment, chain);
+            return;
+        }
+        this.doUrlAnalysisByJsonp(moment.getLink());
         chain.handle(moment, chain);
     }
+
+    /**
+     * 链接分享同学圈,支持链接解析
+     *
+     * @param link
+     */
+    private void doUrlAnalysisByJsonp( MomentLinkVO link) {
+
+        UrlAnalysisResultDTO result = UrlAnalysisUtil.analyze(link.getResource());
+        if (result == null) {
+            return;
+        }
+        link.setTitle(result.getTitle());
+        link.setCover(StringUtils.isNotBlank(result.getFirstImage()) ?
+                result.getFirstImage() : result.getIcon());
+    }
 }

+ 84 - 3
webchat-ugc/src/main/java/com/webchat/ugc/service/chain/MomentRefreshHandler.java

@@ -1,22 +1,103 @@
 package com.webchat.ugc.service.chain;
 
+import com.webchat.common.constants.MomentConstants;
+import com.webchat.domain.vo.response.moment.MomentLinkVO;
+import com.webchat.domain.vo.response.moment.MomentMediaVO;
 import com.webchat.domain.vo.response.moment.MomentVO;
+import com.webchat.ugc.repository.dao.IMomentDAO;
+import com.webchat.ugc.repository.dao.IMomentLinkDAO;
+import com.webchat.ugc.repository.dao.IMomentMediaDAO;
+import com.webchat.ugc.repository.entity.MomentEntity;
+import com.webchat.ugc.repository.entity.MomentLinkEntity;
+import com.webchat.ugc.repository.entity.MomentMediaEntity;
+import com.webchat.ugc.service.moment.MomentLinkService;
+import com.webchat.ugc.service.moment.MomentMediaService;
+import com.webchat.ugc.service.moment.MomentService;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
 
 @Slf4j
 @Component
 public class MomentRefreshHandler implements MomentPublishHandler {
 
 
+    @Autowired
+    private MomentService momentService;
+    @Autowired
+    private IMomentMediaDAO momentMediaDAO;
+    @Autowired
+    private IMomentLinkDAO momentLinkDAO;
+    @Autowired
+    private IMomentDAO momentDAO;
+
     @Override
     public void handle(MomentVO moment, MomentPublishHandlerChain chain) {
+        /**
+         * 1. 持久化新生成动态数据
+         */
+        this.doSaveMomentData(moment);
+        /**
+         * 2. 重新刷新动态缓存
+         */
+        momentService.refreshMomentCache(moment.getId());
+        chain.handle(moment, chain);
+    }
 
 
-        
-
+    /**
+     * 持久化消息队列消费中获取到的朋友圈动态扩展字段信息
+     *
+     * @param moment
+     */
+    private void doSaveMomentData(MomentVO moment) {
 
-        chain.handle(moment, chain);
+        Long momentId = moment.getId();
+        MomentEntity momentEntity = momentDAO.findById(momentId).orElse(null);
+        if (momentEntity == null) {
+            return;
+        }
+        // 刷主表数据
+        momentEntity.setIpAddress(moment.getIpAddress());
+        momentEntity.setReviewScore(moment.getReviewScore());
+        momentEntity.setStatus(MomentConstants.getStatusByReviewScore(moment.getReviewScore()).getStatus());
+        momentDAO.save(momentEntity);
+        // 刷新媒体资源数据
+        if (ObjectUtils.equals(moment.getIncludeImage(), true)) {
+            Set<Long> resourceIds = moment.getImages().stream().map(MomentMediaVO::getId).collect(Collectors.toSet());
+            List<MomentMediaEntity> images = momentMediaDAO.findAllById(resourceIds);
+            Map<Long, MomentMediaVO> mediaVOMap = moment.getImages().stream().collect(Collectors.toMap(MomentMediaVO::getId, Function.identity()));
+            images.forEach(m -> {
+                MomentMediaVO momentMediaVo = mediaVOMap.get(m.getId());
+                m.setWidth(momentMediaVo.getWidth());
+                m.setHeight(momentMediaVo.getHeight());
+                m.setSize(momentMediaVo.getSize());
+            });
+            momentMediaDAO.saveAll(images);
+        }
+        // 刷新媒体资源数据
+        if (ObjectUtils.equals(moment.getIncludeVideo(), true)) {
+            MomentMediaVO videoVo = moment.getVideo();
+            MomentMediaEntity video = momentMediaDAO.findById(videoVo.getId()).orElse(null);
+            video.setWidth(videoVo.getWidth());
+            video.setHeight(videoVo.getHeight());
+            video.setSize(videoVo.getSize());
+            momentMediaDAO.save(video);
+        }
+        if (ObjectUtils.equals(moment.getIncludeLink(), true)) {
+            MomentLinkVO linkVo = moment.getLink();
+            MomentLinkEntity momentLink = momentLinkDAO.findById(linkVo.getId()).orElse(null);
+            momentLink.setTitle(linkVo.getTitle());
+            momentLink.setCover(linkVo.getCover());
+            momentLinkDAO.save(momentLink);
+        }
     }
 }

+ 4 - 4
webchat-ugc/src/main/java/com/webchat/ugc/service/chain/MomentReviewHandler.java

@@ -34,6 +34,7 @@ public class MomentReviewHandler implements MomentPublishHandler {
         // 这里我们仅支持对正文内容的审核
         String content = moment.getContent();
         if (StringUtils.isBlank(content)) {
+            chain.handle(moment, chain);
             return;
         }
 
@@ -47,6 +48,7 @@ public class MomentReviewHandler implements MomentPublishHandler {
         } catch (Exception e) {
             log.error("[朋友圈内容审核异常] ===> prompt模版引擎模版渲染失败!vars:{}",
                     JsonUtil.toJsonString(vars), e);
+            chain.handle(moment, chain);
             return;
         }
         ChatCompletionMessageRequest chatCompletionMessage = new ChatCompletionMessageRequest(prompt);
@@ -57,15 +59,13 @@ public class MomentReviewHandler implements MomentPublishHandler {
             reviewScore = StringUtils.isNoneBlank(response) ? Integer.valueOf(response) : null;
         } else {
             log.error("[朋友圈内容审核异常] ===> LLM Chat Error!prompt:{}", prompt);
+            chain.handle(moment, chain);
             return;
         }
 
         log.info("[朋友圈内容审核结果] ===> prompt:{}, 内容质量分:{}", prompt, reviewScore);
 
-        if (reviewScore != null && reviewScore < 10) {
-            // 异常内容
-
-        }
+        moment.setReviewScore(reviewScore);
         chain.handle(moment, chain);
     }
 }

+ 7 - 1
webchat-ugc/src/main/java/com/webchat/ugc/service/chain/MomentVideoHandler.java

@@ -2,6 +2,7 @@ package com.webchat.ugc.service.chain;
 
 import com.webchat.domain.vo.response.moment.MomentVO;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
 import org.springframework.stereotype.Component;
 
 
@@ -13,8 +14,13 @@ public class MomentVideoHandler implements MomentPublishHandler {
     @Override
     public void handle(MomentVO moment, MomentPublishHandlerChain chain) {
 
+        if (!ObjectUtils.equals(moment.getIncludeVideo(), true)) {
+            chain.handle(moment, chain);
+            return;
+        }
+
+
 
-        
 
 
         chain.handle(moment, chain);

+ 2 - 1
webchat-ugc/src/main/java/com/webchat/ugc/service/moment/MomentService.java

@@ -92,7 +92,7 @@ public class MomentService {
         MomentPublishMessageDTO messageDTO = new MomentPublishMessageDTO();
         messageDTO.setMoment(momentVO);
         messageQueueProducer.send(MessageQueueEnum.QUEUE_MOMENT_PUBLISH, messageDTO);
-        return null;
+        return moment.getId();
     }
 
 
@@ -237,6 +237,7 @@ public class MomentService {
             moment = new MomentEntity();
             moment.setStatus(MomentConstants.MomentStatusEnum.NEW.getStatus());
             moment.setAuthor(momentSaveOrUpdate.getAuthor());
+            moment.setIp(momentSaveOrUpdate.getClientIp());
             moment.setCreateDate(new Date());
         }
         moment.setContent(momentSaveOrUpdate.getContent());

+ 7 - 3
webchat-ugc/src/main/java/com/webchat/ugc/service/moment/MomentTimeLineService.java

@@ -11,6 +11,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import java.util.Date;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.stream.Collectors;
@@ -35,9 +36,12 @@ public class MomentTimeLineService {
      * @param momentId
      * @param time
      */
-    public void addTimeLine(String author, Long momentId, Long time) {
-
-        Set<String> friends = accountService.getAllSubscriberByAccount(author, AccountRelationTypeEnum.USER_USER);
+    public void addTimeLine(String author, Long momentId, Long time, boolean writeFriends) {
+        Set<String> friends = new HashSet<>();
+        friends.add(author);
+        if (writeFriends) {
+            friends = accountService.getAllSubscriberByAccount(author, AccountRelationTypeEnum.USER_USER);
+        }
         if (CollectionUtils.isEmpty(friends)) {
             return;
         }