|
@@ -14,7 +14,6 @@ import com.fs.qw.service.ICorporateWeChatSpaceService;
|
|
|
import com.fs.qw.utils.WeChatSpaceUtil;
|
|
import com.fs.qw.utils.WeChatSpaceUtil;
|
|
|
import com.fs.qw.vo.QwSessionConfigVo;
|
|
import com.fs.qw.vo.QwSessionConfigVo;
|
|
|
import com.fs.qw.vo.SearchResultVO;
|
|
import com.fs.qw.vo.SearchResultVO;
|
|
|
-import com.fs.system.service.ISysConfigService;
|
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
@@ -28,7 +27,6 @@ import java.time.format.DateTimeFormatter;
|
|
|
import java.util.ArrayList;
|
|
import java.util.ArrayList;
|
|
|
import java.util.List;
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
import java.util.Map;
|
|
|
-import java.util.Objects;
|
|
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
import java.util.function.Function;
|
|
import java.util.function.Function;
|
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Collectors;
|
|
@@ -38,11 +36,9 @@ import java.util.stream.Collectors;
|
|
|
@RequiredArgsConstructor
|
|
@RequiredArgsConstructor
|
|
|
public class ICorporateWeChatSpaceServiceImpl implements ICorporateWeChatSpaceService {
|
|
public class ICorporateWeChatSpaceServiceImpl implements ICorporateWeChatSpaceService {
|
|
|
|
|
|
|
|
- @Autowired
|
|
|
|
|
- private ISysConfigService sysConfigService;
|
|
|
|
|
-
|
|
|
|
|
@Autowired
|
|
@Autowired
|
|
|
private QwConversationMessageMapper messageMapper;
|
|
private QwConversationMessageMapper messageMapper;
|
|
|
|
|
+
|
|
|
@Autowired
|
|
@Autowired
|
|
|
private QwConversationParticipantMapper participantMapper;
|
|
private QwConversationParticipantMapper participantMapper;
|
|
|
|
|
|
|
@@ -52,13 +48,13 @@ public class ICorporateWeChatSpaceServiceImpl implements ICorporateWeChatSpaceSe
|
|
|
@Autowired
|
|
@Autowired
|
|
|
private QwProgramInvoker qwProgramInvoker;
|
|
private QwProgramInvoker qwProgramInvoker;
|
|
|
|
|
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private ConversationSyncService conversationSyncService;
|
|
|
|
|
+
|
|
|
private final RestTemplate restTemplate = new RestTemplate();
|
|
private final RestTemplate restTemplate = new RestTemplate();
|
|
|
|
|
|
|
|
private final ConcurrentHashMap<String, String> consumedCodes = new ConcurrentHashMap<>();
|
|
private final ConcurrentHashMap<String, String> consumedCodes = new ConcurrentHashMap<>();
|
|
|
|
|
|
|
|
- // 系统配置缓存前缀
|
|
|
|
|
- private final static String CONFIG_KEY = "qw.sessionConfig";
|
|
|
|
|
-
|
|
|
|
|
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
|
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
|
|
|
|
|
|
|
// =============== 查询数据库保存的会话 ===============
|
|
// =============== 查询数据库保存的会话 ===============
|
|
@@ -124,7 +120,7 @@ public class ICorporateWeChatSpaceServiceImpl implements ICorporateWeChatSpaceSe
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
public JSONObject getAgentConfigSignature(String url,String corpid) {
|
|
public JSONObject getAgentConfigSignature(String url,String corpid) {
|
|
|
- QwSessionConfigVo qwSessionConfig = getQwSessionConfigByCorpid(corpid);
|
|
|
|
|
|
|
+ QwSessionConfigVo qwSessionConfig = weChatSpaceUtil.getQwSessionConfigByCorpid(corpid);
|
|
|
return weChatSpaceUtil.generateAgentConfigSignature(qwSessionConfig.getCorpid(), qwSessionConfig.getAgentid(), url);
|
|
return weChatSpaceUtil.generateAgentConfigSignature(qwSessionConfig.getCorpid(), qwSessionConfig.getAgentid(), url);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -141,7 +137,7 @@ public class ICorporateWeChatSpaceServiceImpl implements ICorporateWeChatSpaceSe
|
|
|
result.put("errmsg", "code already used");
|
|
result.put("errmsg", "code already used");
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
|
- QwSessionConfigVo qwSessionConfig = getQwSessionConfigByCorpid(corpid);
|
|
|
|
|
|
|
+ QwSessionConfigVo qwSessionConfig = weChatSpaceUtil.getQwSessionConfigByCorpid(corpid);
|
|
|
try {
|
|
try {
|
|
|
String accessToken = weChatSpaceUtil.getAccessToken(qwSessionConfig.getCorpid());
|
|
String accessToken = weChatSpaceUtil.getAccessToken(qwSessionConfig.getCorpid());
|
|
|
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token="
|
|
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token="
|
|
@@ -168,42 +164,11 @@ public class ICorporateWeChatSpaceServiceImpl implements ICorporateWeChatSpaceSe
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /**
|
|
|
|
|
- * 获取所有企业微信专区会话配置列表
|
|
|
|
|
- */
|
|
|
|
|
- @Override
|
|
|
|
|
- public List<QwSessionConfigVo> getQwSessionConfigList() {
|
|
|
|
|
- String json = sysConfigService.selectConfigByKey(CONFIG_KEY);
|
|
|
|
|
- if (StringUtils.isBlank(json)) {
|
|
|
|
|
- log.error("未找到企微专区配置, key:{}", CONFIG_KEY);
|
|
|
|
|
- throw new CustomException("未找到企微专区配置");
|
|
|
|
|
- }
|
|
|
|
|
- try {
|
|
|
|
|
- // 直接解析为 List
|
|
|
|
|
- return JSON.parseArray(json, QwSessionConfigVo.class);
|
|
|
|
|
- } catch (Exception e) {
|
|
|
|
|
- log.error("解析企微专区配置失败", e);
|
|
|
|
|
- throw new CustomException("企微专区配置格式错误");
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 根据企业ID获取单个配置
|
|
|
|
|
- */
|
|
|
|
|
- @Override
|
|
|
|
|
- public QwSessionConfigVo getQwSessionConfigByCorpid(String corpid) {
|
|
|
|
|
- List<QwSessionConfigVo> all = getQwSessionConfigList();
|
|
|
|
|
- return all.stream()
|
|
|
|
|
- .filter(config -> config.getCorpid().equals(corpid))
|
|
|
|
|
- .findFirst()
|
|
|
|
|
- .orElseThrow(() -> new CustomException("未找到corpid为 " + corpid + " 的配置"));
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
// ========== 关键词搜索 ==========
|
|
// ========== 关键词搜索 ==========
|
|
|
@Override
|
|
@Override
|
|
|
public SearchResultVO searchMsg(SearchMsgRequest request) {
|
|
public SearchResultVO searchMsg(SearchMsgRequest request) {
|
|
|
// 1. 获取企业配置,并找到 search_msg 能力ID
|
|
// 1. 获取企业配置,并找到 search_msg 能力ID
|
|
|
- QwSessionConfigVo config = getQwSessionConfigByCorpid(request.getCorpId());
|
|
|
|
|
|
|
+ QwSessionConfigVo config = weChatSpaceUtil.getQwSessionConfigByCorpid(request.getCorpId());
|
|
|
String abilityId = config.getAbilityIds().stream()
|
|
String abilityId = config.getAbilityIds().stream()
|
|
|
.filter(item -> "invokeSearchMsg".equals(item.getKey()))
|
|
.filter(item -> "invokeSearchMsg".equals(item.getKey()))
|
|
|
.map(QwSessionConfigVo.AbilityItem::getValue)
|
|
.map(QwSessionConfigVo.AbilityItem::getValue)
|
|
@@ -301,6 +266,54 @@ public class ICorporateWeChatSpaceServiceImpl implements ICorporateWeChatSpaceSe
|
|
|
return new SearchResultVO(resultList, hasMore, nextCursor);
|
|
return new SearchResultVO(resultList, hasMore, nextCursor);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public void syncConversationsByNotifyId(String notifyId, String corpId) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ QwSessionConfigVo config = weChatSpaceUtil.getQwSessionConfigByCorpid(corpId);
|
|
|
|
|
+ String abilityId = config.getAbilityIds().stream()
|
|
|
|
|
+ .filter(item -> "notifyId".equals(item.getKey()))
|
|
|
|
|
+ .map(QwSessionConfigVo.AbilityItem::getValue)
|
|
|
|
|
+ .findFirst()
|
|
|
|
|
+ .orElseThrow(() -> new CustomException("未配置 notifyId 能力,请在企微后台添加"));
|
|
|
|
|
+
|
|
|
|
|
+ // 构造请求数据,将notify_id传给专区程序
|
|
|
|
|
+ JSONObject requestData = new JSONObject();
|
|
|
|
|
+ requestData.put("notify_id", notifyId);
|
|
|
|
|
+
|
|
|
|
|
+ // 调用 sync_call_program
|
|
|
|
|
+ JSONObject response = qwProgramInvoker.callProgram(corpId, config.getProgramId(), abilityId, requestData);
|
|
|
|
|
+
|
|
|
|
|
+ if (response != null && response.getIntValue("errcode") == 0) {
|
|
|
|
|
+ String responseDataStr = response.getString("response_data");
|
|
|
|
|
+ JSONObject responseData = JSON.parseObject(responseDataStr);
|
|
|
|
|
+ String token = responseData.getString("token");
|
|
|
|
|
+ String cursor = responseData.getString("cursor");
|
|
|
|
|
+
|
|
|
|
|
+ // 立刻使用获取到的 token 进行消息同步,token 有效期只有10分钟
|
|
|
|
|
+ if (StringUtils.isNotBlank(token)) {
|
|
|
|
|
+ syncConversationsWithToken(corpId, token, cursor);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("Failed to sync by notify_id: {}", notifyId, e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public void syncConversationsWithToken(String corpId, String token, String cursor) {
|
|
|
|
|
+ // 获取配置等逻辑...
|
|
|
|
|
+ JSONObject requestData = new JSONObject();
|
|
|
|
|
+ if (StringUtils.isNotBlank(cursor)) {
|
|
|
|
|
+ requestData.put("cursor", cursor);
|
|
|
|
|
+ }
|
|
|
|
|
+ requestData.put("token", token);
|
|
|
|
|
+ requestData.put("limit", 1000);
|
|
|
|
|
+
|
|
|
|
|
+ // 调用 qwProgramInvoker.callProgram,传入 "invoke_sync_msg" 能力ID
|
|
|
|
|
+ // ... 处理返回的消息列表并入库
|
|
|
|
|
+ conversationSyncService.syncConversationsForCorp(corpId, token);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 构建前端需要的消息JSON
|
|
* 构建前端需要的消息JSON
|
|
|
*/
|
|
*/
|
|
@@ -339,82 +352,4 @@ public class ICorporateWeChatSpaceServiceImpl implements ICorporateWeChatSpaceSe
|
|
|
}
|
|
}
|
|
|
return item;
|
|
return item;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-// private JSONArray processMessages(JSONArray msgList, String customerId, String staffUserId, QwSessionConfigVo qwConfig) {
|
|
|
|
|
-// return msgList.parallelStream()
|
|
|
|
|
-// .map(obj -> (JSONObject) obj)
|
|
|
|
|
-// .filter(msg -> isMessageRelatedToUsers(msg, customerId, staffUserId))
|
|
|
|
|
-// .map(msg -> decryptAndFormatMessage(msg, qwConfig))
|
|
|
|
|
-// .filter(Objects::nonNull)
|
|
|
|
|
-// .collect(Collectors.toCollection(JSONArray::new));
|
|
|
|
|
-// }
|
|
|
|
|
-//
|
|
|
|
|
-// private boolean isMessageRelatedToUsers(JSONObject msg, String customerId, String staffUserId) {
|
|
|
|
|
-// // 如果都为空,则不过滤,返回全部
|
|
|
|
|
-// if ((customerId == null || customerId.isEmpty()) && (staffUserId == null || staffUserId.isEmpty())) {
|
|
|
|
|
-// return true;
|
|
|
|
|
-// }
|
|
|
|
|
-// boolean hasCustomer = (customerId == null || customerId.isEmpty());
|
|
|
|
|
-// boolean hasStaff = (staffUserId == null || staffUserId.isEmpty());
|
|
|
|
|
-//
|
|
|
|
|
-// JSONObject sender = msg.getJSONObject("sender");
|
|
|
|
|
-// JSONArray receivers = msg.getJSONArray("receiver_list");
|
|
|
|
|
-//
|
|
|
|
|
-// // 检查发送者
|
|
|
|
|
-// if (sender != null) {
|
|
|
|
|
-// String senderId = sender.getString("id");
|
|
|
|
|
-// int senderType = sender.getIntValue("type");
|
|
|
|
|
-// if (!hasCustomer && senderType == 2 && customerId.equals(senderId)) hasCustomer = true;
|
|
|
|
|
-// if (!hasStaff && senderType == 1 && staffUserId.equals(senderId)) hasStaff = true;
|
|
|
|
|
-// }
|
|
|
|
|
-// if (hasCustomer && hasStaff) return true;
|
|
|
|
|
-//
|
|
|
|
|
-// // 检查接收者
|
|
|
|
|
-// if (receivers != null) {
|
|
|
|
|
-// for (int i = 0; i < receivers.size(); i++) {
|
|
|
|
|
-// JSONObject recv = receivers.getJSONObject(i);
|
|
|
|
|
-// String recvId = recv.getString("id");
|
|
|
|
|
-// int recvType = recv.getIntValue("type");
|
|
|
|
|
-// if (!hasCustomer && recvType == 2 && customerId.equals(recvId)) hasCustomer = true;
|
|
|
|
|
-// if (!hasStaff && recvType == 1 && staffUserId.equals(recvId)) hasStaff = true;
|
|
|
|
|
-// if (hasCustomer && hasStaff) return true;
|
|
|
|
|
-// }
|
|
|
|
|
-// }
|
|
|
|
|
-// return hasCustomer && hasStaff;
|
|
|
|
|
-// }
|
|
|
|
|
-//
|
|
|
|
|
-// private JSONObject decryptAndFormatMessage(JSONObject msg, QwSessionConfigVo qwConfig) {
|
|
|
|
|
-// JSONObject result = new JSONObject();
|
|
|
|
|
-// try {
|
|
|
|
|
-// JSONObject encryptInfo = msg.getJSONObject("service_encrypt_info");
|
|
|
|
|
-// if (encryptInfo == null) return null;
|
|
|
|
|
-// String encryptedKey = encryptInfo.getString("encrypted_secret_key");
|
|
|
|
|
-// if (encryptedKey == null) return null;
|
|
|
|
|
-//
|
|
|
|
|
-// // 解密得到 secretKey
|
|
|
|
|
-// String secretKey = WeChatSpaceDecryptUtil.decryptSecretKey(encryptedKey, qwConfig.getPrivateKey());
|
|
|
|
|
-//
|
|
|
|
|
-// // 复制需要返回的字段
|
|
|
|
|
-// result.put("msgid", msg.getString("msgid"));
|
|
|
|
|
-// result.put("secretKey", secretKey);
|
|
|
|
|
-// result.put("sender", msg.get("sender"));
|
|
|
|
|
-// result.put("receiver_list", msg.get("receiver_list"));
|
|
|
|
|
-// result.put("msgtype", msg.getInteger("msgtype"));
|
|
|
|
|
-//
|
|
|
|
|
-// Long sendTime = msg.getLong("send_time");
|
|
|
|
|
-// if (sendTime != null) {
|
|
|
|
|
-// String formattedTime = Instant.ofEpochSecond(sendTime)
|
|
|
|
|
-// .atZone(ZoneId.systemDefault())
|
|
|
|
|
-// .toLocalDateTime()
|
|
|
|
|
-// .format(DATE_TIME_FORMATTER);
|
|
|
|
|
-// result.put("send_time_str", formattedTime);
|
|
|
|
|
-// result.put("send_time", sendTime);
|
|
|
|
|
-// }
|
|
|
|
|
-// return result;
|
|
|
|
|
-// } catch (Exception e) {
|
|
|
|
|
-// log.error("解密消息失败, msgid: {}", msg.getString("msgid"), e);
|
|
|
|
|
-// return null;
|
|
|
|
|
-// }
|
|
|
|
|
-// }
|
|
|
|
|
}
|
|
}
|