百度外呼平台对接。

This commit is contained in:
haown 2024-08-30 17:11:03 +08:00
parent 43432e3d2b
commit 363d52d91e
16 changed files with 1009 additions and 6 deletions

View File

@ -107,7 +107,7 @@ token:
# 令牌有效期默认30分钟
expireTime: 30
# 请求拦截白名单
ant-matchers: /postDischarge/**,/testMobile/**,/postDischargeApplet/**
ant-matchers: /postDischarge/**,/testMobile/**,/postDischargeApplet/**,/api/**
## MyBatis-Plus配置
mybatis-plus:
@ -274,3 +274,18 @@ push-message-restrictions:
number: 1
#时间 单位,天
time: 2
# 爱医生平台对接
aifuv-robots:
# 接口地址
url: https://aifuv2cloud.aihealthx.cn/
# 密钥
secrectKey: lJayvsWsVUWDmWnNan1WzmH9OEOiwB
# 百度客悦·智能外呼平台
aiob:
# 请求地址
url: https://aiob-open.baidu.com/api
accessKey: b6d2d73cdf2d4d7ca2ccc84968accdb3
secretKey: 9262d2fe32544eaabce18331bd5ca2c5

View File

@ -0,0 +1,80 @@
package com.xinelu.manage.controller.aibo;
import com.xinelu.common.core.controller.BaseController;
import com.xinelu.common.core.domain.R;
import com.xinelu.manage.dto.aibo.CreateTaskDto;
import com.xinelu.manage.dto.aibo.TaskCallbackDto;
import com.xinelu.manage.service.aibo.IAIOBService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import javax.annotation.Resource;
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;
/**
* @description: 百度客悦·智能外呼平台接口
* @author: haown
* @create: 2024-08-22 09:50
**/
@Api(tags = "百度客悦·智能外呼平台接口")
@RestController
@RequestMapping("/api")
public class AIOBController extends BaseController {
@Resource
public IAIOBService aiobService;
/**
* @description 获取外呼平台accessToken
* @return 智能外呼平台 accessToken
* @Author haown
* @Date 2024-8-22 15:30
*/
@ApiOperation("获取accessToken")
@PostMapping("/getAccessToken")
public R<String> getAccessToken() {
return R.ok(aiobService.getAccessToken());
}
/**
* @description 创建任务
* @param createTaskDto 创建任务传输对象
* @return taskId 客悦·智能外呼平台任务唯一标识
* @Author haown
* @Date 2024-8-22 15:34
*/
@ApiOperation("创建任务")
@PostMapping("/createTask")
public R<String> createTask(CreateTaskDto createTaskDto) {
return R.ok(aiobService.createTask(createTaskDto));
}
/**
* @description 任务状态变更
* @param taskId 任务id
* @param taskStatus 修改状态 2执行中3已暂停4已完成如点击终止
* @return 状态码
* @Author haown
* @Date 2024-8-23 11:49
*/
@ApiOperation("任务状态变更")
@PostMapping("/updateTaskStatus")
public R<String> updateTaskStatus(String taskId, Integer taskStatus) {
int flag = aiobService.updateTaskStatus(taskId, taskStatus);
return flag > 0 ? R.ok() : R.fail();
}
/**
* @description 任务单通电话回调
* @param taskCallbackDto 回调数据类型 0-任务呼叫单通电话回调 1-号码组终态回调 2-任务状态变更回调 3-实时呼叫单通电话回调
* @return null
* @Author haown
* @Date 2024-8-29 11:15
*/
@ApiOperation("任务单通电话回调")
@PostMapping("/taskCallBack")
public R<String> taskCallBack(@RequestBody TaskCallbackDto taskCallbackDto) {
return R.ok(aiobService.taskCallBack(taskCallbackDto.getCallbackType(), taskCallbackDto.getData()));
}
}

View File

@ -145,6 +145,11 @@ public class ScriptInfo extends BaseEntity {
*/
private String robotPublishId;
/**
* 话术中用到的变量竖线隔开来源于字段标签表的key
*/
private String variables;
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)

View File

@ -0,0 +1,47 @@
package com.xinelu.manage.domain.scriptinfotaskinfo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @description: 百度外呼话术任务临时表
* @author: haown
* @create: 2024-08-30 14:29
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(value = "百度外呼话术任务临时表", description = "script_info_task_info")
public class ScriptInfoTaskInfo {
/**
* 主键
*/
private Long id;
/**
* 话术表主键
*/
private Long scriptInfoId;
/**
* 机器人ID
*/
private String robotId;
/**
* 任务标识
*/
private String taskId;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
}

View File

