Procházet zdrojové kódy

企微聊天-回调消息推送调整

Long před 2 týdny
rodič
revize
12f7b031ad
23 změnil soubory, kde provedl 186 přidání a 184 odebrání
  1. 29 4
      fs-company/src/main/java/com/fs/company/controller/qw/QwMsgController.java
  2. 5 6
      fs-qw-api-msg/src/main/java/com/fs/app/controller/QwMsgController.java
  3. 65 10
      fs-qw-api-msg/src/main/java/com/fs/app/socket/QwImSocket.java
  4. 0 136
      fs-qw-api-msg/src/main/java/com/fs/app/socket/QwImSocketBroadcaster.java
  5. 14 2
      fs-qw-api-msg/src/main/java/com/fs/app/socket/configurator/QwImConfigurator.java
  6. 3 3
      fs-qw-api-msg/src/main/java/com/fs/app/util/AudioUtils.java
  7. 14 0
      fs-qw-api-msg/src/main/java/com/fs/framework/service/TokenService.java
  8. 6 1
      fs-service/src/main/java/com/fs/fastGpt/service/impl/AiHookServiceImpl.java
  9. 1 4
      fs-service/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java
  10. 1 1
      fs-service/src/main/java/com/fs/qw/mapper/QwGroupChatMapper.java
  11. 4 0
      fs-service/src/main/java/com/fs/qw/mapper/QwTagMapper.java
  12. 1 1
      fs-service/src/main/java/com/fs/qw/mapper/QwUserMapper.java
  13. 2 2
      fs-service/src/main/java/com/fs/qw/service/IQwMsgService.java
  14. 5 0
      fs-service/src/main/java/com/fs/qw/service/IQwTagService.java
  15. 4 4
      fs-service/src/main/java/com/fs/qw/service/impl/QwMsgServiceImpl.java
  16. 8 0
      fs-service/src/main/java/com/fs/qw/service/impl/QwTagServiceImpl.java
  17. 1 1
      fs-service/src/main/java/com/fs/qw/vo/QwMessageListVO.java
  18. 7 7
      fs-service/src/main/java/com/fs/wxwork/utils/WxWorkHttpUtil.java
  19. 1 1
      fs-service/src/main/resources/application-dev.yml
  20. 1 1
      fs-service/src/main/resources/application-druid-jnmy-test.yml
  21. 3 0
      fs-service/src/main/resources/mapper/qw/QwExternalContactMapper.xml
  22. 3 0
      fs-service/src/main/resources/mapper/qw/QwGroupChatMapper.xml
  23. 8 0
      fs-service/src/main/resources/mapper/qw/QwTagMapper.xml

+ 29 - 4
fs-company/src/main/java/com/fs/company/controller/qw/QwMsgController.java

@@ -10,6 +10,7 @@ import com.fs.common.enums.BusinessType;
 import com.fs.common.exception.CustomException;
 import com.fs.common.exception.ServiceException;
 import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.course.param.FsCourseLinkMiniParam;
 import com.fs.course.param.FsCourseListBySidebarParam;
@@ -33,6 +34,7 @@ import com.fs.qw.service.*;
 import com.fs.qw.vo.QwContactListVO;
 import com.fs.qw.vo.QwContactVO;
 import com.fs.qw.vo.QwMessageListVO;
+import com.fs.qw.vo.QwTagVO;
 import com.fs.statis.service.IStatisticsService;
 import com.fs.statistics.dto.WatchCourseStatisticsDTO;
 import com.fs.statistics.param.WatchCourseStatisticsParam;
@@ -81,6 +83,8 @@ public class QwMsgController extends BaseController
     private IFsUserOperationLogService userOperationLogService;
     @Autowired
     private IQwSessionService sessionService;
