|
|
@@ -33,6 +33,7 @@ import org.slf4j.Logger;
|
|
|
import org.springframework.beans.BeanUtils;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.context.annotation.Lazy;
|
|
|
+import org.springframework.scheduling.annotation.Async;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
import org.springframework.util.CollectionUtils;
|
|
|
@@ -538,42 +539,62 @@ public class MarketEmailsServiceImpl extends MyBaseServiceImpl<MarketEmailsMappe
|
|
|
return rows;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 生成邮件正文(调 AI 重写内容)并落库。
|
|
|
+ * <p>
|
|
|
+ * 一次调用 = 一次 AI 请求,耗时通常是秒级,一个 SOP 步骤下可能有几十上百封邮件,
|
|
|
+ * 所以这里必须是异步的:@Async 放在<strong>实现类方法</strong>上(放在接口方法上时,
|
|
|
+ * 一旦 bean 走 CGLIB 代理或与其它 AOP 代理叠加,匹配行为会随代理方式变化,不如放实现类稳妥)。
|
|
|
+ * <p>
|
|
|
+ * 异步线程的异常不会回到请求线程,因此内部自行兜住并记 error 日志,
|
|
|
+ * 避免失败后邮件一直停在旧内容上却没有任何痕迹。
|
|
|
+ */
|
|
|
@Override
|
|
|
+ @Async("tradeEmailExecutor")
|
|
|
public void updateEmailContent(MarketEmailsEntity marketEmailsEntity) {
|
|
|
- //通过客户id获取客户分析结果
|
|
|
- CustomerAnalysisResultEntity customerAnalysisResult = new CustomerAnalysisResultEntity();
|
|
|
- LambdaQueryWrapper<CustomerAnalysisResultEntity> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
- queryWrapper.eq(CustomerAnalysisResultEntity::getCustomerId, marketEmailsEntity.getCustomerId());
|
|
|
- queryWrapper.last("limit 1");
|
|
|
- customerAnalysisResult = customerAnalysisResultService.getOne(queryWrapper);
|
|
|
- String reasonSummary = "";
|
|
|
- if (customerAnalysisResult != null){
|
|
|
- reasonSummary = customerAnalysisResult.getReasonSummary();
|
|
|
- }
|
|
|
- Map sendMap = new HashMap();
|
|
|
- sendMap.put("email", marketEmailsEntity.getContent());
|
|
|
- sendMap.put("reason_summary", reasonSummary);
|
|
|
-
|
|
|
- ChatDTO chatDTO = new ChatDTO();
|
|
|
- chatDTO.setInputs(sendMap);
|
|
|
-// chatDTO.setQuery("send_time");
|
|
|
- chatDTO.setResponseMode("blocking");
|
|
|
- chatDTO.setAppId("app-CVPtLUPO0zd2SeGPvZv3Q4Ev");
|
|
|
- chatDTO.setUser("1");
|
|
|
-
|
|
|
- // 调取 AI 获取新的content
|
|
|
- Result<Object> r = workflowsService.AiWorkflows(chatDTO);
|
|
|
- if (r == null) {
|
|
|
- log.warn("无内容生成");
|
|
|
- return;
|
|
|
- }
|
|
|
- // 从结构化结果里取出 AI 真正输出的 text 字段(多个结束节点拼接)。
|
|
|
- // 不再做 inner.toString() 兜底——整串 Result 里有 files=[]/inputs=[] 等空数组
|
|
|
- // 会被误当成 AI 输出的 JSON 数组(如 [models]),污染解析。
|
|
|
- String text = extractCleanText(r.getResult());
|
|
|
- marketEmailsEntity.setContent(text);
|
|
|
+ log.info("[updateEmailContent] 提交异步生成,emailId={},customerId={},线程={}",
|
|
|
+ marketEmailsEntity.getId(), marketEmailsEntity.getCustomerId(), Thread.currentThread().getName());
|
|
|
+ try {
|
|
|
+ //通过客户id获取客户分析结果
|
|
|
+ CustomerAnalysisResultEntity customerAnalysisResult = new CustomerAnalysisResultEntity();
|
|
|
+ LambdaQueryWrapper<CustomerAnalysisResultEntity> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
+ queryWrapper.eq(CustomerAnalysisResultEntity::getCustomerId, marketEmailsEntity.getCustomerId());
|
|
|
+ queryWrapper.last("limit 1");
|
|
|
+ customerAnalysisResult = customerAnalysisResultService.getOne(queryWrapper);
|
|
|
+ String reasonSummary = "";
|
|
|
+ if (customerAnalysisResult != null) {
|
|
|
+ reasonSummary = customerAnalysisResult.getReasonSummary();
|
|
|
+ }
|
|
|
+ Map sendMap = new HashMap();
|
|
|
+ sendMap.put("email", marketEmailsEntity.getContent());
|
|
|
+ sendMap.put("reason_summary", reasonSummary);
|
|
|
+
|
|
|
+ ChatDTO chatDTO = new ChatDTO();
|
|
|
+ chatDTO.setInputs(sendMap);
|
|
|
+ chatDTO.setResponseMode("blocking");
|
|
|
+ chatDTO.setAppId("app-CVPtLUPO0zd2SeGPvZv3Q4Ev");
|
|
|
+ chatDTO.setUser("1");
|
|
|
|
|
|
- this.updateById(marketEmailsEntity);
|
|
|
+ // 调取 AI 获取新的content
|
|
|
+ Result<Object> r = workflowsService.AiWorkflows(chatDTO);
|
|
|
+ if (r == null) {
|
|
|
+ log.warn("无内容生成,emailId={}", marketEmailsEntity.getId());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 从结构化结果里取出 AI 真正输出的 text 字段(多个结束节点拼接)。
|
|
|
+ // 不再做 inner.toString() 兜底——整串 Result 里有 files=[]/inputs=[] 等空数组
|
|
|
+ // 会被误当成 AI 输出的 JSON 数组(如 [models]),污染解析。
|
|
|
+ String text = extractCleanText(r.getResult());
|
|
|
+ marketEmailsEntity.setContent(text);
|
|
|
+
|
|
|
+ this.updateById(marketEmailsEntity);
|
|
|
+ log.info("[updateEmailContent] 生成完成并已落库,emailId={},线程={}",
|
|
|
+ marketEmailsEntity.getId(), Thread.currentThread().getName());
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 异步线程:异常不会回到请求线程,这里必须自己兜住,否则失败将毫无痕迹
|
|
|
+ log.error("[updateEmailContent] 生成邮件内容失败,emailId={},customerId={}",
|
|
|
+ marketEmailsEntity.getId(), marketEmailsEntity.getCustomerId(), e);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|