@ -0,0 +1,67 @@
package com.xinelu.manage.dto.aibo;
import java.util.List;
import lombok.Data;
/**
* @description: 创建任务传输对象
* @author: haown
* @create: 2024-08-22 14:52
**/
@Data
public class CreateTaskDto {
/**
* 任务名称
*/
private String taskName;
/**
* 外呼机器人唯一标识
*/
private String robotId;
/**
* 任务呼叫开始日期
*/
private String dialStartDate;
/**
* 任务呼叫结束日期
*/
private String dialEndDate;
/**
* 呼叫开始时间
*/
private String dialStartTime = "09:00";
/**
* 呼叫结束时间
*/
private String dialEndTime = "18:00";
/**
* 禁呼日期 99-节假日1-周一2-周二3-周三4-周四5-周五6-周六0-周日默认为空表示不限制禁呼日期
*/
private List<Integer> forbidDialDate;
/**
* 是否开启24小时空号检测
*/
private Boolean isOpenEmptyNum = false;
/**
* 是否开启12小时内停机检测
*/
private Boolean isOpenPhoneDown = false;
/**
* 号码类型过滤 1-400号码2-800号码3-手机号码4-固话5-95号码6-96号码7-其他
*/
private List<Integer> numTypeFilterList;
/**
* 任务拨号完成后是否进入完成状态, 0:拨号完成后任务不结束 1:拨号完成后任务结束 默认值为1
*/
private Integer callFinishTaskEnd = 0;
}

View File

@ -0,0 +1,33 @@
package com.xinelu.manage.dto.aibo;
import com.alibaba.fastjson2.JSONObject;
import lombok.Data;
/**
* @description: 客户名单传输对象
* @author: haown
* @create: 2024-08-23 15:52
**/
@Data
public class CustomerInfoDto {
/**
* 调用方的业务随路数据, 字符串百度侧原文回传
*/
private String extJson;
/**
* 号码1;号码2;号码3, 最多5个号码英文分号分隔,支持明文或密文
*/
private String mobile;
/**
* 用户指定归属地为空则默认使用号码实际归属地实际行政区域即可无格式要求"-"分隔开即可
*/
private String address;
/**
* 名单的变量var map名单外呼时需要用到的变量,{"key1": "value1","key2": "value2"}
*/
private JSONObject var;
}

View File

@ -0,0 +1,45 @@
package com.xinelu.manage.dto.aibo;
import java.util.List;
import lombok.Data;
/**
* @description: 导入名单传输对象
* @author: haown
* @create: 2024-08-23 15:10
**/
@Data
public class ImportTaskDto {
/**
* 任务Id
*/
private String taskId;
/**
* 加密类型 1系统加密即被叫号码采用系统的加密方式AES128加密
* 2不加密即被叫号码为明文
* 3自定义加密即被叫号码采用客户自定义的加密方式需在配置台-隐私配置-其他配置中配置自定义加解密服务地址便于请求解密
*/
private Integer secretType;
/**
* 密钥ID加密类型为系统加密时必填
*/
private Integer secretId;
/**
* 明文, 字符串长度小于等于20, 如果加密类型为系统加密则必填
*/
private String plainText;
/**
* 密文字符串加密类型为系统加密时必填
*/
private String cipherText;
/**
* 客户名单list,每一次最多只能导入1000个号码组可以多次导入同一个任务
*/
private List<CustomerInfoDto> customerInfoList;
}

View File