+    @Autowired
+    private IQwTagService tagService;
 
     /**
      * 查询企微聊天记录列表
@@ -311,18 +315,18 @@ public class QwMsgController extends BaseController
     // 获取联系人列表
     @GetMapping("/contactList/{userId}")
     @ApiOperation("获取联系人列表")
-    public TableDataInfo contacts(@PathVariable("userId") Long qwUserId) {
+    public TableDataInfo contacts(@PathVariable("userId") Long qwUserId, @RequestParam(required = false) String name) {
         startPage();
-        List<QwContactVO> list  = qwMsgService.contactListByQwUserId(qwUserId);
+        List<QwContactVO> list  = qwMsgService.contactListByQwUserId(qwUserId, name);
         return getDataTable(list);
     }
 
     // 获取群组列表
     @GetMapping("/groupList/{userId}")
     @ApiOperation("获取群组列表")
-    public TableDataInfo groups(@PathVariable("userId") Long qwUserId) {
+    public TableDataInfo groups(@PathVariable("userId") Long qwUserId, @RequestParam(required = false) String name) {
         startPage();
-        List<QwContactVO> list  = qwMsgService.groupListByQwUserId(qwUserId);
+        List<QwContactVO> list  = qwMsgService.groupListByQwUserId(qwUserId, name);
         return getDataTable(list);
     }
 
@@ -393,4 +397,25 @@ public class QwMsgController extends BaseController
         return getDataTable(logList);
     }
 
+    @ApiOperation("获取外部联系人标签列表")
+    @GetMapping("/getQwExternalContactVisitList")
+    public TableDataInfo getQwExternalContactTagList(@RequestParam(value = "qwExternalContactId") Long qwExternalContactId) {
+        QwExternalContact externalContact = qwExternalContactService.selectQwExternalContactById(qwExternalContactId);
+        if (Objects.isNull(externalContact)){
+            throw new ServiceException("外部联系人不存在");
+        }
+        String tagIds = externalContact.getTagIds();
+
+        List<QwTagVO> tagList = new ArrayList<>();
+        if (StringUtils.isBlank(externalContact.getTagIds()) || "[]".equals(externalContact.getTagIds())) {
+            return getDataTable(tagList);
+        }
+
+        List<String> ids = Arrays.asList(tagIds.replace("[", "").replace("]", "").split(","));
+
+        startPage();
+        tagList = tagService.selectQwTagVOListByTagIds(ids);
+        return getDataTable(tagList);
+    }
+
 }

+ 5 - 6
fs-qw-api-msg/src/main/java/com/fs/app/controller/QwMsgController.java

@@ -3,7 +3,6 @@ package com.fs.app.controller;
 import cn.hutool.core.util.StrUtil;
 import com.alibaba.fastjson.JSON;
 import com.fs.app.socket.QwImSocket;
-import com.fs.app.socket.QwImSocketBroadcaster;
 import com.fs.app.util.AudioUtils;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
@@ -620,7 +619,7 @@ public class QwMsgController {
     private void processTextMessage(Long qwUserId, Long senderVid, Long receiverVid, Long serverId, String content, WxWorkMsgResp wxWorkMsgResp, boolean isRoom, String chatId, String chatAvatar) {
         // 保存聊天消息
         QwMessageListVO message = aiHookService.saveQwMsg(qwUserId, senderVid, receiverVid, serverId, content, wxWorkMsgResp.getUuid(), wxWorkMsgResp.getJson(), 1, isRoom, chatId, chatAvatar);
-        QwImSocketBroadcaster.broadcast(message);
+        QwImSocket.broadcast(message);
     }
 
     /**
@@ -669,7 +668,7 @@ public class QwMsgController {
 
         // 保存聊天消息
         QwMessageListVO message = aiHookService.saveQwMsg(qwUserId, senderVid, receiverVid, serverId, json.toString(), wxWorkMsgResp.getUuid(), wxWorkMsgResp.getJson(), 4, isRoom, chatId, chatAvatar);
-        QwImSocketBroadcaster.broadcast(message);
+        QwImSocket.broadcast(message);
     }
 
     /**
@@ -696,7 +695,7 @@ public class QwMsgController {
         String content = fileUrlResp.getData();
         // 保存聊天消息
         QwMessageListVO message = aiHookService.saveQwMsg(qwUserId, senderVid, receiverVid, serverId, content, wxWorkMsgResp.getUuid(), wxWorkMsgResp.getJson(), 2, isRoom, chatId, chatAvatar);
-        QwImSocketBroadcaster.broadcast(message);
+        QwImSocket.broadcast(message);
     }
 
     /**
@@ -715,7 +714,7 @@ public class QwMsgController {
         String content = wxWorkMessageDTO.getUrl();
         // 保存聊天消息
         QwMessageListVO message = aiHookService.saveQwMsg(qwUserId, senderVid, receiverVid, serverId, content, wxWorkMsgResp.getUuid(), wxWorkMsgResp.getJson(), 3, isRoom, chatId, chatAvatar);
-        QwImSocketBroadcaster.broadcast(message);
+        QwImSocket.broadcast(message);
     }
 
     /**
@@ -750,7 +749,7 @@ public class QwMsgController {
 
         // 保存聊天消息
         QwMessageListVO message = aiHookService.saveQwMsg(qwUserId, senderVid, receiverVid, serverId, json.toString(), wxWorkMsgResp.getUuid(), wxWorkMsgResp.getJson(), 5, isRoom, chatId, chatAvatar);
-        QwImSocketBroadcaster.broadcast(message);
+        QwImSocket.broadcast(message);
     }
 
     /**

+ 65 - 10
fs-qw-api-msg/src/main/java/com/fs/app/socket/QwImSocket.java

@@ -1,6 +1,8 @@
 package com.fs.app.socket;
 
+import com.alibaba.fastjson.JSON;
 import com.fs.app.socket.configurator.QwImConfigurator;
+import com.fs.qw.vo.QwMessageListVO;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
 
@@ -10,44 +12,97 @@ import javax.websocket.OnOpen;
 import javax.websocket.Session;
 import javax.websocket.server.PathParam;
 import javax.websocket.server.ServerEndpoint;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArraySet;
 
 @Slf4j
-@ServerEndpoint(value = "/qwImSocket/{companyId}", configurator = QwImConfigurator.class)
+@ServerEndpoint(value = "/qwImSocket/{companyUserId}", configurator = QwImConfigurator.class)
 @Component
 public class QwImSocket {
 
+    private static final ConcurrentHashMap<Long, CopyOnWriteArraySet<Session>> companyUserSessions = new ConcurrentHashMap<>();
+
     /**
      * 连接建立成功调用的方法
      * @param session   连接会话
-     * @param companyId 公司ID
+     * @param companyUserId 公司ID
      */
     @OnOpen
