35 次代碼提交 564a22364b ... 99bbc9d21a

作者 SHA1 備註 提交日期
  刘立冬 99bbc9d21a 进群如果在写死黑名单里 立即踢出 1 月之前
  刘立冬 a78495b234 去掉是否在群里的强校验,目前发现有些用户触发企微bug,无法手动再拉黑,群列表找不到 1 月之前
  刘立冬 bcf3b2ff39 调整异常用户自动关闭功能比例为20% 3 月之前
  刘立冬 5c9a75d7c2 调整异常用户自动关闭功能比例为30%,用于测试炸群流量 3 月之前
  刘立冬 488ff24e45 调整异常用户自动关闭功能比例 3 月之前
  刘立冬 6ad30ef8d9 补充日志 3 月之前
  刘立冬 23499fd2ba 增加英文名称匹配 3 月之前
  刘立冬 fef6de685c 成员名称匹配增加nickname 3 月之前
  刘立冬 e6559a898e 传递MDC 3 月之前
  刘立冬 ed97e2ea36 修改MDC导入包 3 月之前
  刘立冬 6b7de62a48 日志增加logTraceId 3 月之前
  刘立冬 274c77e486 去除一些日志,跳转参数为正式参数 3 月之前
  刘立冬 9fa002534e 变更字段属性 3 月之前
  刘立冬 6195d73781 补充调试信息 3 月之前
  刘立冬 49092663e1 build error fixed 3 月之前
  刘立冬 a237aed271 变更数据库字段 3 月之前
  刘立冬 0348dec2c6 修改错误文案,添加调试日志 3 月之前
  刘立冬 9b28e52768 调整测试的生效阈值参数 3 月之前
  刘立冬 f023434b4d 调整测试的生效阈值参数 3 月之前
  刘立冬 fa6b1146e1 调整测试的生效阈值,便于测试 3 月之前
  刘立冬 148d0bf13c 增加调试信息 3 月之前
  刘立冬 61459035ec 成员去重,多次加入会有多条,只取最新时间的一条 3 月之前
  刘立冬 5ffd05c2aa 增加调试信息 3 月之前
  刘立冬 01cfb01fc0 UTC+8 3 月之前
  刘立冬 7ed514c96c 时间对比,与数据库偏差问题排查 3 月之前
  刘立冬 ec02893d84 错误参数调整 3 月之前
  刘立冬 785231c22e 错误参数调整 3 月之前
  刘立冬 75098a5bfe 还原变更 3 月之前
  刘立冬 7567077bdb uuid同样也更新 3 月之前
  刘立冬 a22b084868 处理空返回值 3 月之前
  刘立冬 bb2dee23d9 返回异常信息给前端 3 月之前
  刘立冬 110f56a81e 变更数据库表,新增是否显示异常信息和具体提示信息内容 3 月之前
  刘立冬 5bed393903 踢人调用更改 3 月之前
  刘立冬 e9549f5c1d 判断群开关打开时间,内置的防误踢规则从开启开关时生效 3 月之前
  刘立冬 1dbde13706 增加固定规则逻辑,阶段提交 3 月之前
共有 21 個文件被更改,包括 787 次插入127 次删除
  1. 32 20
      risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/aop/RequestLogAspect.java
  2. 11 11
      risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/dao/mapper/WorkWechatRoomInfoMapper.java
  3. 3 0
      risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/model/dto/WorkWechatRoomDTO.java
  4. 97 25
      risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/model/po/WorkWechatRoomInfo.java
  5. 150 20
      risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/model/po/WorkWechatRoomInfoExample.java
  6. 64 0
      risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/model/qywx/RoomAddMemberMessage.java
  7. 42 0
      risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/model/qywx/UserListFromAdPlatformModel.java
  8. 3 1
      risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/service/LoghubService.java
  9. 15 0
      risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/service/impl/LoghubServiceImpl.java
  10. 1 2
      risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/service/impl/QwLoginServiceImpl.java
  11. 2 0
      risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/service/impl/QywxUserDataService.java
  12. 146 5
      risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/service/impl/RiskRuleConfigService.java
  13. 44 6
      risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/service/impl/RiskUserHandleService.java
  14. 66 7
      risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/service/impl/RiskUserOperateService.java
  15. 26 0
      risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/service/impl/WorkWechatServiceImpl.java
  16. 5 1
      risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/service/qywx/Constant.java
  17. 52 20
      risk-control-core/src/main/resources/mapper/WorkWechatRoomInfoMapper.xml
  18. 9 0
      risk-control-server/src/main/java/com/tzld/piaoquan/risk/control/controller/QwCallbackController.java
  19. 7 0
      risk-control-server/src/main/java/com/tzld/piaoquan/risk/control/controller/ReceiveRiskInfoController.java
  20. 2 4
      risk-control-server/src/main/resources/application.yml
  21. 10 5
      risk-control-server/src/test/java/com/tzld/piaoquan/risk/control/service/DemoServiceTest.java

+ 32 - 20
risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/aop/RequestLogAspect.java

@@ -2,11 +2,13 @@ package com.tzld.piaoquan.risk.control.aop;
 
 import com.alibaba.fastjson.JSON;
 import com.google.common.base.Stopwatch;
+import com.tzld.piaoquan.risk.control.common.base.Constant;
 import com.tzld.piaoquan.risk.control.service.LoghubService;
 import org.aspectj.lang.ProceedingJoinPoint;
 import org.aspectj.lang.annotation.Around;
 import org.aspectj.lang.annotation.Aspect;
 import org.aspectj.lang.annotation.Pointcut;
+import org.slf4j.MDC;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Component;
@@ -17,6 +19,7 @@ import javax.servlet.http.HttpServletRequest;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
+import java.util.UUID;
 import java.util.concurrent.TimeUnit;
 
 @Aspect