@ -0,0 +1,169 @@
package com.xinelu.manage.dto.aibo;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import java.util.List;
import lombok.Data;
/**
* @description: 任务单通电话回调传输data
* @author: haown
* @create: 2024-08-29 09:36
**/
@Data
public class TaskCallbackDataDto {
/**
* 通话唯一标识
*/
private String sessionId;
/**
* 租户唯一标识
*/
private Long tenantId;
/**
* 任务标识
*/
private String taskId;
/**
* 任务名称
*/
private String taskName;
/**
* 机器人ID
*/
private String robotId;
/**
* 外呼机器人名称
*/
private String robotName;
/**
* 号码组唯一标识导入名单后返回的名单号码组对应标识
*/
private Long memberId;
/**
* 被叫号码
*/
private String mobile;
/**
* 拨打次数
*/
private Integer callTimes;
/**
* 主叫号码
*/
private String callerNum;
/**
* 接通状态1-已接通 0-未接通
*/
private Integer endType;
/**
* 呼叫类型0-首次呼叫1-重试2-预约呼叫3-实时呼叫
*/
private Integer callType;
/**
* 未接通原因
*/
private String endTypeReason;
/**
* 生成通话录音唯一标识,可通过该标识获取录音
*/
private String contactUUID;
/**
* 文件id名单导入任务时生成的文件ID
*/
private Long field;
/**
* 信息收集内容
*/
private JSONObject collectInfo;
/**
* 会话还原记录
*/
private JSONArray record;
/**
* 拨号总时长单位为秒
*/
private Integer durationTimeLen;
/**
* 振铃时长单位为秒
*/
private Integer ringingTimeLen;
/**
* 对话时长单位为秒
*/
private Integer talkingTimeLen;
/**
* 呼叫开始时间-Unix时间戳(单位:毫秒)
*/
private Long startTime;
/**
* 振铃开始时间-Unix时间戳(单位:毫秒)
*/
private Long ringStartTime;
/**
* 通话开始时间-Unix时间戳(单位:毫秒)未接通显示'-'已接通会有值
*/
private Long talkingStartTime;
/**
* 呼叫结束时间-Unix时间戳(单位:毫秒)
*/
private Long endTime;
/**
* 意向在外呼机器人-流程节点-信息收集配置key为意向对应的value值
*/
private String intent;
/**
* 动作HUNGUP挂机
*/
private List<String> action;
/**
* 是否机器人主动挂机
*/
private Boolean isRobotHangup;
/**
* 变量导入名单的变量
*/
private JSONObject dialogVar;
/**
* 短信变量
*/
private JSONObject smsVar;
/**
* 来自导入信息
*/
private String extJson;
/**
* 转接结果,字段为空或值为0未发起1成功-1失败
*/
private Integer transResult;
}

View File

@ -0,0 +1,22 @@
package com.xinelu.manage.dto.aibo;
import lombok.Data;
/**
* @description: 任务单通电话回调传输对象
* @author: haown
* @create: 2024-08-29 14:06
**/
@Data
public class TaskCallbackDto {
/**
* 回调数据类型 0-任务呼叫单通电话回调 1-号码组终态回调 2-任务状态变更回调 3-实时呼叫单通电话回调
*/
private Integer callbackType;
/**
* 任务单通电话回调传输data
*/
private TaskCallbackDataDto data;
}

View File

@ -0,0 +1,23 @@
package com.xinelu.manage.mapper.scriptinfotaskinfo;
import com.xinelu.manage.domain.scriptinfotaskinfo.ScriptInfoTaskInfo;
/**
* 百度外呼话术任务临时表Mapper接口
*
* @author haown
* @date 2024-08-30
*/
public interface ScriptInfoTaskInfoMapper {
String getByScriptInfoId(Long scriptInfoId);
/**
* 新增百度外呼话术任务临时表
*
* @param scriptInfoTaskInfo 百度外呼话术任务临时表
* @return 结果
*/
int insertScriptInfoTaskInfo(ScriptInfoTaskInfo scriptInfoTaskInfo);
}

View File

@ -0,0 +1,54 @@
package com.xinelu.manage.service.aibo;
import com.xinelu.manage.dto.aibo.CreateTaskDto;
import com.xinelu.manage.dto.aibo.ImportTaskDto;
import com.xinelu.manage.dto.aibo.TaskCallbackDataDto;
import com.xinelu.manage.vo.aibo.ImportTaskVo;
import java.util.List;
/**
* 百度客悦·智能外呼平台Service接口
*
* @author: haown
* @create: 2024-08-22 09:50
*/
public interface IAIOBService {
/**
* @description 获取外呼平台accessToken
* @return 智能外呼平台 accessToken
* @Author haown
* @Date 2024-8-22 15:30
*/
String getAccessToken();
/**
* @description 创建任务
* @param createTaskDto 创建任务传输对象
* @return taskId 客悦·智能外呼平台任务唯一标识
* @Author haown
* @Date 2024-8-22 15:34
*/
String createTask(CreateTaskDto createTaskDto);
/**
* @description 任务状态变更
* @param taskId 任务id
* @param taskStatus 修改状态 2执行中3已暂停4已完成如点击终止
* @return 状态码
* @Author haown
* @Date 2024-8-23 11:49
*/
Integer updateTaskStatus(String taskId, Integer taskStatus);
/**
* @description 任务导入客户名单
* @param importTaskDto 导入名单传输对象
* @return 名单导入结果
* @Author haown
* @Date 2024-8-23 17:34
*/
List<ImportTaskVo> importTask(ImportTaskDto importTaskDto);
String taskCallBack(Integer callbackType, TaskCallbackDataDto data);
}

View File

