|
|
@@ -9,6 +9,7 @@ import com.fasterxml.jackson.core.type.TypeReference;
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
import com.fs.common.core.domain.R;
|
|
|
import com.fs.common.core.domain.entity.SysDictData;
|
|
|
+import com.fs.common.core.redis.RedisCache;
|
|
|
import com.fs.common.utils.PubFun;
|
|
|
import com.fs.common.utils.StringUtils;
|
|
|
import com.fs.common.utils.spring.SpringUtils;
|
|
|
@@ -85,6 +86,27 @@ public class GeneralCustomerEntryServiceImpl implements IGeneralCustomerEntrySer
|
|
|
CrmCustomerPropertyServiceImpl crmCustomerPropertyService;
|
|
|
@Autowired
|
|
|
EasyCallMapper easyCallMapper;
|
|
|
+ @Autowired
|
|
|
+ private RedisCache redisCache2;
|
|
|
+
|
|
|
+ /** 呼入回调 chatContent(对话内容)重试队列 Redis key 前缀,value 为已重试次数 */
|
|
|
+ private static final String INBOUND_CHAT_CONTENT_RETRY_KEY = "inbound:chat:retry:";
|
|
|
+ /** chatContent 对话内容等待重试最大次数(每次间隔约30秒,最多等待 5*30=150秒) */
|
|
|
+ private static final int INBOUND_CHAT_CONTENT_MAX_RETRY = 5;
|
|
|
+ /** chatContent 每次重试等待时长(毫秒) */
|
|
|
+ private static final long INBOUND_CHAT_CONTENT_RETRY_INTERVAL_MS = 30000L;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断 chatContent 对话内容是否为空(null、空字符串、空数组 "[]" 均视为无对话内容)
|
|
|
+ */
|
|
|
+ private boolean isChatContentEmpty(String chatContent) {
|
|
|
+ if (StringUtils.isBlank(chatContent)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ String trimmed = chatContent.trim();
|
|
|
+ return "[]".equals(trimmed);
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 录入客户
|
|
|
*
|
|
|
@@ -123,7 +145,7 @@ public class GeneralCustomerEntryServiceImpl implements IGeneralCustomerEntrySer
|
|
|
}
|
|
|
|
|
|
private static final String TRADE_TYPE = "trade_type";
|
|
|
- @Value("${crm.customer.ai.key:mygpt-oPG2ifhnq0ODGioOBMUvMfOZGrtCykqw3oMeYLchdUDK5He6iNiactrhFWA0sID}")
|
|
|
+ @Value("${crm.customer.ai.Key:mygpt-iTUua2CHVd4WGrBbQQGl1HHjyyBAD1KuXARsxHj5eHpLYv5CfnOh8iwVU}")
|
|
|
private String appKey;
|
|
|
private List<CrmCustomerAiTagVo> getAiTags(String chatRecord) throws JsonProcessingException {
|
|
|
Map<String, Object> requestParam = new HashMap<>();
|
|
|
@@ -298,11 +320,13 @@ public class GeneralCustomerEntryServiceImpl implements IGeneralCustomerEntrySer
|
|
|
property.setCreateTime(new Date());
|
|
|
return property;
|
|
|
}).collect(Collectors.toList());
|
|
|
- crmCustomerPropertyService.remove(new LambdaQueryWrapper<CrmCustomerProperty>()
|
|
|
- .eq(CrmCustomerProperty::getCustomerId, data.getCustomerId())
|
|
|
- .in(CrmCustomerProperty::getPropertyId, ids)
|
|
|
- );
|
|
|
- crmCustomerPropertyService.saveBatch(propertyList);
|
|
|
+ if(null != ids && !ids.isEmpty()){
|
|
|
+ crmCustomerPropertyService.remove(new LambdaQueryWrapper<CrmCustomerProperty>()
|
|
|
+ .eq(CrmCustomerProperty::getCustomerId, data.getCustomerId())
|
|
|
+ .in(CrmCustomerProperty::getPropertyId, ids)
|
|
|
+ );
|
|
|
+ crmCustomerPropertyService.saveBatch(propertyList);
|
|
|
+ }
|
|
|
} catch (JsonProcessingException e) {
|
|
|
throw new RuntimeException(e);
|
|
|
}
|
|
|
@@ -415,21 +439,96 @@ public class GeneralCustomerEntryServiceImpl implements IGeneralCustomerEntrySer
|
|
|
* @param param
|
|
|
*/
|
|
|
@Override
|
|
|
+ @Async("cidWorkFlowExecutor")
|
|
|
public void inboundCallback(InboundCallbackParam param){
|
|
|
try {
|
|
|
Thread.sleep(5000L);
|
|
|
} catch (InterruptedException e) {
|
|
|
+ Thread.currentThread().interrupt();
|
|
|
throw new RuntimeException(e);
|
|
|
}
|
|
|
if (param == null || StringUtils.isBlank(param.getUuid())){
|
|
|
return;
|
|
|
}
|
|
|
InboundCallInfo info = easyCallMapper.selectInboundCallbackInfoByUuid(param.getUuid());
|
|
|
+ // chatContent(对话内容)由对方异步写入,回调时可能尚未赋值,进入延迟重试队列等待
|
|
|
+ if (info == null || isChatContentEmpty(info.getChatContent())) {
|
|
|
+ String retryKey = INBOUND_CHAT_CONTENT_RETRY_KEY + param.getUuid();
|
|
|
+ Integer retryCount = redisCache2.getCacheObject(retryKey);
|
|
|
+ if (retryCount == null) {
|
|
|
+ retryCount = 0;
|
|
|
+ }
|
|
|
+ if (retryCount < INBOUND_CHAT_CONTENT_MAX_RETRY) {
|
|
|
+ redisCache2.setCacheObject(retryKey, retryCount + 1, 10, java.util.concurrent.TimeUnit.MINUTES);
|
|
|
+ log.info("呼入回调chatContent对话内容暂未写入,uuid={},第{}次放入延迟重试队列", param.getUuid(), retryCount + 1);
|
|
|
+ doRetryInboundCallback(param, retryCount + 1);
|
|
|
+ } else {
|
|
|
+ // 超过最大重试次数,以 chatContent 为空兜底继续处理
|
|
|
+ log.warn("呼入回调chatContent对话内容在{}次重试后仍为空,uuid={},以对话为空兜底处理", INBOUND_CHAT_CONTENT_MAX_RETRY, param.getUuid());
|
|
|
+ redisCache2.deleteObject(retryKey);
|
|
|
+ doHandleInboundCallback(param, info);
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // chatContent 已有值,直接正常处理
|
|
|
+ redisCache2.deleteObject(INBOUND_CHAT_CONTENT_RETRY_KEY + param.getUuid());
|
|
|
+ doHandleInboundCallback(param, info);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 延迟重试处理呼入回调(等待 chatContent 对话内容异步写入完成)
|
|
|
+ * 每次重试前等待 {@link #INBOUND_CHAT_CONTENT_RETRY_INTERVAL_MS} 毫秒后重新拉取数据
|
|
|
+ */
|
|
|
+ @Async("cidWorkFlowExecutor")
|
|
|
+ public void doRetryInboundCallback(InboundCallbackParam param, int currentRetry) {
|
|
|
+ try {
|
|
|
+ Thread.sleep(INBOUND_CHAT_CONTENT_RETRY_INTERVAL_MS);
|
|
|
+ } catch (InterruptedException e) {
|
|
|
+ Thread.currentThread().interrupt();
|
|
|
+ log.error("呼入回调chatContent重试等待被中断, uuid={}", param.getUuid());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ log.info("呼入回调chatContent重试第{}次开始, uuid={}", currentRetry, param.getUuid());
|
|
|
+ InboundCallInfo info = easyCallMapper.selectInboundCallbackInfoByUuid(param.getUuid());
|
|
|
+ if (info == null || isChatContentEmpty(info.getChatContent())) {
|
|
|
+ // chatContent 仍为空,继续判断是否还有剩余重试次数
|
|
|
+ String retryKey = INBOUND_CHAT_CONTENT_RETRY_KEY + param.getUuid();
|
|
|
+ Integer retryCount = redisCache2.getCacheObject(retryKey);
|
|
|
+ if (retryCount == null) {
|
|
|
+ retryCount = currentRetry;
|
|
|
+ }
|
|
|
+ if (retryCount < INBOUND_CHAT_CONTENT_MAX_RETRY) {
|
|
|
+ redisCache2.setCacheObject(retryKey, retryCount + 1, 10, java.util.concurrent.TimeUnit.MINUTES);
|
|
|
+ log.info("呼入回调chatContent对话内容仍未写入,uuid={},第{}次继续延迟重试", param.getUuid(), retryCount + 1);
|
|
|
+ doRetryInboundCallback(param, retryCount + 1);
|
|
|
+ } else {
|
|
|
+ log.error("呼入回调chatContent对话内容在{}次重试后仍为空,uuid={},以对话为空兜底处理", INBOUND_CHAT_CONTENT_MAX_RETRY, param.getUuid());
|
|
|
+ redisCache2.deleteObject(retryKey);
|
|
|
+ doHandleInboundCallback(param, info);
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // chatContent 已写入完成,正常处理
|
|
|
+ log.info("呼入回调chatContent重试第{}次成功获取到对话内容,uuid={}", currentRetry, param.getUuid());
|
|
|
+ redisCache2.deleteObject(INBOUND_CHAT_CONTENT_RETRY_KEY + param.getUuid());
|
|
|
+ doHandleInboundCallback(param, info);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 执行呼入回调核心业务处理(组装客户入参并录入)
|
|
|
+ * 供 {@link #inboundCallback} 和重试逻辑统一调用
|
|
|
+ */
|
|
|
+ private void doHandleInboundCallback(InboundCallbackParam param, InboundCallInfo info) {
|
|
|
+ if (info == null) {
|
|
|
+ log.error("呼入回调信息未查询到结果, uuid={}", param.getUuid());
|
|
|
+ return;
|
|
|
+ }
|
|
|
EntryCustomerParam entry = new EntryCustomerParam();
|
|
|
entry.setTraceId(param.getUuid());
|
|
|
entry.setCompanyId(info.getFsCompanyId());
|
|
|
entry.setSceneType(info.getFsSceneType());
|
|
|
entry.setMobile(info.getCaller());
|
|
|
+ entry.setDialogue(info.getChatContent());
|
|
|
entryCustomer(entry);
|
|
|
}
|
|
|
|