@@ -38,25 +41,34 @@ public class RequestLogAspect {
     @Around("allController()")
     public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
         // 获取请求信息
-        HttpServletRequest request = Objects.requireNonNull((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
-        String uri = request.getRequestURI();
-        String clientIp = request.getRemoteAddr();
-        String queryString = request.getQueryString();
-        String method = joinPoint.getSignature().getName();
-        Stopwatch stopwatch = Stopwatch.createStarted();
-        // 执行目标方法
-        Object result = joinPoint.proceed();
-        // 计算耗时
-        long executionTime = stopwatch.stop().elapsed(TimeUnit.MILLISECONDS);
-        Map<String, Object> data = new HashMap<>();
-        data.put("uri", uri);
-        data.put("clientIp", clientIp);
-        data.put("method", method);
-        data.put("executionTime", executionTime);
-        data.put("queryString", queryString);
-        data.put("requestBody", JSON.toJSONString(joinPoint.getArgs()));
-        data.put("responseBody", JSON.toJSONString(result));
-        loghubService.asyncSubmitLog(aliyunLogProject, aliyunLogLogstoreRequest, "", data);
-        return result;
+        String traceId = UUID.randomUUID().toString();
+        MDC.put(Constant.LOG_TRACE_ID, traceId);  // 关键:让所有日志自动携带 traceId
+        try {
+            HttpServletRequest request = Objects.requireNonNull((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+            String uri = request.getRequestURI();
+            String clientIp = request.getRemoteAddr();
+            String queryString = request.getQueryString();
+            String method = joinPoint.getSignature().getName();
+            Stopwatch stopwatch = Stopwatch.createStarted();
+            // 执行目标方法
+            Object result = joinPoint.proceed();
+            // 计算耗时
+            long executionTime = stopwatch.stop().elapsed(TimeUnit.MILLISECONDS);
+            Map<String, Object> data = new HashMap<>();
+            data.put("uri", uri);
+            data.put("clientIp", clientIp);
+            data.put("method", method);
+            data.put("executionTime", executionTime);
+            data.put("queryString", queryString);
+            data.put("logTraceId", traceId);
+            data.put("requestBody", JSON.toJSONString(joinPoint.getArgs()));
+            data.put("responseBody", JSON.toJSONString(result));
+            Map<String, String> mdcContext = MDC.getCopyOfContextMap();
+            loghubService.asyncSubmitLog(aliyunLogProject, aliyunLogLogstoreRequest, "", data, mdcContext);
+            return result;
+        } finally {
+            // 确保在方法执行完毕后清除 traceId
+            MDC.clear();
+        }
     }
 }

+ 11 - 11
risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/dao/mapper/WorkWechatRoomInfoMapper.java

@@ -10,7 +10,7 @@ public interface WorkWechatRoomInfoMapper {
      * This method was generated by MyBatis Generator.
      * This method corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     long countByExample(WorkWechatRoomInfoExample example);
 
@@ -18,7 +18,7 @@ public interface WorkWechatRoomInfoMapper {
      * This method was generated by MyBatis Generator.
      * This method corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     int deleteByExample(WorkWechatRoomInfoExample example);
 
@@ -26,7 +26,7 @@ public interface WorkWechatRoomInfoMapper {
      * This method was generated by MyBatis Generator.
      * This method corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     int deleteByPrimaryKey(Long id);
 
@@ -34,7 +34,7 @@ public interface WorkWechatRoomInfoMapper {
      * This method was generated by MyBatis Generator.
      * This method corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     int insert(WorkWechatRoomInfo record);
 
@@ -42,7 +42,7 @@ public interface WorkWechatRoomInfoMapper {
      * This method was generated by MyBatis Generator.
      * This method corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     int insertSelective(WorkWechatRoomInfo record);
 
@@ -50,7 +50,7 @@ public interface WorkWechatRoomInfoMapper {
      * This method was generated by MyBatis Generator.
      * This method corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     List<WorkWechatRoomInfo> selectByExample(WorkWechatRoomInfoExample example);
 
@@ -58,7 +58,7 @@ public interface WorkWechatRoomInfoMapper {
      * This method was generated by MyBatis Generator.
      * This method corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     WorkWechatRoomInfo selectByPrimaryKey(Long id);
 
@@ -66,7 +66,7 @@ public interface WorkWechatRoomInfoMapper {
      * This method was generated by MyBatis Generator.
      * This method corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     int updateByExampleSelective(@Param("record") WorkWechatRoomInfo record, @Param("example") WorkWechatRoomInfoExample example);
 
@@ -74,7 +74,7 @@ public interface WorkWechatRoomInfoMapper {
      * This method was generated by MyBatis Generator.
      * This method corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     int updateByExample(@Param("record") WorkWechatRoomInfo record, @Param("example") WorkWechatRoomInfoExample example);
 
@@ -82,7 +82,7 @@ public interface WorkWechatRoomInfoMapper {
      * This method was generated by MyBatis Generator.
      * This method corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     int updateByPrimaryKeySelective(WorkWechatRoomInfo record);
 
@@ -90,7 +90,7 @@ public interface WorkWechatRoomInfoMapper {
      * This method was generated by MyBatis Generator.
      * This method corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     int updateByPrimaryKey(WorkWechatRoomInfo record);
 }

+ 3 - 0
risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/model/dto/WorkWechatRoomDTO.java

@@ -1,5 +1,6 @@
 package com.tzld.piaoquan.risk.control.model.dto;
 
+import io.swagger.models.auth.In;
 import lombok.Data;
 
 @Data
@@ -12,5 +13,7 @@ public class WorkWechatRoomDTO {
     private String roomName;
 
     private Integer autoRemoveUserSwitch;
+    private Integer showTip;
+    private String tipInfo;
 
 }

+ 97 - 25
risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/model/po/WorkWechatRoomInfo.java

@@ -15,7 +15,7 @@ public class WorkWechatRoomInfo {
      * This field was generated by MyBatis Generator.
      * This field corresponds to the database column work_wechat_room_info.id
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     private Long id;
 
@@ -26,7 +26,7 @@ public class WorkWechatRoomInfo {
      * This field was generated by MyBatis Generator.
      * This field corresponds to the database column work_wechat_room_info.uuid
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     private String uuid;
 
@@ -37,7 +37,7 @@ public class WorkWechatRoomInfo {
      * This field was generated by MyBatis Generator.
      * This field corresponds to the database column work_wechat_room_info.room_id
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     private String roomId;
 
@@ -48,7 +48,7 @@ public class WorkWechatRoomInfo {
      * This field was generated by MyBatis Generator.
      * This field corresponds to the database column work_wechat_room_info.auto_remove_user_switch
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     private Integer autoRemoveUserSwitch;
 
@@ -59,7 +59,7 @@ public class WorkWechatRoomInfo {
      * This field was generated by MyBatis Generator.
      * This field corresponds to the database column work_wechat_room_info.create_user
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     private Long createUser;
 
@@ -70,7 +70,7 @@ public class WorkWechatRoomInfo {
      * This field was generated by MyBatis Generator.
      * This field corresponds to the database column work_wechat_room_info.update_user
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     private Long updateUser;
 
@@ -81,7 +81,7 @@ public class WorkWechatRoomInfo {
      * This field was generated by MyBatis Generator.
      * This field corresponds to the database column work_wechat_room_info.create_time
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     private Date createTime;
 
@@ -92,17 +92,39 @@ public class WorkWechatRoomInfo {
      * This field was generated by MyBatis Generator.
      * This field corresponds to the database column work_wechat_room_info.update_time
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     private Date updateTime;
 
+    /**
+     * Database Column Remarks:
+     *   是否显示提示
+     *
+     * This field was generated by MyBatis Generator.
+     * This field corresponds to the database column work_wechat_room_info.show_tip
+     *
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
+     */
+    private Integer showTip;
+
+    /**
+     * Database Column Remarks:
+     *   提示信息(长文本)
+     *
+     * This field was generated by MyBatis Generator.
+     * This field corresponds to the database column work_wechat_room_info.tip_info
+     *
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
+     */
+    private String tipInfo;
+
     /**
      * This method was generated by MyBatis Generator.
      * This method returns the value of the database column work_wechat_room_info.id
      *
      * @return the value of work_wechat_room_info.id
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     public Long getId() {
         return id;
@@ -114,7 +136,7 @@ public class WorkWechatRoomInfo {
      *
      * @param id the value for work_wechat_room_info.id
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     public void setId(Long id) {
         this.id = id;
@@ -126,7 +148,7 @@ public class WorkWechatRoomInfo {
      *
      * @return the value of work_wechat_room_info.uuid
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     public String getUuid() {
         return uuid;
@@ -138,7 +160,7 @@ public class WorkWechatRoomInfo {
      *
      * @param uuid the value for work_wechat_room_info.uuid
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     public void setUuid(String uuid) {
         this.uuid = uuid;
@@ -150,7 +172,7 @@ public class WorkWechatRoomInfo {
      *
      * @return the value of work_wechat_room_info.room_id
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     public String getRoomId() {
         return roomId;
@@ -162,7 +184,7 @@ public class WorkWechatRoomInfo {
      *
      * @param roomId the value for work_wechat_room_info.room_id
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     public void setRoomId(String roomId) {
         this.roomId = roomId;
@@ -174,7 +196,7 @@ public class WorkWechatRoomInfo {
      *
      * @return the value of work_wechat_room_info.auto_remove_user_switch
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     public Integer getAutoRemoveUserSwitch() {
         return autoRemoveUserSwitch;
@@ -186,7 +208,7 @@ public class WorkWechatRoomInfo {
      *
      * @param autoRemoveUserSwitch the value for work_wechat_room_info.auto_remove_user_switch
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     public void setAutoRemoveUserSwitch(Integer autoRemoveUserSwitch) {
         this.autoRemoveUserSwitch = autoRemoveUserSwitch;
@@ -198,7 +220,7 @@ public class WorkWechatRoomInfo {
      *
      * @return the value of work_wechat_room_info.create_user
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     public Long getCreateUser() {
         return createUser;
@@ -210,7 +232,7 @@ public class WorkWechatRoomInfo {
      *
      * @param createUser the value for work_wechat_room_info.create_user
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     public void setCreateUser(Long createUser) {
         this.createUser = createUser;
@@ -222,7 +244,7 @@ public class WorkWechatRoomInfo {
      *
      * @return the value of work_wechat_room_info.update_user
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     public Long getUpdateUser() {
         return updateUser;
@@ -234,7 +256,7 @@ public class WorkWechatRoomInfo {
      *
      * @param updateUser the value for work_wechat_room_info.update_user
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     public void setUpdateUser(Long updateUser) {
         this.updateUser = updateUser;
@@ -246,7 +268,7 @@ public class WorkWechatRoomInfo {
      *
      * @return the value of work_wechat_room_info.create_time
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     public Date getCreateTime() {
         return createTime;
@@ -258,7 +280,7 @@ public class WorkWechatRoomInfo {
      *
      * @param createTime the value for work_wechat_room_info.create_time
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     public void setCreateTime(Date createTime) {
         this.createTime = createTime;
@@ -270,7 +292,7 @@ public class WorkWechatRoomInfo {
      *
      * @return the value of work_wechat_room_info.update_time
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     public Date getUpdateTime() {
         return updateTime;
@@ -282,17 +304,65 @@ public class WorkWechatRoomInfo {
      *
      * @param updateTime the value for work_wechat_room_info.update_time
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     public void setUpdateTime(Date updateTime) {
         this.updateTime = updateTime;
     }
 
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method returns the value of the database column work_wechat_room_info.show_tip
+     *
+     * @return the value of work_wechat_room_info.show_tip
+     *
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
+     */
+    public Integer getShowTip() {
+        return showTip;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method sets the value of the database column work_wechat_room_info.show_tip
+     *
+     * @param showTip the value for work_wechat_room_info.show_tip
+     *
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
+     */
+    public void setShowTip(Integer showTip) {
+        this.showTip = showTip;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method returns the value of the database column work_wechat_room_info.tip_info
+     *
+     * @return the value of work_wechat_room_info.tip_info
+     *
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
+     */
+    public String getTipInfo() {
+        return tipInfo;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method sets the value of the database column work_wechat_room_info.tip_info
+     *
+     * @param tipInfo the value for work_wechat_room_info.tip_info
+     *
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
+     */
+    public void setTipInfo(String tipInfo) {
+        this.tipInfo = tipInfo;
+    }
+
     /**
      * This method was generated by MyBatis Generator.
      * This method corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     @Override
     public String toString() {
@@ -308,6 +378,8 @@ public class WorkWechatRoomInfo {
         sb.append(", updateUser=").append(updateUser);
         sb.append(", createTime=").append(createTime);
         sb.append(", updateTime=").append(updateTime);
+        sb.append(", showTip=").append(showTip);
+        sb.append(", tipInfo=").append(tipInfo);
         sb.append("]");
         return sb.toString();
     }

+ 150 - 20
risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/model/po/WorkWechatRoomInfoExample.java

@@ -10,7 +10,7 @@ public class WorkWechatRoomInfoExample {
      * This field was generated by MyBatis Generator.
      * This field corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     protected String orderByClause;
 
@@ -18,7 +18,7 @@ public class WorkWechatRoomInfoExample {
      * This field was generated by MyBatis Generator.
      * This field corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     protected boolean distinct;
 
@@ -26,7 +26,7 @@ public class WorkWechatRoomInfoExample {
      * This field was generated by MyBatis Generator.
      * This field corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     protected List<Criteria> oredCriteria;
 
@@ -34,7 +34,7 @@ public class WorkWechatRoomInfoExample {
      * This field was generated by MyBatis Generator.
      * This field corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     protected Page page;
 
@@ -42,7 +42,7 @@ public class WorkWechatRoomInfoExample {
      * This method was generated by MyBatis Generator.
      * This method corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     public WorkWechatRoomInfoExample() {
         oredCriteria = new ArrayList<Criteria>();
@@ -52,7 +52,7 @@ public class WorkWechatRoomInfoExample {
      * This method was generated by MyBatis Generator.
      * This method corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     public void setOrderByClause(String orderByClause) {
         this.orderByClause = orderByClause;
@@ -62,7 +62,7 @@ public class WorkWechatRoomInfoExample {
      * This method was generated by MyBatis Generator.
      * This method corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     public String getOrderByClause() {
         return orderByClause;
@@ -72,7 +72,7 @@ public class WorkWechatRoomInfoExample {
      * This method was generated by MyBatis Generator.
      * This method corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     public void setDistinct(boolean distinct) {
         this.distinct = distinct;
@@ -82,7 +82,7 @@ public class WorkWechatRoomInfoExample {
      * This method was generated by MyBatis Generator.
      * This method corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     public boolean isDistinct() {
         return distinct;
@@ -92,7 +92,7 @@ public class WorkWechatRoomInfoExample {
      * This method was generated by MyBatis Generator.
      * This method corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     public List<Criteria> getOredCriteria() {
         return oredCriteria;
@@ -102,7 +102,7 @@ public class WorkWechatRoomInfoExample {
      * This method was generated by MyBatis Generator.
      * This method corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     public void or(Criteria criteria) {
         oredCriteria.add(criteria);
@@ -112,7 +112,7 @@ public class WorkWechatRoomInfoExample {
      * This method was generated by MyBatis Generator.
      * This method corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     public Criteria or() {
         Criteria criteria = createCriteriaInternal();
@@ -124,7 +124,7 @@ public class WorkWechatRoomInfoExample {
      * This method was generated by MyBatis Generator.
      * This method corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     public Criteria createCriteria() {
         Criteria criteria = createCriteriaInternal();
@@ -138,7 +138,7 @@ public class WorkWechatRoomInfoExample {
      * This method was generated by MyBatis Generator.
      * This method corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     protected Criteria createCriteriaInternal() {
         Criteria criteria = new Criteria();
@@ -149,7 +149,7 @@ public class WorkWechatRoomInfoExample {
      * This method was generated by MyBatis Generator.
      * This method corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     public void clear() {
         oredCriteria.clear();
@@ -161,7 +161,7 @@ public class WorkWechatRoomInfoExample {
      * This method was generated by MyBatis Generator.
      * This method corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     public void setPage(Page page) {
         this.page=page;
@@ -171,7 +171,7 @@ public class WorkWechatRoomInfoExample {
      * This method was generated by MyBatis Generator.
      * This method corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     public Page getPage() {
         return page;
@@ -181,7 +181,7 @@ public class WorkWechatRoomInfoExample {
      * This class was generated by MyBatis Generator.
      * This class corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     protected abstract static class GeneratedCriteria {
         protected List<Criterion> criteria;
@@ -723,13 +723,143 @@ public class WorkWechatRoomInfoExample {
             addCriterion("update_time not between", value1, value2, "updateTime");
             return (Criteria) this;
         }
+
+        public Criteria andShowTipIsNull() {
+            addCriterion("show_tip is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andShowTipIsNotNull() {
+            addCriterion("show_tip is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andShowTipEqualTo(Integer value) {
+            addCriterion("show_tip =", value, "showTip");
+            return (Criteria) this;
+        }
+
+        public Criteria andShowTipNotEqualTo(Integer value) {
+            addCriterion("show_tip <>", value, "showTip");
+            return (Criteria) this;
+        }
+
+        public Criteria andShowTipGreaterThan(Integer value) {
+            addCriterion("show_tip >", value, "showTip");
+            return (Criteria) this;
+        }
+
+        public Criteria andShowTipGreaterThanOrEqualTo(Integer value) {
+            addCriterion("show_tip >=", value, "showTip");
+            return (Criteria) this;
+        }
+
+        public Criteria andShowTipLessThan(Integer value) {
+            addCriterion("show_tip <", value, "showTip");
+            return (Criteria) this;
+        }
+
+        public Criteria andShowTipLessThanOrEqualTo(Integer value) {
+            addCriterion("show_tip <=", value, "showTip");
+            return (Criteria) this;
+        }
+
+        public Criteria andShowTipIn(List<Integer> values) {
+            addCriterion("show_tip in", values, "showTip");
+            return (Criteria) this;
+        }
+
+        public Criteria andShowTipNotIn(List<Integer> values) {
+            addCriterion("show_tip not in", values, "showTip");
+            return (Criteria) this;
+        }
+
+        public Criteria andShowTipBetween(Integer value1, Integer value2) {
+            addCriterion("show_tip between", value1, value2, "showTip");
+            return (Criteria) this;
+        }
+
+        public Criteria andShowTipNotBetween(Integer value1, Integer value2) {
+            addCriterion("show_tip not between", value1, value2, "showTip");
+            return (Criteria) this;
+        }
+
+        public Criteria andTipInfoIsNull() {
+            addCriterion("tip_info is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andTipInfoIsNotNull() {
+            addCriterion("tip_info is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andTipInfoEqualTo(String value) {
+            addCriterion("tip_info =", value, "tipInfo");
+            return (Criteria) this;
+        }
+
+        public Criteria andTipInfoNotEqualTo(String value) {
+            addCriterion("tip_info <>", value, "tipInfo");
+            return (Criteria) this;
+        }
+
+        public Criteria andTipInfoGreaterThan(String value) {
+            addCriterion("tip_info >", value, "tipInfo");
+            return (Criteria) this;
+        }
+
+        public Criteria andTipInfoGreaterThanOrEqualTo(String value) {
+            addCriterion("tip_info >=", value, "tipInfo");
+            return (Criteria) this;
+        }
+
+        public Criteria andTipInfoLessThan(String value) {
+            addCriterion("tip_info <", value, "tipInfo");
+            return (Criteria) this;
+        }
+
+        public Criteria andTipInfoLessThanOrEqualTo(String value) {
+            addCriterion("tip_info <=", value, "tipInfo");
+            return (Criteria) this;
+        }
+
+        public Criteria andTipInfoLike(String value) {
+            addCriterion("tip_info like", value, "tipInfo");
+            return (Criteria) this;
+        }
+
+        public Criteria andTipInfoNotLike(String value) {
+            addCriterion("tip_info not like", value, "tipInfo");
+            return (Criteria) this;
+        }
+
+        public Criteria andTipInfoIn(List<String> values) {
+            addCriterion("tip_info in", values, "tipInfo");
+            return (Criteria) this;
+        }
+
+        public Criteria andTipInfoNotIn(List<String> values) {
+            addCriterion("tip_info not in", values, "tipInfo");
+            return (Criteria) this;
+        }
+
+        public Criteria andTipInfoBetween(String value1, String value2) {
+            addCriterion("tip_info between", value1, value2, "tipInfo");
+            return (Criteria) this;
+        }
+
+        public Criteria andTipInfoNotBetween(String value1, String value2) {
+            addCriterion("tip_info not between", value1, value2, "tipInfo");
+            return (Criteria) this;
+        }
     }
 
     /**
      * This class was generated by MyBatis Generator.
      * This class corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated do_not_delete_during_merge Fri May 23 14:34:49 CST 2025
+     * @mbg.generated do_not_delete_during_merge Wed May 28 17:51:39 CST 2025
      */
     public static class Criteria extends GeneratedCriteria {
 
@@ -742,7 +872,7 @@ public class WorkWechatRoomInfoExample {
      * This class was generated by MyBatis Generator.
      * This class corresponds to the database table work_wechat_room_info
      *
-     * @mbg.generated Fri May 23 14:34:49 CST 2025
+     * @mbg.generated Wed May 28 17:51:39 CST 2025
      */
     public static class Criterion {
         private String condition;

+ 64 - 0
risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/model/qywx/RoomAddMemberMessage.java

@@ -0,0 +1,64 @@
+package com.tzld.piaoquan.risk.control.model.qywx;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import lombok.Data;
+import java.util.List;
+import java.util.Map;
+
+@Data
+public class RoomAddMemberMessage {
+    @JSONField(name = "innerkf_vid")
+    private long innerkVid;
+
+    private long flag;
+
+    private long receiver;
+
+    @JSONField(name = "extraContent")
+    private Map<String, Object> extraContent;
+
+    @JSONField(name = "devInfo")
+    private long devInfo;
+
+    @JSONField(name = "sender_name")
+    private String senderName;
+
+    @JSONField(name = "is_room")
+    private int isRoom;  // 1=true, 0=false
+
+    @JSONField(name = "server_id")
+    private long serverId;
+
+    @JSONField(name = "kf_id")
+    private long kfId;
+
+    private long invitee;
+
+    private boolean issync;
+
+    @JSONField(name = "send_time")
+    private long sendTime;
+
+    private long sender;
+
+    @JSONField(name = "referid")
+    private long referId;
+
+    @JSONField(name = "member_list")
+    private List<Long> memberList;
+
+    @JSONField(name = "app_info")
+    private String appInfo;
+
+    @JSONField(name = "room_conversation_id")
+    private String roomConversationId;
+
+    @JSONField(name = "readuinscount")
+    private int readUinsCount;
+
+    @JSONField(name = "msg_id")
+    private long msgId;
+
+    @JSONField(name = "msgtype")
+    private int msgType;
+}

+ 42 - 0
risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/model/qywx/UserListFromAdPlatformModel.java

@@ -0,0 +1,42 @@
+package com.tzld.piaoquan.risk.control.model.qywx;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class UserListFromAdPlatformModel {
+    @JSONField(name = "code")
+    private Integer code;
+
+    @JSONField(name = "msg")
+    private String msg;
+
+    @JSONField(name = "data")
+    private List<ChatMember> data;
+
+    @JSONField(name = "success")
+    private Boolean success;
+
+    @Data
+    public static class ChatMember {
+        @JSONField(name = "chatName")
+        private String chatName;
+
+        @JSONField(name = "chatId")
+        private String chatId;
+
+        @JSONField(name = "nickName")
+        private String nickName;
+
+        @JSONField(name = "externalId")
+        private String externalId;
+
+        @JSONField(name = "isAbnormal")
+        private Integer isAbnormal;
+
+        @JSONField(name = "joinTime")
+        private Long joinTime;
+    }
+}

+ 3 - 1
risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/service/LoghubService.java

@@ -14,4 +14,6 @@ public interface LoghubService {
      * @param data
      */
     void asyncSubmitLog(String project, String logStore, String topic, Map<String, Object> data);
-}
+    void asyncSubmitLog(String project, String logStore, String topic, Map<String, Object> data,Map<String, String> mdcContext );
+
+    }

+ 15 - 0
risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/service/impl/LoghubServiceImpl.java

@@ -4,6 +4,7 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder;
 import com.tzld.commons.aliyun.log.AliyunLogManager;
 import com.tzld.piaoquan.risk.control.service.LoghubService;
 import lombok.extern.slf4j.Slf4j;
+import org.slf4j.MDC;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
@@ -52,4 +53,18 @@ public class LoghubServiceImpl implements LoghubService {
     public void asyncSubmitLog(String project, String logStore, String topic, Map<String, Object> data) {
         pool.execute(() -> aliyunLogManager.sendLog(project, logStore, topic, data));
     }
+
+    @Override
+    public void asyncSubmitLog(String project, String logStore, String topic, Map<String, Object> data,Map<String, String> mdcContext ) {
+        pool.execute(() -> {
+            if (mdcContext != null) {
+                MDC.setContextMap(mdcContext);  // 恢复 MDC
+            }
+            try {
+                aliyunLogManager.sendLog(project, logStore, topic, data);
+            } finally {
+                MDC.clear();
+            }
+        });
+    }
 }

+ 1 - 2
risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/service/impl/QwLoginServiceImpl.java

@@ -161,8 +161,7 @@ public class QwLoginServiceImpl implements QwLoginService {
         requestBody.put("deverType", "ipad"); // Default device type
         // Convert the request body to JSON string
         String params = JSON.toJSONString(requestBody);
-        System.out.println("initUUID,params: " + params);
-
+        LOGGER.info("initUUID,params: {},userId: {}", params, userId);
         String url = qywxConfig.getDomain() + qywxConfig.getPath("init-uuid");
         Optional<String> response = httpPoolClientDefault.postJson(url, params);
         // 直接解析 JSON

+ 2 - 0
risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/service/impl/QywxUserDataService.java

@@ -257,6 +257,8 @@ public class QywxUserDataService {
     public List<UserBase> getUserByRealName(String name) {
         UserBaseExample example = new UserBaseExample();
         example.createCriteria().andRealnameEqualTo(name);
+        example.or().andNicknameEqualTo(name);
+        example.or().andEnglishNameEqualTo(name);
         List<UserBase> users = userBaseMapper.selectByExample(example);
         return users.isEmpty() ? null : users;
     }

+ 146 - 5
risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/service/impl/RiskRuleConfigService.java

@@ -1,23 +1,45 @@
 package com.tzld.piaoquan.risk.control.service.impl;
 
+import com.alibaba.fastjson.JSON;
+import com.tzld.piaoquan.risk.control.config.QywxConfig;
+import com.tzld.piaoquan.risk.control.dao.mapper.WorkWechatRoomInfoMapper;
+import com.tzld.piaoquan.risk.control.model.po.WorkWechatRoomInfo;
+import com.tzld.piaoquan.risk.control.model.po.WorkWechatRoomInfoExample;
 import com.tzld.piaoquan.risk.control.model.qywx.RoomListResponse;
+import com.tzld.piaoquan.risk.control.model.qywx.UserListFromAdPlatformModel;
+import com.tzld.piaoquan.risk.control.util.HttpClientUtil;
+import com.tzld.piaoquan.risk.control.util.HttpPoolClient;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.util.*;
+import java.util.stream.Collectors;
 
 @Slf4j
 @Service
 public class RiskRuleConfigService {
     @Autowired
     private WorkWechatServiceImpl workWechatServiceImpl;
+    private static final int USER_COUNT_THRESHOLD = 5; //生效人数阈值
+    private static final int USER_COUNT_THRESHOLD_FOR_ANOMALY_USER_RATIO = 30; //大于30人才触发是否关闭判断
+    private static final float ANOMALY_USER_RATIO = 0.2f; //异常用户比例20%
+    @Autowired
+    private WorkWechatRoomInfoMapper mapper;
+
+    @Autowired
+    private QywxConfig config;
+    @Value("${qywx.corpid}")
+    private long corpid;
+    private static final HttpPoolClient httpPoolClientDefault = HttpClientUtil.create(10000, 10000, 2000, 5000, 5, 10000);
+    @Value("${getUserList.url:https://api.piaoquantv.com/ad/platform/wechat/group/groupUserDetailData}")
+    private String getUserList;
     Map<String, List<RoomListResponse.RoomInfo>> getKickOpenedRoom(Map<String, List<RoomListResponse.RoomInfo>> tobeOperate) {
         Map<String, List<RoomListResponse.RoomInfo>> filteredMap = new HashMap<>();
-
         for (Map.Entry<String, List<RoomListResponse.RoomInfo>> entry : tobeOperate.entrySet()) {
             List<RoomListResponse.RoomInfo> filteredRooms = new ArrayList<>();
 
@@ -35,4 +57,123 @@ public class RiskRuleConfigService {
         return filteredMap;
     }
 
+
+
+    //防止误踢,只有满足存在五个正常群人数才会生效
+    public FixedRuleResult getFixedRulesForThisChat(String chatId, String roomId) {
+        FixedRuleResult ruleResult = new FixedRuleResult();
+        WorkWechatRoomInfoExample example = new WorkWechatRoomInfoExample();
+        example.createCriteria().andRoomIdEqualTo(roomId);
+        List<WorkWechatRoomInfo> examples = mapper.selectByExample(example);
+        if (examples == null || examples.isEmpty()) {
+            log.error("getFixedRulesForThisChat, no room found for roomId: {}", roomId);
+            return ruleResult;
+        }
+        WorkWechatRoomInfo roomInfo = examples.get(0);
+        long roomUpdateTime =  roomInfo.getUpdateTime().getTime()/1000;
+        log.info("getFixedRulesForThisChat, roomId: {}, roomUpdateTime: {}", roomId, roomUpdateTime);
+        List<UserListFromAdPlatformModel.ChatMember> memberList = getUserListFromAdPlatform(chatId);
+        List<UserListFromAdPlatformModel.ChatMember> distinctMembers = memberList.stream()
+                .collect(Collectors.toMap(
+                        UserListFromAdPlatformModel.ChatMember::getExternalId,  // key: externalId
+                        member -> member,                                       // value: ChatMember 对象
+                        (existing, replacement) ->                              // 合并策略:取 joinTime 更大的那条
+                                existing.getJoinTime() > replacement.getJoinTime() ? existing : replacement
+                ))
+                .values()  // 获取去重后的成员列表
+                .stream()
+                .collect(Collectors.toList());
+        if (distinctMembers == null || distinctMembers.isEmpty()) {
+            log.error("checkIfOpenedForFixedRules, memberList is empty for chatId: {}", chatId);
+            return ruleResult;
+        } else {
+            // 打印所有成员的joinTime和roomUpdateTime用于调试
+            distinctMembers.forEach(member -> {
+                if (member.getJoinTime() != null) {
+                    log.info(
+                            "Member ID: " + member.getNickName() +
+                                    ", joinTime: " + member.getJoinTime() +
+                                    ", roomUpdateTime: " + roomUpdateTime +
+                                    ", isAfter: " + (member.getJoinTime() > roomUpdateTime)
+                    );
+                } else {
+                    System.out.println("Member ID: " + member.getNickName() + ", joinTime: null");
+                }
+            });
+            //开关开启后的加入的数量
+            int size = (int) distinctMembers.stream()
+                    .filter(member -> member.getJoinTime() != null && member.getJoinTime() > roomUpdateTime)
+                    .count();;//群总数,包括异常和正常
+            //memberList中isAbnormal是0代表正常用户,判断joinTime是否大于updateTime
+            int normalSize = (int) distinctMembers.stream()
+                    .filter(member -> member.getIsAbnormal() == 0).filter(
+                            member -> member.getJoinTime() != null && member.getJoinTime() > roomUpdateTime)
+                    .count();
+            ruleResult.normalCount = normalSize;
+            ruleResult.abnormalCount = size - normalSize;
+            log.info("checkIfOpenedForFixedRules, chatId: {}, size: {}, normalSize: {},total size:{}", chatId, size, normalSize);
+
+            if(normalSize >= USER_COUNT_THRESHOLD) {//大于等于3才生效
+                log.info("checkIfOpenedForFixedRules, size: {}, normalSize: {}", size, normalSize);
+                //如果群成员大于30人,才判断异常用户比例
+                ruleResult.isNormalCountEnough = true;
+                if (size >= USER_COUNT_THRESHOLD_FOR_ANOMALY_USER_RATIO) {
+                    //计算异常用户比例
+                    float anomalyUserRatio = (float) (size - normalSize) / size;
+                    log.info("checkIfOpenedForFixedRules, chatId: {}, anomalyUserRatio: {}", chatId, anomalyUserRatio);
+                    if (anomalyUserRatio >= ANOMALY_USER_RATIO) {
+                        log.info("checkIfOpenedForFixedRules, chatId: {}, anomalyUserRatio is high enough, opening rules", chatId);
+                        ruleResult.isAbnormalRatioHighEnough = true;
+                    } else {
+                        log.info("checkIfOpenedForFixedRules, chatId: {}, anomalyUserRatio is not high enough, not opening rules", chatId);
+                    }
+                }
+                return ruleResult;
+            } else {
+                log.info("checkIfOpenedForFixedRules, chatId: {}, size: {}, normalSize: {}, not enough normal users", chatId, size, normalSize);
+                return ruleResult;
+            }
+        }
+    }
+
+
+
+    private List<UserListFromAdPlatformModel.ChatMember> getUserListFromAdPlatform(String chatId) {
+        String url = String.format("%s?chatId=%s", getUserList, chatId);
+        List<UserListFromAdPlatformModel.ChatMember> result = Collections.emptyList();
+        log.info("getUserListFromAdPlatform, url: {}", url);
+        Optional<String> response = httpPoolClientDefault.get(url);
+        if (!response.isPresent()) {
+            log.error("getUserListFromAdPlatform, response is empty for chatId: {}", chatId);
+        } else {
+            log.info("getUserListFromAdPlatform, response: {}", response.get());
+            UserListFromAdPlatformModel model =  JSON.parseObject(response.get(), UserListFromAdPlatformModel.class);
+            if (model != null && model.getData() != null) {
+                result = model.getData();
+            } else {
+                log.error("getUserListFromAdPlatform, model is null or data is empty for chatId: {}", chatId);
+                return new ArrayList<>();
+            }
+        }
+        return result;
+    }
+
+    public static class FixedRuleResult {
+        boolean isNormalCountEnough = false;
+        int normalCount = 0;
+        boolean isAbnormalRatioHighEnough = false;
+        float abnormalUserRatio = 0.0f;
+        int abnormalCount = 0;
+
+        @Override
+        public String toString() {
+            return "FixedRuleResult{" +
+                    "isNormalCountEnough=" + isNormalCountEnough +
+                    ", normalCount=" + normalCount +
+                    ", isAbnormalRatioHighEnough=" + isAbnormalRatioHighEnough +
+                    ", abnormalUserRatio=" + abnormalUserRatio +
+                    ", abnormalCount=" + abnormalCount +
+                    '}';
+        }
+    }
 }

+ 44 - 6
risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/service/impl/RiskUserHandleService.java

@@ -45,9 +45,7 @@ public class RiskUserHandleService {
         Map<String, List<RoomListResponse.RoomInfo>> toBeOperate = matchUserAndRoom(staffList, riskUserInfo);
         if (toBeOperate.isEmpty()) return 0;
         log.info("handleRiskUser, toBeOperate: {}", toBeOperate);
-        //TODO:检查过滤规则是否配置
         toBeOperate = riskRuleConfigService.getKickOpenedRoom(toBeOperate);
-
         //找到待踢的人,找到终止
         for (Map.Entry<String, List<RoomListResponse.RoomInfo>> entry : toBeOperate.entrySet()) {
             String staffVid = entry.getKey(); // Key: staff vid内部员工
@@ -59,12 +57,31 @@ public class RiskUserHandleService {
                 log.info("handleRiskUser, externalId2Vid error, staff: {}, riskUserInfo: {}", staff, riskUserInfo);
                 continue;
             }
+            //这里的room都是已经打开的
             for (RoomListResponse.RoomInfo roomInfo : roomInfoList) {
-                boolean success = riskUserOperateService.checkAndKickExternalUser(staff, riskUserInfo, externalVid,Long.parseLong(roomInfo.getRoomId()));
-                if (success) {
-                    log.info("handleRiskUser to be kick user, vid: {},name: {}", externalVid,riskUserInfo.getExternalNickname());
-                    return 1;
+                if (riskUserInfo.getJoinScene() == 3){//risk用户在这个群
+                    //固定规则校验,chatId转化到roomId
+                    long roomId = chatIdToRoomId(staff.getUuid(), riskUserInfo.getChatId());
+                    if ((roomId + "").equals(roomInfo.getRoomId())) {//匹配群
+                        log.info("handleRiskUser, chatIdToRoomId , staff: {}, riskUserInfo: {}", staff, riskUserInfo);
+                        RiskRuleConfigService.FixedRuleResult ruleResult = riskRuleConfigService.getFixedRulesForThisChat(riskUserInfo.getChatId(),roomId +"");
+                        log.info("handleRiskUser, ruleResult: {}, roomInfo: {}", ruleResult, roomInfo);
+                        if(ruleResult.isNormalCountEnough && !ruleResult.isAbnormalRatioHighEnough) {//正常进入用户满足需求,且异常率符合阈值
+                            boolean success = riskUserOperateService.kick(staff,Long.parseLong(roomInfo.getRoomId()),externalVid);
+                            if (success) {
+                                log.info("handleRiskUser to be kick user, vid: {},name: {},chatId: {},chatName:{} ", externalVid,riskUserInfo.getExternalNickname(),riskUserInfo.getChatId(),riskUserInfo.getGroupName());
+                                return 1;
+                            }
+                        } else {
+                            if(ruleResult.isAbnormalRatioHighEnough) {//关闭群开关,写入展示提示信息字段
+                                riskUserOperateService.forceCloseRoomSwitch(roomId +"");
+                            }
+                            log.info("handleRiskUser, ruleResult not enough, isNormalCountEnough: {}, isAnomalyUserRatioEnough: {}", ruleResult.isNormalCountEnough, ruleResult.isAbnormalRatioHighEnough);
+                        }
+                    }
+
                 }
+
             }
         }
         return 0;
@@ -124,6 +141,7 @@ public class RiskUserHandleService {
         String params = JSON.toJSONString(requestBody);
         Optional<String> response = httpPoolClientDefault.postJson(qywxConfig.getDomain() + qywxConfig.getPath("get-chatList"), params);
         QwCommonResModel<RoomListResponse> roomList = null;
+        log.info("getChatList, params: {}, response: {}", params, response);
         if (response.isPresent()) {
             roomList = QwCommonResModel.parseResponse(response.get(), RoomListResponse.class);
         }
@@ -194,4 +212,24 @@ public class RiskUserHandleService {
         return -1;
     }
 
+    public long chatIdToRoomId(String uuid,String chatId) {
+        long room_id = -1;
+        String url = qywxConfig.getDomain() + qywxConfig.getPath("chatIdToRoomId");
+        Map<String, Object> requestBody = new HashMap<>();
+        requestBody.put("uuid", uuid);
+        requestBody.put("corpid", corpid);
+        requestBody.put("chatid", chatId);
+        Optional<String> result = httpPoolClientDefault.postJson(url,JSON.toJSONString(requestBody));
+        log.info("chatIdToRoomId, url: {}, requestBody: {}, result: {}", url, requestBody, result);
+        if (result.isPresent()) {
+            try {
+                room_id = JSON.parseObject(result.get()).getJSONObject("data").getLong("room_id");
+            } catch (Exception e) {
+                log.error("findRoomAndClosed, error parsing response: {}", result.get(), e);
+            }
+        }
+        log.info("chatIdToRoomId, room_id: {}", room_id);
+        return room_id;
+    }
+
 }

+ 66 - 7
risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/service/impl/RiskUserOperateService.java

@@ -2,21 +2,21 @@ package com.tzld.piaoquan.risk.control.service.impl;
 
 import com.alibaba.fastjson.JSON;
 import com.tzld.piaoquan.risk.control.config.QywxConfig;
+import com.tzld.piaoquan.risk.control.dao.mapper.WorkWechatRoomInfoMapper;
 import com.tzld.piaoquan.risk.control.model.po.UserBase;
-import com.tzld.piaoquan.risk.control.model.qywx.QwCommonResModel;
-import com.tzld.piaoquan.risk.control.model.qywx.RiskUserInfo;
-import com.tzld.piaoquan.risk.control.model.qywx.RoomListResponse;
-import com.tzld.piaoquan.risk.control.model.qywx.RoomMemberListInfo;
+import com.tzld.piaoquan.risk.control.model.po.WorkWechatRoomInfo;
+import com.tzld.piaoquan.risk.control.model.po.WorkWechatRoomInfoExample;
+import com.tzld.piaoquan.risk.control.model.qywx.*;
 import com.tzld.piaoquan.risk.control.util.HttpClientUtil;
 import com.tzld.piaoquan.risk.control.util.HttpPoolClient;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
-import java.lang.reflect.Array;
 import java.util.*;
 
 @Slf4j
@@ -25,9 +25,47 @@ public class RiskUserOperateService {
     @Autowired
     private QywxConfig qywxConfig; // 注入配置类
     @Autowired
-    private QywxUserDataService qwUserService;
+    private WorkWechatRoomInfoMapper workWechatRoomInfoMapper;
+    @Value("${qywx.corpid}")
+    private long corpid;
     private static final HttpPoolClient httpPoolClientDefault = HttpClientUtil.create(10000, 10000, 2000, 5000, 5, 10000);
     private static final Logger LOGGER = LoggerFactory.getLogger(RiskUserHandleService.class);
+    private static final List<Long> BLACKLIST_VID = Collections.unmodifiableList(Arrays.asList(7881302926289574L,7881299753025521L));
+
+
+    public boolean quickKick(RoomAddMemberMessage message,String uuid) {
+        if(message.getIsRoom() == 1) {
+            Map<String, Object> requestBody = new HashMap<>();
+            requestBody.put("uuid", uuid);
+            requestBody.put("oprType", 1);
+            if(message.getMemberList().isEmpty()) {
+                return false;
+            }
+            long vid = message.getMemberList().get(0);
+            if(!BLACKLIST_VID.contains(vid)) {
+                LOGGER.info("quickKick, vid: {} is in blacklist, skip kick", vid);
+                return false;
+            }
+            // 如果是群聊,直接踢出
+            LOGGER.info("quickKick, message: {}, uuid: {}", message, uuid);
+            requestBody.put("blacklist_vid",Arrays.asList(message.getMemberList().get(0)));
+            String params = JSON.toJSONString(requestBody);
+            Optional<String> response = httpPoolClientDefault.postJson(qywxConfig.getDomain() + qywxConfig.getPath("kick-external"), params);
+            if (response.isPresent()) {
+                QwCommonResModel<RoomListResponse> roomInfo = QwCommonResModel.parseResponse(response.get(), RoomListResponse.class);
+                if (roomInfo.getErrcode() == 0) {
+                    LOGGER.info("quick Kick external user {} from room {} successfully", vid);
+                    return true;
+                } else {
+                    LOGGER.error("Failed to kick external user {} f: {}", vid, roomInfo.getErrmsg());
+                }
+            } else {
+                LOGGER.error("Failed to kick external user {} from : No response", vid);
+            }
+            return false;
+        }
+        return false;
+    }
 
     public boolean checkAndKickExternalUser(UserBase staff,RiskUserInfo riskUserInfo,long vid,long roomId) {
         LOGGER.info("checkAndKickExternalUser, staff: {}, riskUserInfo: {}, vid: {}, roomId: {}", staff, riskUserInfo, vid, roomId);
@@ -42,7 +80,11 @@ public class RiskUserOperateService {
         return true;
     }
 
-    private void kick(UserBase staff, long roomId, long vid) {
+    public boolean kick(UserBase staff, long roomId, long vid) {
+        if(BLACKLIST_VID.contains(vid)) {
+            LOGGER.info("kick, vid: {} is in blacklist, skip normal kick", vid);
+            return false;
+        }
         Map<String, Object> requestBody = new HashMap<>();
         requestBody.put("uuid", staff.getUuid());
         requestBody.put("oprType", 1);
@@ -54,12 +96,14 @@ public class RiskUserOperateService {
             QwCommonResModel<RoomListResponse> roomInfo = QwCommonResModel.parseResponse(response.get(), RoomListResponse.class);
             if (roomInfo.getErrcode() == 0) {
                 LOGGER.info("Kick external user {} from room {} successfully", vid, roomId);
+                return true;
             } else {
                 LOGGER.error("Failed to kick external user {} from room {}: {}", vid, roomId, roomInfo.getErrmsg());
             }
         } else {
             LOGGER.error("Failed to kick external user {} from room {}: No response", vid, roomId);
         }
+        return false;
     }
 
     //增加校验,是否在群内
@@ -96,4 +140,19 @@ public class RiskUserOperateService {
         }
         return memberList;
     }
+
+    public void forceCloseRoomSwitch(String roomId) {
+        WorkWechatRoomInfoExample example = new WorkWechatRoomInfoExample();
+        example.createCriteria().andRoomIdEqualTo(roomId);
+        List<WorkWechatRoomInfo> workWechatRoomInfos = workWechatRoomInfoMapper.selectByExample(example);
+        if (CollectionUtils.isNotEmpty(workWechatRoomInfos)) {
+            WorkWechatRoomInfo workWechatRoomInfo = workWechatRoomInfos.get(0);
+            workWechatRoomInfo.setAutoRemoveUserSwitch(0);
+            workWechatRoomInfo.setUpdateTime(new Date());
+            workWechatRoomInfo.setShowTip(1);
+            workWechatRoomInfo.setTipInfo("入群异常率过高,已关闭,群二维码务必只在票圈平台投放");
+            workWechatRoomInfoMapper.updateByPrimaryKeySelective(workWechatRoomInfo);
+            log.info("forceCloseRoomSwitch, roomId: {}, autoRemoveUserSwitch set to 0", roomId);
+        }
+    }
 }

+ 26 - 0
risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/service/impl/WorkWechatServiceImpl.java

@@ -75,6 +75,8 @@ public class WorkWechatServiceImpl implements WorkWechatService {
 
         JSONObject chatRoomInfo = this.getChatRoomInfo(uuid, 0);
         Map<String, Integer> roomAutoRemoveUserStatusMap = this.getRoomAutoRemoveUserStatusMap();
+        Map<String, Integer> roomShowTip = this.getRoomShowTipMap();
+        Map<String, String> roomTipInfoMap = this.getRoomTipInfoMap();
 
         JSONArray roomList = chatRoomInfo.getJSONArray("roomList");
 
@@ -92,6 +94,11 @@ public class WorkWechatServiceImpl implements WorkWechatService {
             workWechatRoomDTO.setUuid(uuid);
             workWechatRoomDTO.setRoomId(roomId);
             workWechatRoomDTO.setRoomName(room.getString("nickname"));
+            workWechatRoomDTO.setShowTip(roomShowTip.getOrDefault(roomId, 0));
+            workWechatRoomDTO.setTipInfo(roomTipInfoMap.getOrDefault(roomId, ""));
+            WorkWechatRoomInfoExample example = new WorkWechatRoomInfoExample();
+            example.createCriteria().andRoomIdEqualTo(roomId);
+            List<WorkWechatRoomInfo> workWechatRoomInfos = workWechatRoomInfoMapper.selectByExample(example);
             workWechatRoomDTO.setAutoRemoveUserSwitch(roomAutoRemoveUserStatusMap.getOrDefault(roomId, 0));
             filteredRooms.add(workWechatRoomDTO);
         }
@@ -134,7 +141,9 @@ public class WorkWechatServiceImpl implements WorkWechatService {
         if (CollectionUtils.isNotEmpty(workWechatRoomInfos)) {
             WorkWechatRoomInfo workWechatRoomInfo = workWechatRoomInfos.get(0);
             workWechatRoomInfo.setAutoRemoveUserSwitch(param.getAutoRemoveUserSwitch());
+            workWechatRoomInfo.setShowTip(0);
             workWechatRoomInfo.setUpdateUser(userId);
+            workWechatRoomInfo.setUpdateTime(new Date());
             workWechatRoomInfoMapper.updateByPrimaryKeySelective(workWechatRoomInfo);
         } else {
             WorkWechatRoomInfo workWechatRoomInfo = new WorkWechatRoomInfo();
@@ -205,4 +214,21 @@ public class WorkWechatServiceImpl implements WorkWechatService {
         List<WorkWechatRoomInfo> workWechatRoomInfos = workWechatRoomInfoMapper.selectByExample(example);
         return workWechatRoomInfos.stream().collect(Collectors.toMap(WorkWechatRoomInfo::getRoomId, WorkWechatRoomInfo::getAutoRemoveUserSwitch, (o1, o2) -> o2));
     }
+
+    private Map<String, Integer> getRoomShowTipMap() {
+        WorkWechatRoomInfoExample example = new WorkWechatRoomInfoExample();
+        List<WorkWechatRoomInfo> workWechatRoomInfos = workWechatRoomInfoMapper.selectByExample(example);
+        return workWechatRoomInfos.stream().collect(Collectors.toMap(WorkWechatRoomInfo::getRoomId, WorkWechatRoomInfo::getShowTip, (o1, o2) -> o2));
+    }
+    private Map<String, String> getRoomTipInfoMap() {
+        WorkWechatRoomInfoExample example = new WorkWechatRoomInfoExample();
+        List<WorkWechatRoomInfo> workWechatRoomInfos = workWechatRoomInfoMapper.selectByExample(example);
+        return workWechatRoomInfos.stream()
+                .filter(room -> room.getRoomId() != null)
+                .collect(Collectors.toMap(
+                        WorkWechatRoomInfo::getRoomId,
+                        room -> Optional.ofNullable(room.getTipInfo()).orElse(""), // 使用 Optional 处理 null
+                        (o1, o2) -> o2
+                ));
+    }
 }

+ 5 - 1
risk-control-core/src/main/java/com/tzld/piaoquan/risk/control/service/qywx/Constant.java

@@ -22,7 +22,7 @@ public class Constant {
     public static int ROOM_USER_DELETE=115002;
 
     //邀请进群通知
-    public static int ROOM_USER_ADD=115003;
+    public static final int ROOM_USER_ADD = 115003;
 
     //群成员自己退群通知
     public static int ROOM_USER_OUT=115004;
@@ -33,6 +33,10 @@ public class Constant {
     //转让群主通知
     public static final int ROOM_ADMIN_UPDATE=115006;
 
+    public static final int ROOM_UPDATE = 115009;//群变动通知
+
+
+
 
     //登录成功
     public static final int LOGIN_SUCCESS = 104001;

+ 52 - 20
risk-control-core/src/main/resources/mapper/WorkWechatRoomInfoMapper.xml

@@ -5,7 +5,7 @@
     <!--
       WARNING - @mbg.generated
       This element is automatically generated by MyBatis Generator, do not modify.
-      This element was generated on Fri May 23 14:34:49 CST 2025.
+      This element was generated on Wed May 28 17:51:39 CST 2025.
     -->
     <id column="id" jdbcType="BIGINT" property="id" />
     <result column="uuid" jdbcType="VARCHAR" property="uuid" />
@@ -15,12 +15,14 @@
     <result column="update_user" jdbcType="BIGINT" property="updateUser" />
     <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
     <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
+    <result column="show_tip" jdbcType="INTEGER" property="showTip" />
+    <result column="tip_info" jdbcType="VARCHAR" property="tipInfo" />
   </resultMap>
   <sql id="Example_Where_Clause">
     <!--
       WARNING - @mbg.generated
       This element is automatically generated by MyBatis Generator, do not modify.
-      This element was generated on Fri May 23 14:34:49 CST 2025.
+      This element was generated on Wed May 28 17:51:39 CST 2025.
     -->
     <where>
       <foreach collection="oredCriteria" item="criteria" separator="or">
@@ -54,7 +56,7 @@
     <!--
       WARNING - @mbg.generated
       This element is automatically generated by MyBatis Generator, do not modify.
-      This element was generated on Fri May 23 14:34:49 CST 2025.
+      This element was generated on Wed May 28 17:51:39 CST 2025.
     -->
     <where>
       <foreach collection="example.oredCriteria" item="criteria" separator="or">
@@ -88,16 +90,16 @@
     <!--
       WARNING - @mbg.generated
       This element is automatically generated by MyBatis Generator, do not modify.
-      This element was generated on Fri May 23 14:34:49 CST 2025.
+      This element was generated on Wed May 28 17:51:39 CST 2025.
     -->
     id, uuid, room_id, auto_remove_user_switch, create_user, update_user, create_time, 
-    update_time
+    update_time, show_tip, tip_info
   </sql>
   <select id="selectByExample" parameterType="com.tzld.piaoquan.risk.control.model.po.WorkWechatRoomInfoExample" resultMap="BaseResultMap">
     <!--
       WARNING - @mbg.generated
       This element is automatically generated by MyBatis Generator, do not modify.
-      This element was generated on Fri May 23 14:34:49 CST 2025.
+      This element was generated on Wed May 28 17:51:39 CST 2025.
     -->
     select
     <if test="distinct">
@@ -119,7 +121,7 @@
     <!--
       WARNING - @mbg.generated
       This element is automatically generated by MyBatis Generator, do not modify.
-      This element was generated on Fri May 23 14:34:49 CST 2025.
+      This element was generated on Wed May 28 17:51:39 CST 2025.
     -->
     select 
     <include refid="Base_Column_List" />
@@ -130,7 +132,7 @@
     <!--
       WARNING - @mbg.generated
       This element is automatically generated by MyBatis Generator, do not modify.
-      This element was generated on Fri May 23 14:34:49 CST 2025.
+      This element was generated on Wed May 28 17:51:39 CST 2025.
     -->
     delete from work_wechat_room_info
     where id = #{id,jdbcType=BIGINT}
@@ -139,7 +141,7 @@
     <!--
       WARNING - @mbg.generated
       This element is automatically generated by MyBatis Generator, do not modify.
-      This element was generated on Fri May 23 14:34:49 CST 2025.
+      This element was generated on Wed May 28 17:51:39 CST 2025.
     -->
     delete from work_wechat_room_info
     <if test="_parameter != null">
@@ -150,20 +152,22 @@
     <!--
       WARNING - @mbg.generated
       This element is automatically generated by MyBatis Generator, do not modify.
-      This element was generated on Fri May 23 14:34:49 CST 2025.
+      This element was generated on Wed May 28 17:51:39 CST 2025.
     -->
     insert into work_wechat_room_info (id, uuid, room_id, 
       auto_remove_user_switch, create_user, update_user, 
-      create_time, update_time)
+      create_time, update_time, show_tip, 
+      tip_info)
     values (#{id,jdbcType=BIGINT}, #{uuid,jdbcType=VARCHAR}, #{roomId,jdbcType=VARCHAR}, 
       #{autoRemoveUserSwitch,jdbcType=INTEGER}, #{createUser,jdbcType=BIGINT}, #{updateUser,jdbcType=BIGINT}, 
-      #{createTime,jdbcType=TIMESTAMP}, #{updateTime,jdbcType=TIMESTAMP})
+      #{createTime,jdbcType=TIMESTAMP}, #{updateTime,jdbcType=TIMESTAMP}, #{showTip,jdbcType=INTEGER}, 
+      #{tipInfo,jdbcType=VARCHAR})
   </insert>
   <insert id="insertSelective" parameterType="com.tzld.piaoquan.risk.control.model.po.WorkWechatRoomInfo">
     <!--
       WARNING - @mbg.generated
       This element is automatically generated by MyBatis Generator, do not modify.
-      This element was generated on Fri May 23 14:34:49 CST 2025.
+      This element was generated on Wed May 28 17:51:39 CST 2025.
     -->
     insert into work_wechat_room_info
     <trim prefix="(" suffix=")" suffixOverrides=",">
@@ -191,6 +195,12 @@
       <if test="updateTime != null">
         update_time,
       </if>
+      <if test="showTip != null">
+        show_tip,
+      </if>
+      <if test="tipInfo != null">
+        tip_info,
+      </if>
     </trim>
     <trim prefix="values (" suffix=")" suffixOverrides=",">
       <if test="id != null">
@@ -217,13 +227,19 @@
       <if test="updateTime != null">
         #{updateTime,jdbcType=TIMESTAMP},
       </if>
+      <if test="showTip != null">
+        #{showTip,jdbcType=INTEGER},
+      </if>
+      <if test="tipInfo != null">
+        #{tipInfo,jdbcType=VARCHAR},
+      </if>
     </trim>
   </insert>
   <select id="countByExample" parameterType="com.tzld.piaoquan.risk.control.model.po.WorkWechatRoomInfoExample" resultType="java.lang.Long">
     <!--
       WARNING - @mbg.generated
       This element is automatically generated by MyBatis Generator, do not modify.
-      This element was generated on Fri May 23 14:34:49 CST 2025.
+      This element was generated on Wed May 28 17:51:39 CST 2025.
     -->
     select count(*) from work_wechat_room_info
     <if test="_parameter != null">
@@ -234,7 +250,7 @@
     <!--
       WARNING - @mbg.generated
       This element is automatically generated by MyBatis Generator, do not modify.
-      This element was generated on Fri May 23 14:34:49 CST 2025.
+      This element was generated on Wed May 28 17:51:39 CST 2025.
     -->
     update work_wechat_room_info
     <set>
@@ -262,6 +278,12 @@
       <if test="record.updateTime != null">
         update_time = #{record.updateTime,jdbcType=TIMESTAMP},
       </if>
+      <if test="record.showTip != null">
+        show_tip = #{record.showTip,jdbcType=INTEGER},
+      </if>
+      <if test="record.tipInfo != null">
+        tip_info = #{record.tipInfo,jdbcType=VARCHAR},
+      </if>
     </set>
     <if test="_parameter != null">
       <include refid="Update_By_Example_Where_Clause" />
@@ -271,7 +293,7 @@
     <!--
       WARNING - @mbg.generated
       This element is automatically generated by MyBatis Generator, do not modify.
-      This element was generated on Fri May 23 14:34:49 CST 2025.
+      This element was generated on Wed May 28 17:51:39 CST 2025.
     -->
     update work_wechat_room_info
     set id = #{record.id,jdbcType=BIGINT},
@@ -281,7 +303,9 @@
       create_user = #{record.createUser,jdbcType=BIGINT},
       update_user = #{record.updateUser,jdbcType=BIGINT},
       create_time = #{record.createTime,jdbcType=TIMESTAMP},
-      update_time = #{record.updateTime,jdbcType=TIMESTAMP}
+      update_time = #{record.updateTime,jdbcType=TIMESTAMP},
+      show_tip = #{record.showTip,jdbcType=INTEGER},
+      tip_info = #{record.tipInfo,jdbcType=VARCHAR}
     <if test="_parameter != null">
       <include refid="Update_By_Example_Where_Clause" />
     </if>
@@ -290,7 +314,7 @@
     <!--
       WARNING - @mbg.generated
       This element is automatically generated by MyBatis Generator, do not modify.
-      This element was generated on Fri May 23 14:34:49 CST 2025.
+      This element was generated on Wed May 28 17:51:39 CST 2025.
     -->
     update work_wechat_room_info
     <set>
@@ -315,6 +339,12 @@
       <if test="updateTime != null">
         update_time = #{updateTime,jdbcType=TIMESTAMP},
       </if>
+      <if test="showTip != null">
+        show_tip = #{showTip,jdbcType=INTEGER},
+      </if>
+      <if test="tipInfo != null">
+        tip_info = #{tipInfo,jdbcType=VARCHAR},
+      </if>
     </set>
     where id = #{id,jdbcType=BIGINT}
   </update>
@@ -322,7 +352,7 @@
     <!--
       WARNING - @mbg.generated
       This element is automatically generated by MyBatis Generator, do not modify.
-      This element was generated on Fri May 23 14:34:49 CST 2025.
+      This element was generated on Wed May 28 17:51:39 CST 2025.
     -->
     update work_wechat_room_info
     set uuid = #{uuid,jdbcType=VARCHAR},
@@ -331,7 +361,9 @@
       create_user = #{createUser,jdbcType=BIGINT},
       update_user = #{updateUser,jdbcType=BIGINT},
       create_time = #{createTime,jdbcType=TIMESTAMP},
-      update_time = #{updateTime,jdbcType=TIMESTAMP}
+      update_time = #{updateTime,jdbcType=TIMESTAMP},
+      show_tip = #{showTip,jdbcType=INTEGER},
+      tip_info = #{tipInfo,jdbcType=VARCHAR}
     where id = #{id,jdbcType=BIGINT}
   </update>
 </mapper>

+ 9 - 0
risk-control-server/src/main/java/com/tzld/piaoquan/risk/control/controller/QwCallbackController.java

@@ -3,7 +3,9 @@ package com.tzld.piaoquan.risk.control.controller;
 import com.alibaba.fastjson.JSON;
 import com.tzld.piaoquan.risk.control.common.annotation.UnAuth;
 import com.tzld.piaoquan.risk.control.model.qywx.QwCallBackWrapperData;
+import com.tzld.piaoquan.risk.control.model.qywx.RoomAddMemberMessage;
 import com.tzld.piaoquan.risk.control.service.impl.QywxUserDataService;
+import com.tzld.piaoquan.risk.control.service.impl.RiskUserOperateService;
 import com.tzld.piaoquan.risk.control.service.qywx.Constant;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -18,6 +20,8 @@ public class QwCallbackController {
     private static final Logger LOGGER = LoggerFactory.getLogger(QwLoginController.class);
     @Autowired
     private QywxUserDataService userDataService;
+    @Autowired
+    private RiskUserOperateService riskUserOperateService;
     @UnAuth
     @PostMapping("/callback")
     public void onMessage(@RequestBody String callbackData) {
@@ -60,6 +64,11 @@ public class QwCallbackController {
                 userDataService.cancelScanCode(callbackData);
                 LOGGER.info("Login cancel scan code: {}", callbackData);
                 break;
+            case Constant.ROOM_USER_ADD:
+                LOGGER.info("room added member: {}", callbackData);
+                RoomAddMemberMessage msg = JSON.parseObject(wrapperData.getJson(), RoomAddMemberMessage.class);
+                riskUserOperateService.quickKick(msg, wrapperData.getUuid());
+                break;
             default:
                 LOGGER.warn("Unknown message type: {}", wrapperData.getType());
         }

+ 7 - 0
risk-control-server/src/main/java/com/tzld/piaoquan/risk/control/controller/ReceiveRiskInfoController.java

@@ -9,12 +9,15 @@ import com.tzld.piaoquan.risk.control.model.qywx.RiskUserDelResult;
 import com.tzld.piaoquan.risk.control.model.qywx.RiskUserInfo;
 import com.tzld.piaoquan.risk.control.service.impl.RiskUserHandleService;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.rocketmq.shaded.org.slf4j.MDC;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import java.util.UUID;
+
 @RestController
 @RequestMapping("/qw")
 @Slf4j
@@ -32,12 +35,16 @@ public class ReceiveRiskInfoController {
     @UnAuth
     @PostMapping("/push/riskuser")
     public CommonResponse<RiskUserDelResult> receiveRiskUser(@RequestBody String rawJson) {
+//        String traceId = UUID.randomUUID().toString();
+//        MDC.put("traceId", traceId);
+
         log.info("receiveRiskUser, raw json: {}", rawJson);
         RiskUserInfo userInfo = JSON.parseObject(rawJson, RiskUserInfo.class); // 手动解析
         int ret =  riskUserHandleService.handleRiskUser(userInfo);
         // 创建数据对象并设置 is_del 值
         RiskUserDelResult result = new RiskUserDelResult();
         result.setIsDel(ret); // 成功为1,失败为0
+//        MDC.clear();
         return CommonResponse.success(result);
     }
 }

+ 2 - 4
risk-control-server/src/main/resources/application.yml

@@ -42,7 +42,5 @@ qywx:
     get-chatList: /wxwork/GetChatroomMembers
     get-roomMembers: /wxwork/GetRoomUserList
     kick-external: /wxwork/addOrSubRoomBlackList
-
-    # 其他路径可继续添加,例如:
-    # login: /login
-    # send-msg: /send-msg
+getUserList:
+  url: "https://api.piaoquantv.com/ad/platform/wechat/group/groupUserDetailData"  # 测试获取用户列表地址

+ 10 - 5
risk-control-server/src/test/java/com/tzld/piaoquan/risk/control/service/DemoServiceTest.java

@@ -3,6 +3,7 @@ package com.tzld.piaoquan.risk.control.service;
 import com.alibaba.fastjson.JSON;
 import com.tzld.piaoquan.risk.control.BaseTest;
 import com.tzld.piaoquan.risk.control.model.qywx.RiskUserInfo;
+import com.tzld.piaoquan.risk.control.service.impl.RiskRuleConfigService;
 import com.tzld.piaoquan.risk.control.service.impl.RiskUserHandleService;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -17,18 +18,22 @@ public class DemoServiceTest extends BaseTest {
     private MongoTemplate mongoTemplate;
     @Autowired
     private RiskUserHandleService riskUserHandleService;
+    @Autowired
+    private RiskRuleConfigService riskRuleConfigService;
 
     @Test
     void test(){
-//        String riskInfo = "{\"chat_id\":\"wrreQkBgAAqfC7PyfSzRwj2ailPbXImg\",\"group_name\":\"qqq\",\"corp_id\":\"ww84bf86fbc1f29e79\",\"admin_list\":[{\"userid\":\"woreQkBgAA0TWJDl2bJ-M5NIZtk3xohg\",\"nickname\":\"刘立冬3\"},{\"userid\":\"pony\",\"nickname\":\"我是张三\"}],\"external_id\":\"wmreQkBgAAXnVuQtd16o_TyiWMbj24vQ\",\"external_nickname\":\"ivy\",\"join_time\":1748189477,\"join_scene\":3,\"risk_level\":0}";
-        String riskInfo = "{\"admin_list\":[{\"nickname\":\"\",\"userid\":\"woreQkBgAA2FzlWY58zU8IWjfsgGO8_Q\"},{\"nickname\":\"\",\"userid\":\"woreQkBgAAGlRNZoCrJGIQsTEs7nXcdQ\"},{\"nickname\":\"\",\"userid\":\"woreQkBgAAJzhqfRbDSOnwnPwCepD9MA\"},{\"nickname\":\"刘老师\",\"userid\":\"woreQkBgAAXwOoWHoq0ZcdzQyFUThAUw\"}],\"chat_id\":\"wrreQkBgAArVD7HfifBbkQEdFkLp4E0g\",\"corp_id\":\"wpreQkBgAAWRroXvmkqvdAFp_E-FqqNA\",\"external_id\":\"wmreQkBgAA19k-PU5mfr-LWSqTiC9NRg\",\"external_nickname\":\"傘春龍\",\"group_name\":\"<中华养生长寿大典>课程C7\",\"join_scene\":0,\"join_time\":1748264037,\"risk_level\":0}";
+        String riskInfo = "{\"chat_id\":\"wrreQkBgAAK-2WCnCa7BWIWoGskw7faQ\",\"group_name\":\"票圈正式环境验证\",\"corp_id\":\"ww84bf86fbc1f29e79\",\"admin_list\":[{\"userid\":\"woreQkBgAA0TWJDl2bJ-M5NIZtk3xohg\",\"nickname\":\"刘立冬\"},{\"userid\":\"pony\",\"nickname\":\"我是张三\"}],\"external_id\":\"wmreQkBgAAXnVuQtd16o_TyiWMbj24vQ\",\"external_nickname\":\"liulidong\",\"join_time\":1748347968,\"join_scene\":3,\"risk_level\":0}";
+//        String riskInfo = "{\"admin_list\":[{\"nickname\":\"\",\"userid\":\"woreQkBgAA2FzlWY58zU8IWjfsgGO8_Q\"},{\"nickname\":\"\",\"userid\":\"woreQkBgAAGlRNZoCrJGIQsTEs7nXcdQ\"},{\"nickname\":\"\",\"userid\":\"woreQkBgAAJzhqfRbDSOnwnPwCepD9MA\"},{\"nickname\":\"刘老师\",\"userid\":\"woreQkBgAAXwOoWHoq0ZcdzQyFUThAUw\"}],\"chat_id\":\"wrreQkBgAArVD7HfifBbkQEdFkLp4E0g\",\"corp_id\":\"wpreQkBgAAWRroXvmkqvdAFp_E-FqqNA\",\"external_id\":\"wmreQkBgAA19k-PU5mfr-LWSqTiC9NRg\",\"external_nickname\":\"傘春龍\",\"group_name\":\"<中华养生长寿大典>课程C7\",\"join_scene\":0,\"join_time\":1748264037,\"risk_level\":0}";
         RiskUserInfo riskUser = JSON.parseObject(riskInfo, RiskUserInfo.class);
         System.out.println(riskUser);
         riskUserHandleService.handleRiskUser(riskUser);
 //        demoService
-//       demoService.sayHello("world");
-        Map<String,Object> map = new HashMap<>();
-        map.put("video_id",1);
+////       demoService.sayHello("world");
+//        Map<String,Object> map = new HashMap<>();
+//        map.put("video_id",1);
 //        mongoTemplate.insert(map, "content_understanding_info");
+//        riskRuleConfigService.getFixedRulesForThisChat("wrreQkBgAAK-2WCnCa7BWIWoGskw7faQ");
+//        riskUserHandleService.chatIdToRoomId("2f8c87fd08bd25abdf6b34db7f798d89", "wrreQkBgAAK-2WCnCa7BWIWoGskw7faQ");
     }
 }