@ -0,0 +1,227 @@
package com.xinelu.manage.service.aibo.impl;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.xinelu.common.core.redis.RedisCache;
import com.xinelu.common.exception.ServiceException;
import com.xinelu.common.utils.StringUtils;
import com.xinelu.manage.dto.aibo.CreateTaskDto;
import com.xinelu.manage.dto.aibo.ImportTaskDto;
import com.xinelu.manage.dto.aibo.TaskCallbackDataDto;
import com.xinelu.manage.service.aibo.IAIOBService;
import com.xinelu.manage.vo.aibo.ImportTaskVo;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
/**
* @description: 百度客悦·智能外呼平台Service实现
* @author: haown
* @create: 2024-08-22 09:54
**/
@Service
public class AIOBServiceImpl implements IAIOBService {
@Value("${aiob.url}")
private String url;
@Value("${aiob.accessKey}")
private String accessKey;
@Value("${aiob.secretKey}")
private String secretKey;
@Resource
private RedisCache redisCache;
/**
* @description 获取外呼平台accessToken
* @return 智能外呼平台 accessToken
* @Author haown
* @Date 2024-8-22 15:30
*/
@Override
public String getAccessToken() {
String accessToken = redisCache.getCacheObject("AIOBAssessToken");
if (StringUtils.isBlank(accessToken)) {
JSONObject requestBodyJson = new JSONObject();
requestBodyJson.put("secretKey", secretKey);
requestBodyJson.put("accessKey", accessKey);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<JSONObject> requestEntity = new HttpEntity<>(requestBodyJson, headers);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> responseEntity = restTemplate.exchange(url + "/v2/getToken", HttpMethod.POST, requestEntity, String.class);
JSONObject object = JSON.parseObject(responseEntity.getBody());
if (object == null || object.getInteger("code") == null || object.getInteger("code") != 200) {
throw new ServiceException("获取accessToken失败");
}
JSONObject data = object.getJSONObject("data");
// accessToken放入redis缓存
accessToken = data.getString("accessToken");
// 有效期30天
int expires_in = object.getInteger("expires_in");
redisCache.setCacheObject("AIOBAssessToken", accessToken, expires_in, TimeUnit.MINUTES);
}
return accessToken;
//{
// "time" : 1724293180993,
// "code" : 200,
// "msg" : "OK",
// "data" : {
// "tenantId" : 3849515568332800,
// "accessToken" : "cc-api-auth-v2/10.B88F964FF8AEF605D75872F1B0EDFE57B28609A057F5E24104B4A7FB74CD63EC8199731D637E6EDFD195A82D5DFF454E",
// "expiresTime" : 43200,
// "accessKey" : "b6d2d73cdf2d4d7ca2ccc84968accdb3",
// "secretKey" : "9262d2fe32544eaabce18331bd5ca2c5"
// }
//}
}
/**
* @description 创建任务
* @param createTaskDto 创建任务传输对象
* @return taskId 客悦·智能外呼平台任务唯一标识
* @Author haown
* @Date 2024-8-22 15:34
*/
@Override
public String createTask(CreateTaskDto createTaskDto) {
String accessToken = getAccessToken();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", accessToken);
HttpEntity<JSONObject> requestEntity = new HttpEntity<>((JSONObject)JSON.toJSON(createTaskDto), headers);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> responseEntity = restTemplate.exchange(url + "/v3/console/apitask/create", HttpMethod.POST, requestEntity, String.class);
JSONObject object = JSON.parseObject(responseEntity.getBody());
if (object == null || object.getInteger("code") == null || object.getInteger("code") != 200) {
throw new ServiceException("创建任务失败," + object.getString("msg"));
}
JSONObject data = object.getJSONObject("data");
return data.getString("taskId");
// {
// "code": 200,
// "msg": "success",
// "data": {
// "taskId": "731346584781056" //任务ID必填
// }
// }
}
/**
* @description 任务状态变更
* @param taskId 任务id
* @param taskStatus 修改状态 2执行中3已暂停4已完成如点击终止
* @return 状态码
* @Author haown
* @Date 2024-8-23 11:49
*/
@Override
public Integer updateTaskStatus(String taskId, Integer taskStatus) {
String accessToken = getAccessToken();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", accessToken);
JSONObject jsonObject = new JSONObject();
jsonObject.fluentPut("taskId", taskId)
.fluentPut("taskStatus", taskStatus);
HttpEntity<JSONObject> requestEntity = new HttpEntity<>(jsonObject, headers);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> responseEntity = restTemplate.exchange(url + "/v3/console/realtime/status/create", HttpMethod.POST, requestEntity, String.class);
JSONObject object = JSON.parseObject(responseEntity.getBody());
if (object == null || object.getInteger("code") == null || object.getInteger("code") != 200) {
throw new ServiceException("修改任务状态失败," + object.getString("msg"));
}
return 1;
// {
// "requestId": "c323dc9e5f67427c9c9d735a76e14164",
// "time": 1724375025670,
// "code": 200,
// "msg": "OK",
// "data": null
//}
}
/**
* @description 任务导入客户名单
* @param importTaskDto 导入名单传输对象
* @return 名单导入结果
* @Author haown
* @Date 2024-8-23 17:34
*/
@Override
public List<ImportTaskVo> importTask(ImportTaskDto importTaskDto) {
String accessToken = getAccessToken();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", accessToken);
HttpEntity<JSONObject> requestEntity = new HttpEntity<>((JSONObject)JSON.toJSON(importTaskDto), headers);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> responseEntity = restTemplate.exchange(url + "/v3/console/apitask/import", HttpMethod.POST, requestEntity, String.class);
JSONObject object = JSON.parseObject(responseEntity.getBody());
if (object == null || object.getInteger("code") == null || object.getInteger("code") != 200) {
throw new ServiceException("导入名单失败," + object.getString("msg"));
}
JSONObject data = object.getJSONObject("data");
JSONArray resList = data.getJSONArray("retList");
List<ImportTaskVo> importTaskList = Arrays.asList(resList.toArray(ImportTaskVo.class));
return importTaskList;
// {
// "code": 200,
// "msg": "success",
// "data": {
// "successNum": 2, // 名单导入成功条数
// "failedNum": 0, // 名单导入失败条数
// "resList": [ // 导入明细
// {
// "status": true, // 导入的状态true为 成功 bool
// "extJson": "",//调用方的业务随路数据, 字符串百度侧原文回传
// "reason": null, // 失败原因
// "taskMemberId": 734116037099777 // 百度侧memberID用户 用户号码组详情查询
// },
// {
// "status": true,
// "extJson": "",//调用方的业务随路数据, 字符串百度侧原文回传
// "reason": null,
// "taskMemberId": 734116037116160
// }
// ]
// }
// }
}
@Override
public String taskCallBack(Integer callbackType, TaskCallbackDataDto data) {
//TaskCallbackDataDto(sessionId=3874887150206976_2e1ae4e5ee0a4513b61d2411523f7b9b, tenantId=3849515568332800,
// taskId=3874887150206976, taskName=测试20240829, robotId=11a05202-12a4-437a-b989-43235b920a2f,
// robotName=慢病用药回访, memberId=3875110220070912, mobile=15166940975, callTimes=1,
// callerNum=02110001062, endType=1, callType=0, endTypeReason=null, contactUUID=1724917686303674_278162,
// field=null, collectInfo={},
// record=[{"role":"speech","timestamp":1724917698521,"contextText":"您好,我是山东省立医院的电话客服,请问您是李四本人或家属吗?", "sn":"19a09431-1ac6-43a1-8f9f-4b6f617bed73","intent":"NOINTENT","start":"00:00.033","stop":"00:06.974","timeLen":6941,"interrupted":false,"silent":false,"nodeInfo":"eyJwcm9jZXNzTmFtZSI6IuaFoueXheeUqOiNr+Wbnuiuv+ivneacryIsIm5vZGVOYW1lIjoi5byA5Zy655m96IqC54K5X3RwcnZtc2g4IiwiaW50ZW50IjoiIiwiZW50aXR5IjpbXSwic3lzdGVtRXZlbnQiOiIiLCJmYXEiOiIifQ=="},
// {"role":"voice","timestamp":1724917707542,"content":"是的","contextText":"是的","sn":"0e5c9c4e65db11ef_2_1","start":"00:06.930","stop":"00:08.330","timeLen":1400,"interrupted":false,"silent":false},
// {"role":"speech","timestamp":1724917708359,"content":"是的","contextText":"好的,您之前在我们医院住过院,想了解一下您近期的健康状况。","sn":"0e5c9c4e65db11ef_2_1","intent":"patient_self","start":"00:09.871","stop":"00:15.793","timeLen":5922,"interrupted":false,"silent":false,"nodeInfo":"eyJwcm9jZXNzTmFtZSI6IuaFoueXheeUqOiNr+Wbnuiuv+ivneacryIsIm5vZGVOYW1lIjoi5pys5Lq66IqC54K5X3Nyd2Q5MTR3IiwiaW50ZW50Ijoi5piv5pys5Lq6IiwiZW50aXR5IjpbXSwic3lzdGVtRXZlbnQiOiIiLCJmYXEiOiIifQ=="},
// {"role":"voice","timestamp":1724917718191,"content":"好的嗯","contextText":"好的嗯","sn":"0e5c9c4e65db11ef_4_1","start":"00:16.650","stop":"00:18.970","timeLen":2320,"interrupted":false,"silent":false},
// {"role":"speech","timestamp":1724917718873,"content":"好的嗯","contextText":"您是否正常按时服药呢?","sn":"0e5c9c4e65db11ef_4_1","intent":"NOINTENT","start":"00:20.385","stop":"00:22.917","timeLen":2532,"interrupted":false,"silent":false,"nodeInfo":"eyJwcm9jZXNzTmFtZSI6IuaFoueXheeUqOiNr+Wbnuiuv+ivneacryIsIm5vZGVOYW1lIjoi6IKv5a6a6IqC54K5X3F6aWpzb2g2IiwiaW50ZW50IjoiIiwiZW50aXR5IjpbXSwic3lzdGVtRXZlbnQiOiIiLCJmYXEiOiIifQ=="},
// {"role":"voice","timestamp":1724917725425,"content":"是的","contextText":"是的","sn":"0e5c9c4e65db11ef_6_1","start":"00:24.810","stop":"00:26.210","timeLen":1400,"interrupted":false,"silent":false},
// {"role":"speech","timestamp":1724917725993,"content":"是的","contextText":"祝您早日康复。","sn":"0e5c9c4e65db11ef_6_1","intent":"sys_yes","start":"00:27.505","stop":"00:29.637","timeLen":2132,"interrupted":false,"silent":false,"nodeInfo":"eyJwcm9jZXNzTmFtZSI6IuaFoueXheeUqOiNr+Wbnuiuv+ivneacryIsIm5vZGVOYW1lIjoi5oyJ5pe25pyN6I2v6IqC54K5X3Bxd2RkcDBpIiwiaW50ZW50Ijoi6IKv5a6a5oSP5Zu+IiwiZW50aXR5IjpbXSwic3lzdGVtRXZlbnQiOiIiLCJmYXEiOiIifQ=="}],
// durationTimeLen=42, ringingTimeLen=9, talkingTimeLen=29, startTime=1724917686000, ringStartTime=1724917688000, talkingStartTime=1724917698000, endTime=1724917728000, intent=null, action=[HANGUP], isRobotHangup=true,
// dialogVar={"departmentName":"山东省立医院","patientName":"李四"}, smsVar=null, extJson=测试2, transResult=0)
return data.getTaskId();
}
}