-    public void onOpen(Session session, @PathParam("companyId") Long companyId) {
+    public void onOpen(Session session, @PathParam("companyUserId") Long companyUserId) {
         // 将当前会话加入到会话池中
-        QwImSocketBroadcaster.registerSession(companyId, session);
+        companyUserSessions.computeIfAbsent(companyUserId, k -> new CopyOnWriteArraySet<>()).add(session);
     }
 
     /**
      * 连接关闭调用的方法
      * @param session   连接会话
-     * @param companyId 公司ID
+     * @param companyUserId 公司ID
      */
     @OnClose
-    public void onClose(Session session, @PathParam("companyId") Long companyId) {
+    public void onClose(Session session, @PathParam("companyUserId") Long companyUserId) {
         // 从会话池中移除当前会话
-        QwImSocketBroadcaster.removeSession(companyId, session);
+        CopyOnWriteArraySet<Session> sessions = companyUserSessions.get(companyUserId);
+        if (sessions != null) {
+            sessions.remove(session);
+            // 如果直播间没人了,可以移除该直播间
+            if (sessions.isEmpty()) {
+                companyUserSessions.remove(companyUserId);
+            }
+        }
     }
 
     /**
      * 发生错误时调用的方法
      * @param session   连接会话
-     * @param companyId 公司ID
+     * @param companyUserId 公司ID
      * @param error     错误对象
      */
     @OnError
-    public void onError(Session session, @PathParam("companyId") Long companyId, Throwable error) {
+    public void onError(Session session, @PathParam("companyUserId") Long companyUserId, Throwable error) {
         log.error("发生错误!会话ID: {}", session.getId());
-        QwImSocketBroadcaster.removeSession(companyId, session);
+        CopyOnWriteArraySet<Session> sessions = companyUserSessions.get(companyUserId);
+        if (sessions != null) {
+            sessions.remove(session);
+            // 如果直播间没人了,可以移除该直播间
+            if (sessions.isEmpty()) {
+                companyUserSessions.remove(companyUserId);
+            }
+        }
+    }
+
+    /**
+     * 群发消息
+     * @param message   要发送的消息
+     */
+    public static void broadcast(QwMessageListVO message) {
+        if (Objects.isNull(message) || message.getCompanyUserId() == null) {
+            return;
+        }
+
+        String msg = JSON.toJSONString(message);
+        CopyOnWriteArraySet<Session> sessions = companyUserSessions.get(message.getCompanyUserId());
+        if (sessions != null) {
+            List<Session> closeSession = new ArrayList<>();
+            for (Session session : sessions) {
+                if (!session.isOpen()) {
+                    closeSession.add(session);
+                    continue;
+                }
+
+                try {
+                    session.getBasicRemote().sendText(msg);
+                } catch (IOException e) {
+                    log.error("发送消息给会话[{}]失败: {}", session.getId(), e.getMessage());
+                    // 移除无效会话
+                    closeSession.add(session);
+                }
+            }
+            closeSession.forEach(sessions::remove);
+        }
     }
 
 }

+ 0 - 136
fs-qw-api-msg/src/main/java/com/fs/app/socket/QwImSocketBroadcaster.java

@@ -1,136 +0,0 @@
-package com.fs.app.socket;
-
-import com.alibaba.fastjson.JSON;
-import com.fs.qw.vo.QwMessageListVO;
-import lombok.extern.slf4j.Slf4j;
-
-import javax.websocket.Session;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-import java.util.concurrent.*;
-
-@Slf4j
-public class QwImSocketBroadcaster {
-
-    /** 租户 -> Session 集合 */
-    private static final ConcurrentMap<Long, CopyOnWriteArraySet<Session>> companySessions = new ConcurrentHashMap<>();
-
-    /** 租户 -> 消息队列 */
-    private static final ConcurrentMap<Long, BlockingQueue<QwMessageListVO>> messageQueues = new ConcurrentHashMap<>();
-
-    /** 每个租户一个发送线程 */
-    private static final ConcurrentMap<Long, Future<?>> companyWorkers = new ConcurrentHashMap<>();
-
-    /** 全局线程池(异步发送) */
-    private static final ExecutorService EXECUTOR = new ThreadPoolExecutor(
-            5,
-            100,
-            60,
-            TimeUnit.SECONDS,
-            new LinkedBlockingQueue<>(100),
-            r -> {
-                Thread t = new Thread(r, "qwIm-websocket-broadcast-worker");
-                t.setDaemon(true);
-                return t;
-            },
-            // 使用 AbortPolicy:拋出 RejectedExecutionException
-            new ThreadPoolExecutor.AbortPolicy()
-    );
-
-    // 注册会话
-    public static void registerSession(Long companyId, Session session) {
-        companySessions.computeIfAbsent(companyId, k -> new CopyOnWriteArraySet<>()).add(session);
-        messageQueues.computeIfAbsent(companyId, k -> new LinkedBlockingQueue<>(10000));
-        startWorkerIfAbsent(companyId);
-        log.info("✅ 新连接注册:companyId={}, sessionId={}", companyId, session.getId());
-    }
-
-    // 移除会话
-    public static void removeSession(Long companyId, Session session) {
-        Optional.ofNullable(companySessions.get(companyId)).ifPresent(sessions -> {
-            sessions.remove(session);
-            log.info("🗑️ 移除Session:companyId={}, sessionId={}", companyId, session.getId());
-            if (sessions.isEmpty()) stopWorker(companyId);
-        });
-    }
-
-    // 广播消息(异步入队)
-    public static void broadcast(QwMessageListVO message) {
-        if (message == null) {
-            log.warn("广播消息不存在");
-            return;
-        }
-
-        BlockingQueue<QwMessageListVO> queue = messageQueues.get(message.getCompanyId());
-        if (queue == null) {
-            log.warn("消息队列不存在 msg: {}", message);
-            return;
-        }
-
-        boolean success = queue.offer(message);
-        if (!success) {
-            log.warn("消息队列已满 companyId: {}", message.getCompanyId());
-        }
-    }
-
-    // 启动对应租户的发送线程
-    private static void startWorkerIfAbsent(Long companyId) {
-        companyWorkers.computeIfAbsent(companyId, id -> EXECUTOR.submit(() -> {
-            BlockingQueue<QwMessageListVO> queue = messageQueues.get(companyId);
-            CopyOnWriteArraySet<Session> sessions = companySessions.get(companyId);
-            log.info("🚀 启动租户消息分发线程:companyId={}", companyId);
-
-            while (!Thread.currentThread().isInterrupted()) {
-                try {
-                    QwMessageListVO msgObj = queue.take();
-                    String msg = JSON.toJSONString(msgObj);
-
-                    if (sessions == null || sessions.isEmpty()) {
-                        continue;
-                    }
-
-                    List<Session> closed = new ArrayList<>();
-                    for (Session s : sessions) {
-                        if (!s.isOpen()) {
-                            closed.add(s);
-                            continue;
-                        }
-                        try {
-                            s.getAsyncRemote().sendText(msg, result -> {
-                                if (!result.isOK()) {
-                                    Throwable e = result.getException();
-                                    log.error("❌ 异步发送失败:companyId={}, sessionId={}, error={}", companyId, s.getId(), e != null ? e.getMessage() : "未知错误", e);
-                                    closed.add(s);
-                                }
-                            });
-                        } catch (IllegalStateException e) {
-                            log.warn("⚠️ Session状态异常:sessionId={}, msg={}", s.getId(), e.getMessage());
-                            closed.add(s);
-                        } catch (Exception e) {
-                            log.error("广播异常:sessionId={}, error={}", s.getId(), e.getMessage(), e);
-                        }
-                    }
-                    closed.forEach(sessions::remove);
-
-                    TimeUnit.MILLISECONDS.sleep(1); // 1 毫秒延迟
-                } catch (InterruptedException e) {
-                    Thread.currentThread().interrupt();
-                } catch (Exception e) {
-                    log.error("广播线程异常:companyId={}, error={}", companyId, e.getMessage(), e);
-                }
-            }
-        }));
-    }
-
-    // 停止租户线程
-    private static void stopWorker(Long companyId) {
-        Future<?> worker = companyWorkers.remove(companyId);
-        if (worker != null) {
-            worker.cancel(true);
-            log.info("🛑 停止租户广播线程:companyId={}", companyId);
-        }
-        messageQueues.remove(companyId);
-    }
-
-}

+ 14 - 2
fs-qw-api-msg/src/main/java/com/fs/app/socket/configurator/QwImConfigurator.java

@@ -1,7 +1,11 @@
 package com.fs.app.socket.configurator;
 
 import com.fs.app.exception.FSException;
+import com.fs.common.utils.spring.SpringUtils;
+import com.fs.framework.security.LoginUser;
+import com.fs.framework.service.TokenService;
 
+import javax.servlet.http.HttpServletRequest;
 import javax.websocket.HandshakeResponse;
 import javax.websocket.server.HandshakeRequest;
 import javax.websocket.server.ServerEndpointConfig;
@@ -11,11 +15,19 @@ import java.util.Objects;
 
 public class QwImConfigurator extends ServerEndpointConfig.Configurator {
 
+    private final TokenService tokenService = SpringUtils.getBean(TokenService.class);
+
     @Override
     public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
         Map<String, List<String>> parameterMap = request.getParameterMap();
-        List<String> token = parameterMap.get("token");
-        if (Objects.isNull(token)) {
+        List<String> tokens = parameterMap.get("token");
+        if (tokens == null || tokens.isEmpty() || tokens.get(0).isEmpty()) {
+            throw new FSException("Unauthorized access to WebSocket endpoint.");
+        }
+
+        String token = tokens.get(0);
+        LoginUser loginUser = tokenService.getLoginUser(token);
+        if (loginUser == null) {
             throw new FSException("Unauthorized access to WebSocket endpoint.");
         }
     }

+ 3 - 3
fs-qw-api-msg/src/main/java/com/fs/app/util/AudioUtils.java

@@ -39,7 +39,7 @@ public class AudioUtils {
         try {
             // 1. 从网络下载 SILK 文件
             downloadFile(silkUrl, downloadedSilkFilePath);
-            log.info("SILK file downloaded to: {}", downloadedSilkFilePath);
+            log.debug("SILK file downloaded to: {}", downloadedSilkFilePath);
 
             // 2. 使用 silk-v3-decoder 解码 SILK 到 PCM
             List<String> silkDecodeCommand = new ArrayList<>();
@@ -58,7 +58,7 @@ public class AudioUtils {
                 log.error("silk conversion failed or timed out. error: {}", silkDecoderOutput);
                 return null;
             }
-            log.info("SILK decoder to PCM successfully.");
+            log.debug("SILK decoder to PCM successfully.");
 
             // 3. 使用 FFmpeg 将 PCM 转码为 MP3
             Process ffmpegProcess = getFfmpegProcess(pcmFilePath, mp3FilePath);
@@ -69,7 +69,7 @@ public class AudioUtils {
                 log.error("ffmpeg conversion failed or timed out. error: {}", ffmpegOutput);
                 return null;
             }
-            log.info("ffmpeg conversion to MP3 successfully.");
+            log.debug("ffmpeg conversion to MP3 successfully.");
 
             // 4. 上传oss
             String fileName = mp3FilePath.getFileName().toString();

+ 14 - 0
fs-qw-api-msg/src/main/java/com/fs/framework/service/TokenService.java

@@ -82,6 +82,20 @@ public class TokenService
         return null;
     }
 
+    /**
+     * 获取用户身份信息
+     *
+     * @return 用户信息
+     */
+    public LoginUser getLoginUser(String token) {
+        Claims claims = parseToken(token);
+        // 解析对应的权限以及用户信息
+        String uuid = (String) claims.get(Constants.COMPANY_LOGIN_USER_KEY);
+        String userKey = getTokenKey(uuid);
+        return redisCache.getCacheObject(userKey);
+
+    }
+
     /**
      * 设置用户身份信息
      */

+ 6 - 1
fs-service/src/main/java/com/fs/fastGpt/service/impl/AiHookServiceImpl.java

@@ -2104,6 +2104,11 @@ public class AiHookServiceImpl implements AiHookService {
             return null;
         }
 
+        if (qwUser.getCompanyUserId() == null) {
+            log.warn("企微用户未归属销售 qwUserId: {}", qwUserId);
+            return null;
+        }
+
         // 获取发送人
         QwImUserDTO sender = getUserByVid(senderVid, uuid, serverId, qwUser.getCorpId(), qwUser.getQwUserId());
         if (Objects.isNull(sender)) {
@@ -2168,7 +2173,7 @@ public class AiHookServiceImpl implements AiHookService {
         qwFromUser.setAvatar(sender.getAvatar());
         qwFromUser.setDisplayName(sender.getUserName());
 
-        listVO.setCompanyId(qwUser.getCompanyId());
+        listVO.setCompanyUserId(qwUser.getCompanyUserId());
         String type = "text";
         MsgType messageType = MsgType.getMsgType(msgType);
         if (Objects.nonNull(messageType)){

+ 1 - 4
fs-service/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java

@@ -1,8 +1,6 @@
 package com.fs.qw.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.fs.common.annotation.DataSource;
-import com.fs.common.enums.DataSourceType;
 import com.fs.fastGpt.domain.FastgptChatArtificialWords;
 import com.fs.qw.domain.QwExternalContact;
 import com.fs.qw.domain.QwUserDelLossLog;
@@ -14,7 +12,6 @@ import com.fs.qw.result.QwExternalContactVo;
 import com.fs.qw.vo.*;
 import com.fs.qw.vo.newvo.ExternalContactListVO;
 import com.fs.qw.vo.newvo.ExternalContactNumVO;
-import com.fs.qw.vo.sidebar.ExternalContactQwUserVO;
 import com.fs.qwApi.param.QwExternalContactHParam;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
@@ -546,7 +543,7 @@ public interface QwExternalContactMapper extends BaseMapper<QwExternalContact> {
     /**
      * 根据企微用户ID查询联系人列表
      */
-    List<QwContactVO> getContactListByQwUserId(@Param("qwUserId") Long qwUserId);
+    List<QwContactVO> getContactListByQwUserId(@Param("qwUserId") Long qwUserId, @Param("name") String name);
 
     /**
      * 查询群组指定企微关联外部联系人

+ 1 - 1
fs-service/src/main/java/com/fs/qw/mapper/QwGroupChatMapper.java

@@ -133,5 +133,5 @@ public interface QwGroupChatMapper
     /**
      * 根据企微用户ID查询群组列表
      */
-    List<QwContactVO> getGroupListByQwUserId(@Param("qwUserId") Long qwUserId);
+    List<QwContactVO> getGroupListByQwUserId(@Param("qwUserId") Long qwUserId, @Param("name") String name);
 }

+ 4 - 0
fs-service/src/main/java/com/fs/qw/mapper/QwTagMapper.java

@@ -105,4 +105,8 @@ public interface QwTagMapper
             "</script>")
     List<ExternalContactTagVO> selectQwTagListByTagIds(@Param("tagIds") List<String> tagIds);
 
+    /**
+     * 根据tagIds查询标签
+     */
+    List<QwTagVO> selectQwTagVOListByTagIds(List<String> tagIds);
 }

+ 1 - 1
fs-service/src/main/java/com/fs/qw/mapper/QwUserMapper.java

@@ -447,6 +447,6 @@ public interface QwUserMapper extends BaseMapper<QwUser>
      */
     List<QwUserVO> selectQwUserVoListByCompanyUserId(@Param("companyUserId") Long companyUserId);
 
-    @Select("select * from qw_user where vid = #{vid}")
+    @Select("select * from qw_user where vid = #{vid} order by id desc limit 1")
     QwUser selectQwUserByVid(@Param("vid") Long vid);
 }

+ 2 - 2
fs-service/src/main/java/com/fs/qw/service/IQwMsgService.java

@@ -99,12 +99,12 @@ public interface IQwMsgService extends IService<QwMsg>
     /**
      * 根据企微用户ID查询联系人列表
      */
-    List<QwContactVO> contactListByQwUserId(Long qwUserId);
+    List<QwContactVO> contactListByQwUserId(Long qwUserId, String name);
 
     /**
      * 根据企微用户ID查询群组列表
      */
-    List<QwContactVO> groupListByQwUserId(Long qwUserId);
+    List<QwContactVO> groupListByQwUserId(Long qwUserId, String name);
 
     /**
      * 获取会话ID

+ 5 - 0
fs-service/src/main/java/com/fs/qw/service/IQwTagService.java

@@ -72,4 +72,9 @@ public interface IQwTagService
     public List<String> selectQwTagListByTagIds(QwTagSearchParam param);
 
     List<QwTagVO> getTagListByUserId(ContactTagListParam param);
+
+    /**
+     * 根据tagIds查询标签
+     */
+    List<QwTagVO> selectQwTagVOListByTagIds(List<String> tagIds);
 }

+ 4 - 4
fs-service/src/main/java/com/fs/qw/service/impl/QwMsgServiceImpl.java

@@ -595,16 +595,16 @@ public class QwMsgServiceImpl extends ServiceImpl<QwMsgMapper, QwMsg> implements
      * 根据企微用户ID查询联系人列表
      */
     @Override
-    public List<QwContactVO> contactListByQwUserId(Long qwUserId) {
-        return externalContactMapper.getContactListByQwUserId(qwUserId);
+    public List<QwContactVO> contactListByQwUserId(Long qwUserId, String name) {
+        return externalContactMapper.getContactListByQwUserId(qwUserId, name);
     }
 
     /**
      * 根据企微用户ID查询群组列表
      */
     @Override
-    public List<QwContactVO> groupListByQwUserId(Long qwUserId) {
-        return groupChatMapper.getGroupListByQwUserId(qwUserId);
+    public List<QwContactVO> groupListByQwUserId(Long qwUserId, String name) {
+        return groupChatMapper.getGroupListByQwUserId(qwUserId, name);
     }
 
     /**

+ 8 - 0
fs-service/src/main/java/com/fs/qw/service/impl/QwTagServiceImpl.java

@@ -208,4 +208,12 @@ public class QwTagServiceImpl implements IQwTagService
         }
         return qwTagMapper.selectTagListByUserId(param, keywords);
     }
+
+    /**
+     * 根据tagIds查询标签
+     */
+    @Override
+    public List<QwTagVO> selectQwTagVOListByTagIds(List<String> tagIds) {
+        return qwTagMapper.selectQwTagVOListByTagIds(tagIds);
+    }
 }

+ 1 - 1
fs-service/src/main/java/com/fs/qw/vo/QwMessageListVO.java

@@ -22,7 +22,7 @@ public class QwMessageListVO {
     private QWFromUser fromUser;
     private String appKey;
     @JSONField(serialize = false)
-    private Long companyId;
+    private Long companyUserId;
     private String extId;
 
     //获取fromUser

+ 7 - 7
fs-service/src/main/java/com/fs/wxwork/utils/WxWorkHttpUtil.java

@@ -260,20 +260,20 @@ public class WxWorkHttpUtil {
      * @param headers 请求头
      */
     private static void logRequest(String method, String url, Map<String, Object> params, String body, Map<String, String> headers) {
-        logger.info("发送请求: {} {}", method, url);
+        logger.debug("发送请求: {} {}", method, url);
 
         if (MapUtil.isNotEmpty(params)) {
-            logger.info("请求参数: {}", JSONUtil.toJsonStr(params));
+            logger.debug("请求参数: {}", JSONUtil.toJsonStr(params));
         }
 
         if (body != null && !body.isEmpty()) {
             // 如果是较长的JSON,可能需要截断
             String logBody = body.length() > 1000 ? body.substring(0, 1000) + "... (截断)" : body;
-            logger.info("请求体: {}", logBody);
+            logger.debug("请求体: {}", logBody);
         }
 
         if (MapUtil.isNotEmpty(headers)) {
-            logger.info("请求头: {}", JSONUtil.toJsonStr(headers));
+            logger.debug("请求头: {}", JSONUtil.toJsonStr(headers));
         }
     }
 
@@ -283,19 +283,19 @@ public class WxWorkHttpUtil {
      * @param responseBody 响应体
      */
     private static void logResponse(HttpResponse response, String responseBody) {
-        logger.info("响应状态码: {}", response.getStatus());
+        logger.debug("响应状态码: {}", response.getStatus());
 
         // 记录响应头
         Map<String, List<String>> headers = response.headers();
         if (MapUtil.isNotEmpty(headers)) {
-            logger.info("响应头: {}", JSONUtil.toJsonStr(headers));
+            logger.debug("响应头: {}", JSONUtil.toJsonStr(headers));
         }
 
         // 记录响应体,但限制大小以防止日志过大
         if (responseBody != null && !responseBody.isEmpty()) {
             String logBody = responseBody.length() > 1000 ?
                     responseBody.substring(0, 1000) + "... (截断)" : responseBody;
-            logger.info("响应体: {}", logBody);
+            logger.debug("响应体: {}", logBody);
         }
     }
 }

+ 1 - 1
fs-service/src/main/resources/application-dev.yml

@@ -176,7 +176,7 @@ cloud_host:
 headerImg:
     imgUrl: https://hzyy.obs.cn-north-4.myhuaweicloud.com/fs/20250616/1750067609692.png
 ipad:
-    ipadUrl: http://139.159.133.223:8667
+    ipadUrl: https://contrastably-magnetooptic-gladys.ngrok-free.dev
     aiApi: http://1.95.196.10:3000/api
 wx_miniapp_temp:
     pay_order_temp_id:

+ 1 - 1
fs-service/src/main/resources/application-druid-jnmy-test.yml

@@ -41,7 +41,7 @@ spring:
             druid:
                 # 主库数据源
                 master:
-                    url: jdbc:mysql://120.46.174.121:2345/fs_his_test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    url: jdbc:mysql://120.46.174.121:2345/fs_his?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
                     username: root
                     password: Ylrztek250218!3@.
                 # 从库数据源

+ 3 - 0
fs-service/src/main/resources/mapper/qw/QwExternalContactMapper.xml

@@ -816,6 +816,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             from qw_msg
         ) qm on qm.session_id = qs.session_id and qm.rn = 1
         where qec.qw_user_id = #{qwUserId}
+        <if test="name != null and name != ''">
+            and qec.name like concat('%', #{name}, '%')
+        </if>
     </select>
 
     <select id="selectGroupContactByChatIdAndQwUserId" resultType="com.fs.qw.domain.QwExternalContact">

+ 3 - 0
fs-service/src/main/resources/mapper/qw/QwGroupChatMapper.xml

@@ -232,5 +232,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             from qw_msg
         ) qm on qm.session_id = qs.session_id and qm.rn = 1
         where qu.id = #{qwUserId}
+        <if test="name != null and name != ''">
+            and qgc.name like concat('%', #{name}, '%')
+        </if>
     </select>
 </mapper>

+ 8 - 0
fs-service/src/main/resources/mapper/qw/QwTagMapper.xml

@@ -124,4 +124,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <delete id="deleteQwTagByGroupId" parameterType="String">
         delete from qw_tag where group_id =#{id}
     </delete>
+
+    <select id="selectQwTagVOListByTagIds" resultType="com.fs.qw.vo.QwTagVO">
+        select * from qw_tag
+        where tag_id in
+        <foreach collection="ids" item="tagId" open="(" separator="," close=")">
+            #{tagId}
+        </foreach>
+    </select>
 </mapper>