View File

@ -0,0 +1,32 @@
package com.xinelu.manage.vo.aibo;
import lombok.Data;
/**
* @description: 导入任务名单返回视图类
* @author: haown
* @create: 2024-08-23 16:19
**/
@Data
public class ImportTaskVo {
/**
* 导入的状态true为 成功
*/
private Boolean status;
/**
* 调用方的业务随路数据, 字符串百度侧原文回传
*/
private String extJson;
/**
* 失败原因
*/
private String reason;
/**
* 百度侧memberID用户 用户号码组详情查询
*/
private Integer taskMemberId;
}

View File

@ -26,6 +26,7 @@
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
<result property="robotPublishId" column="robot_publish_id"/>
<result property="variables" column="variables"/>
</resultMap>
@ -56,7 +57,8 @@
create_time,
update_by,
update_time,
robot_publish_id
robot_publish_id,
variables
from script_info
</sql>
@ -80,7 +82,8 @@
si.create_time,
si.update_by,
si.update_time,
si.robot_publish_id
si.robot_publish_id,
si.variables
from script_info si
left join department d on d.id = si.department_id
<where>
@ -200,6 +203,8 @@
</if>
<if test="robotPublishId != null">robot_publish_id,
</if>
<if test="variables != null">variables,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="departmentId != null">#{departmentId},
@ -242,6 +247,8 @@
</if>
<if test="robotPublishId != null">#{robotPublishId},
</if>
<if test="variables != null">#{variables},
</if>
</trim>
</insert>
@ -304,6 +311,9 @@
<if test="robotPublishId != null">robot_publish_id =
#{robotPublishId},
</if>
<if test="variables != null">variables =
#{variables},
</if>
</trim>
where id = #{id}
</update>

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xinelu.manage.mapper.scriptinfotaskinfo.ScriptInfoTaskInfoMapper">
<resultMap type="ScriptInfoTaskInfo" id="ScriptInfoTaskInfoResult">
<result property="id" column="id"/>
<result property="scriptInfoId" column="script_info_id"/>
<result property="robotId" column="robot_id"/>
<result property="taskId" column="task_id"/>
<result property="createTime" column="create_time"/>
</resultMap>
<sql id="selectScriptInfoTaskInfoVo">
select id,
script_info_id,
robot_id,
task_id,
create_time,
from script_info_task_info
</sql>
<select id="getByScriptInfoId" resultType="java.lang.String">
select task_id
from script_info_task_info
where script_info_id = #{scriptInfoId}
</select>
<insert id="insertScriptInfoTaskInfo" parameterType="ScriptInfoTaskInfo" useGeneratedKeys="true"
keyProperty="id">
insert into script_info_task_info
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="scriptInfoId != null">script_info_id,
</if>
<if test="robotId != null">robot_id,
</if>
<if test="taskId != null">task_id,
</if>
<if test="createTime != null">create_time,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="scriptInfoId != null">#{scriptInfoId},
</if>
<if test="robotId != null">#{robotId},
</if>
<if test="taskId != null">#{taskId},
</if>
<if test="createTime != null">#{createTime},
</if>
</trim>
</insert>
</mapper>

View File

@ -1,26 +1,45 @@
package com.xinelu.quartz.task;
import com.alibaba.fastjson2.JSONObject;
import com.xinelu.common.enums.NodeExecuteStatusEnum;
import com.xinelu.common.enums.PhoneDialMethodEnum;
import com.xinelu.common.enums.TaskNodeTypeEnum;
import com.xinelu.common.utils.StringUtils;
import com.xinelu.manage.domain.patientinfo.PatientInfo;
import com.xinelu.manage.domain.patientvisitrecord.PatientVisitRecord;
import com.xinelu.manage.domain.scriptInfo.ScriptInfo;
import com.xinelu.manage.domain.scriptinfotaskinfo.ScriptInfoTaskInfo;
import com.xinelu.manage.domain.signpatientmanageroute.SignPatientManageRoute;
import com.xinelu.manage.domain.signpatientmanageroutenode.SignPatientManageRouteNode;
import com.xinelu.manage.domain.signpatientrecord.SignPatientRecord;
import com.xinelu.manage.dto.aibo.CreateTaskDto;
import com.xinelu.manage.dto.aibo.CustomerInfoDto;
import com.xinelu.manage.dto.aibo.ImportTaskDto;
import com.xinelu.manage.dto.signpatientmanageroutenode.SignPatientManageRouteNodeDto;
import com.xinelu.manage.mapper.labelfieldcontent.LabelFieldContentMapper;
import com.xinelu.manage.mapper.patientinfo.PatientInfoMapper;
import com.xinelu.manage.mapper.patientvisitrecord.PatientVisitRecordMapper;
import com.xinelu.manage.mapper.scriptInfo.ScriptInfoMapper;
import com.xinelu.manage.mapper.scriptinfotaskinfo.ScriptInfoTaskInfoMapper;
import com.xinelu.manage.mapper.signpatientmanageroute.SignPatientManageRouteMapper;
import com.xinelu.manage.mapper.signpatientmanageroutenode.SignPatientManageRouteNodeMapper;
import com.xinelu.manage.mapper.signpatientrecord.SignPatientRecordMapper;
import com.xinelu.manage.service.signpatientmanageroutenode.IRobotPublishService;
import com.xinelu.manage.service.aibo.IAIOBService;
import com.xinelu.manage.service.signpatientmanageroutenode.ISignPatientManageRouteNodeService;
import com.xinelu.manage.vo.labelfieldcontent.LabelFieldInfoContentVo;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Component;
/**
@ -43,10 +62,20 @@ public class UploadRobotPublishTask {
@Resource
private PatientVisitRecordMapper patientVisitRecordMapper;
@Resource
private IRobotPublishService robotPublishService;
private ScriptInfoTaskInfoMapper scriptInfoTaskInfoMapper;
@Resource
private IAIOBService aiobService;
@Resource
private ScriptInfoMapper scriptInfoMapper;
@Resource
private PatientInfoMapper patientInfoMapper;
@Resource
private LabelFieldContentMapper labelFieldContentMapper;
public void uploadRobotPublishTask() {
log.info("开始执行爱医声上传任务定时任务......");
log.info("开始执行百度智能外呼创建任务定时任务......");
// 查找需要当天执行的AI打电话任务
SignPatientManageRouteNodeDto signPatientManageRouteNodeDto = new SignPatientManageRouteNodeDto();
signPatientManageRouteNodeDto.setTaskNodeType(TaskNodeTypeEnum.PHONE_OUTBOUND.getInfo());
@ -65,7 +94,98 @@ public class UploadRobotPublishTask {
}
});
if (CollectionUtils.isNotEmpty(executeNodeList)) {
// 按照话术id分组每个话术id对应不同的机器人id, 每个机器人每天创建一条任务
Map<Long, List<SignPatientManageRouteNode>> groupByScriptInfo = executeNodeList.stream().collect(Collectors.groupingBy(SignPatientManageRouteNode::getScriptInfoId));
// 根据机器人id查询智能外呼系统的任务id
for (Long scriptInfoId : groupByScriptInfo.keySet()) {
ScriptInfo scriptInfo = scriptInfoMapper.selectScriptInfoById(scriptInfoId);
String taskId = scriptInfoTaskInfoMapper.getByScriptInfoId(scriptInfoId);
if (StringUtils.isBlank(taskId)) {
// 没有任务则创建任务
if (ObjectUtils.isNotEmpty(scriptInfo)) {
CreateTaskDto createTaskDto = new CreateTaskDto();
createTaskDto.setTaskName(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + scriptInfo.getScriptName());
createTaskDto.setRobotId(scriptInfo.getRobotPublishId());
createTaskDto.setDialStartDate(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
createTaskDto.setDialEndDate(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
createTaskDto.setForbidDialDate(Arrays.asList(99));
createTaskDto.setNumTypeFilterList(Arrays.asList(1,2));
taskId = aiobService.createTask(createTaskDto);
ScriptInfoTaskInfo scriptInfoTaskInfo = new ScriptInfoTaskInfo();
scriptInfoTaskInfo.setScriptInfoId(scriptInfoId);
scriptInfoTaskInfo.setTaskId(taskId);
scriptInfoTaskInfo.setRobotId(scriptInfo.getRobotPublishId());
scriptInfoTaskInfo.setCreateTime(LocalDateTime.now());
scriptInfoTaskInfoMapper.insertScriptInfoTaskInfo(scriptInfoTaskInfo);
}
}
// 客户名单list
List<SignPatientManageRouteNode> scriptNodeList = groupByScriptInfo.get(scriptInfoId);
List<CustomerInfoDto> customerInfoList = new ArrayList<>();
scriptNodeList.forEach(node -> {
CustomerInfoDto customerInfoDto = new CustomerInfoDto();
customerInfoDto.setExtJson(node.getId() + "");
SignPatientManageRoute signPatientManageRoute = signPatientManageRouteMapper.selectSignPatientManageRouteById(node.getManageRouteId());
PatientInfo patientInfo = patientInfoMapper.selectPatientInfoById(signPatientManageRoute.getPatientId());
customerInfoDto.setMobile(patientInfo.getPatientPhone());
// 查询患者画像信息
List<LabelFieldInfoContentVo> labelFieldContentList = labelFieldContentMapper.selectByPatientId(patientInfo.getId());
// 处理变量
JSONObject jsonObject = new JSONObject();
if (StringUtils.isNotBlank(scriptInfo.getVariables())) {
List<String> variables = Arrays.asList(scriptInfo.getVariables().split("|"));
variables.forEach(variable -> {
LabelFieldInfoContentVo labelFieldContent = labelFieldContentList.stream().filter(s -> Objects.equals(s.getFieldCode(), variable.replaceAll("_", "").toUpperCase())).findFirst().orElse(null);
jsonObject.fluentPut(variable, ObjectUtils.isEmpty(labelFieldContent) ? "" : labelFieldContent.getFieldValue());
});
}
customerInfoDto.setVar(jsonObject);
customerInfoList.add(customerInfoDto);
});
// 上传名单
ImportTaskDto importTaskDto = new ImportTaskDto();
importTaskDto.setTaskId(taskId);
importTaskDto.setSecretType(2);
importTaskDto.setCustomerInfoList(customerInfoList);
aiobService.importTask(importTaskDto);
// 开启任务
aiobService.updateTaskStatus(taskId, 2);
}
// 开启任务
//robotPublishService.uploadRobotPublishTask(executeNodeList);
}
}
public static String convertToCamelCase(String s) {
if (s == null || !s.contains("_")){
return s;
}
StringBuffer sb = new StringBuffer();
//用来判断大写的标志
boolean nextUpperCase = false;
for (int i = 0; i < s.length(); i++) {
if ("_".equals(String.valueOf(s.charAt(i)))) {
nextUpperCase = true;
} else {
if (nextUpperCase) {
sb = sb.append(String.valueOf(s.charAt(i)).toUpperCase());
nextUpperCase = false;
}else {
sb = sb.append(s.charAt(i));
}
}
}
return sb.toString();
}
public static void main(String[] args) {
String underscoreCase = "user_name";
String camelCase = convertToCamelCase(underscoreCase);
System.out.println(camelCase); // 输出 "UserName"
}
}