package cn.gistack.sm.intelligentCall.service.impl;
|
|
import cn.gistack.sm.intelligentCall.constant.CallConstant;
|
import cn.gistack.sm.intelligentCall.constant.ZtConstant;
|
import cn.gistack.sm.intelligentCall.entity.Scene;
|
import cn.gistack.sm.intelligentCall.mapper.CallTaskMapper;
|
import cn.gistack.sm.intelligentCall.service.CallService;
|
import cn.gistack.sm.intelligentCall.vo.CallTaskResultVO;
|
import cn.gistack.sm.intelligentCall.vo.CallTaskStatistic;
|
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSONArray;
|
import com.alibaba.fastjson.JSONObject;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import lombok.extern.slf4j.Slf4j;
|
import org.apache.commons.lang3.RandomUtils;
|
import org.apache.commons.lang3.StringUtils;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.http.*;
|
import org.springframework.stereotype.Service;
|
import org.springframework.util.DigestUtils;
|
import org.springframework.util.LinkedMultiValueMap;
|
import org.springframework.util.MultiValueMap;
|
import org.springframework.web.client.RestTemplate;
|
|
import java.text.SimpleDateFormat;
|
import java.time.LocalDateTime;
|
import java.time.format.DateTimeFormatter;
|
import java.util.*;
|
import java.util.stream.Collectors;
|
|
@Slf4j
|
@Service
|
public class CallServiceImpl implements CallService {
|
|
@Autowired
|
private CallTaskMapper callTaskMapper;
|
|
@Autowired
|
private RestTemplate restTemplate;
|
|
// @Value("${dial.task.url}")
|
private String url = CallConstant.HOST;
|
|
// @Value("${dial.task.empId}")
|
private String empId = CallConstant.empId;
|
|
// @Value("${dial.task.appId}")
|
private String appId = CallConstant.AppKey;
|
|
// @Value("${dial.task.appSecret}")
|
private String appSecret = CallConstant.AppSecret;
|
|
// @Value("${dial.task.scenesId}")
|
private String scenesId = CallConstant.scenesId;
|
|
// @Value("${dial.task.callingNumbers}")
|
private String callingNumbers = CallConstant.callingNumbers;
|
|
private static final String CONTENT_TYPE = "application/json; charset=UTF-8";
|
private static final String ACCEPT_TYPE = "application/json";
|
private static final String ACCEPT_ENCODING = "gzip";
|
|
private final static String CREATE_TASK_URL = "/api/task/createTask.json";
|
private final static String CALL_DETAIL_URL = "/api/task/callDetail.json";
|
|
/**
|
* 创建任务
|
* @param map
|
* @return
|
*/
|
@Override
|
public List<String> createTask(Map<String,Object> map) {
|
List<Map<String, String>> params = (List<Map<String, String>>) map.get("list");
|
String requestUrl = url + CREATE_TASK_URL;
|
LocalDateTime now = LocalDateTime.now();
|
String timestamp = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss SSS"));
|
String transId = generateTransId(now);
|
String taskName = now.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日HH:mm:ss")) + "智能外呼";;
|
if(null!= map.get("taskName") && !map.get("taskName").equals("")){
|
taskName = map.get("taskName").toString();
|
}
|
String[] numbers = StringUtils.split(callingNumbers,",");
|
String taskScheduleTime = LocalDateTime.now().plusHours(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
if (null!=map.get("taskScheduleTime") && !map.get("taskScheduleTime").equals("")){
|
taskScheduleTime = map.get("taskScheduleTime").toString();
|
}
|
String scenesIds = scenesId;
|
if (null!=map.get("scenesId") && !map.get("scenesId").equals("")){
|
scenesIds = map.get("scenesId").toString();
|
}
|
JSONArray arr = getCalleeInfo(params);
|
HttpHeaders httpHeaders = new HttpHeaders();
|
httpHeaders.add("Content-Type", CONTENT_TYPE);
|
httpHeaders.add("Accept", ACCEPT_TYPE);
|
httpHeaders.add("Accept-Encoding", ACCEPT_ENCODING);
|
JSONObject paramJson = new JSONObject();
|
paramJson.put("taskName", taskName);
|
paramJson.put("scenesId", scenesIds);
|
paramJson.put("callingNumbers", numbers);
|
paramJson.put("taskScheduleTime", taskScheduleTime);
|
paramJson.put("calleeData", arr);
|
paramJson.put("empId", empId);
|
String token = getToken(appId, timestamp, transId, appSecret, paramJson.toJSONString());
|
JSONObject headerJson = new JSONObject();
|
headerJson.put("appId", appId);
|
headerJson.put("timestamp", timestamp);
|
headerJson.put("transId", transId);
|
headerJson.put("token", token);
|
JSONObject bodyJson = new JSONObject();
|
bodyJson.put("head", headerJson);
|
bodyJson.put("body", paramJson);
|
HttpEntity<String> stringHttpEntity = new HttpEntity<>(bodyJson.toJSONString(), httpHeaders);
|
log.info("创建超汛限智能外呼任务请求参数:{}", bodyJson.toJSONString());
|
ResponseEntity<String> response = restTemplate.postForEntity(requestUrl, stringHttpEntity, String.class);
|
log.info("创建外呼任务请求结果:{}", response.getBody());
|
if (response.getStatusCode() != HttpStatus.OK) {
|
log.error("创建智能外呼任务失败:{}", response.getStatusCode());
|
}
|
JSONObject jsonObject = JSON.parseObject(response.getBody());
|
Boolean success = jsonObject.getBoolean("success");
|
String taskId = "";
|
if (success){
|
//通过任务名称查询taskId
|
taskId = callTaskMapper.getTaskIdByTaskName(taskName);
|
}
|
List<String> list = new ArrayList<>();
|
list.add(response.getBody());
|
list.add(taskId);
|
list.add(success.toString());
|
// 返回
|
return list;
|
}
|
|
/**
|
* 获取通话详情
|
* @param taskId
|
* @param calleeNumber
|
* @return
|
*/
|
@Override
|
public Object getCallDetail(String taskId, String calleeNumber) {
|
String requestUrl = url + CALL_DETAIL_URL;
|
LocalDateTime now = LocalDateTime.now();
|
String timestamp = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss SSS"));
|
String transId = generateTransId(now);
|
HttpHeaders httpHeaders = new HttpHeaders();
|
httpHeaders.add("Content-Type", CONTENT_TYPE);
|
httpHeaders.add("Accept", ACCEPT_TYPE);
|
httpHeaders.add("Accept-Encoding", ACCEPT_ENCODING);
|
JSONObject paramJson = new JSONObject();
|
paramJson.put("taskId", taskId);
|
paramJson.put("calleeNumber", calleeNumber);
|
JSONObject bodyJson = new JSONObject();
|
bodyJson.put("head", getHeadJson(appId, timestamp, transId, appSecret, paramJson.toJSONString()));
|
bodyJson.put("body", paramJson);
|
HttpEntity<String> stringHttpEntity = new HttpEntity<>(bodyJson.toJSONString(), httpHeaders);
|
ResponseEntity<String> response = restTemplate.postForEntity(requestUrl, stringHttpEntity, String.class);
|
log.info(response.toString());
|
if (response.getStatusCode() == HttpStatus.OK) {
|
JSONObject responseJson = (JSONObject) JSONObject.parse(response.getBody());
|
assert responseJson != null;
|
// return JSONObject.parseObject(responseJson.getString("data"), CallBackDetailVO.class);
|
return response.getBody();
|
}
|
return null;
|
}
|
|
private String getToken(String appId, String timestamp, String transId, String appSecret, String body) {
|
String sb = "appId" + appId + "timestamp" + timestamp + "transId" + transId + body + appSecret;
|
return DigestUtils.md5DigestAsHex(sb.getBytes());
|
}
|
|
private JSONObject getHeadJson(String appId, String timestamp, String transId, String appSecret, String body) {
|
String token = getToken(appId, timestamp, transId, appSecret, body);
|
JSONObject headerJson = new JSONObject();
|
headerJson.put("appId", appId);
|
headerJson.put("timestamp", timestamp);
|
headerJson.put("transId", transId);
|
headerJson.put("token", token);
|
return headerJson;
|
}
|
|
private String generateTransId(LocalDateTime now) {
|
String s = "000000" + RandomUtils.nextLong(0, 999999);
|
String randomStr = StringUtils.substring(s, -6);
|
return now.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS")) + randomStr;
|
}
|
|
/**
|
* 封装被呼人对象
|
* @param params 被呼人信息
|
* @return 被呼人数组
|
*/
|
private JSONArray getCalleeInfo(List<Map<String, String>> params) {
|
JSONArray arr = new JSONArray();
|
params.forEach(map -> {
|
String phone = map.get("phone");
|
String name = map.get("name");
|
String code = map.get("code");
|
String userName = map.get("userName");
|
String userId = map.get("userId");
|
String over = map.get("over");
|
if (StringUtils.isNotEmpty(phone) && StringUtils.isNotEmpty(name)) {
|
JSONObject json = new JSONObject();
|
json.put("被叫号码", phone);
|
json.put("水库名称", name);
|
json.put("水库编码", code);
|
json.put("用户名", userName);
|
json.put("用户id", userId);
|
json.put("超汛限多少米", over);
|
arr.add(json);
|
}
|
});
|
return arr;
|
}
|
|
/**
|
* 查詢任务列表数据
|
* @param page
|
* @param callTask
|
* @return
|
*/
|
@Override
|
public IPage<CallTaskStatistic> getCallListPage(IPage<CallTaskStatistic> page, CallTaskStatistic callTask) {
|
return page.setRecords(callTaskMapper.getCallListPage(page,callTask));
|
}
|
|
/**
|
* 查询呼叫详情列表信息
|
* @param callTaskResult
|
* @param page
|
* @return
|
*/
|
@Override
|
public IPage<CallTaskResultVO> getCallTaskResultListPage(IPage<CallTaskResultVO> page, CallTaskResultVO callTaskResult) {
|
List<String> list = new ArrayList<>();
|
if (null!= callTaskResult.getAreaName() && !callTaskResult.getAreaName().equals("")){
|
// 查询所有下级区域code
|
list = getAllChildrenAreaByAreaCode(callTaskResult.getAreaName());
|
}
|
List<CallTaskResultVO> resultListPage = callTaskMapper.getCallTaskResultListPage(page, callTaskResult,list,null);
|
return page.setRecords(resultListPage);
|
}
|
|
/**
|
* 导出呼叫详情列表信息
|
* @param callTaskResult
|
* @return
|
*/
|
@Override
|
public Object getCallTaskResultList(CallTaskResultVO callTaskResult) {
|
List<String> list = new ArrayList<>();
|
List<String> idsList = new ArrayList<>();
|
if (null!= callTaskResult.getAreaName() && !callTaskResult.getAreaName().equals("")){
|
// 查询所有下级区域code
|
list = getAllChildrenAreaByAreaCode(callTaskResult.getAreaName());
|
}
|
if (null!= callTaskResult.getIds() && !callTaskResult.getIds().equals("")){
|
// 查询所有下级区域code
|
idsList = Arrays.asList(callTaskResult.getIds().split(","));
|
}
|
List<CallTaskResultVO> resultListPage = callTaskMapper.getCallTaskResultListPage(null, callTaskResult,list,idsList);
|
// 返回
|
return resultListPage;
|
}
|
|
/**
|
* 查询智能外呼统计数据-按日期-行政区统计
|
* @param callTaskStatistic
|
* @return
|
*/
|
@Override
|
public Object getOutgoingStatisticList(IPage<CallTaskStatistic> page,CallTaskStatistic callTaskStatistic) {
|
List<String> list = new ArrayList<>();
|
if (null!= callTaskStatistic.getAdName() && !callTaskStatistic.getAdName().equals("")){
|
// 查询所有下级区域code
|
list = getAllChildrenAreaByAreaCode(callTaskStatistic.getAdName());
|
}
|
// 勾选导出行政区 id 组装
|
if (null!= callTaskStatistic.getIds() && !callTaskStatistic.getIds().equals("")){
|
list = Arrays.asList(callTaskStatistic.getIds().split(","));
|
}
|
// 判断是否分页
|
if (null!= callTaskStatistic.getIsPage()){
|
return callTaskMapper.getOutgoingStatisticList(null,callTaskStatistic,list);
|
}
|
return page.setRecords(callTaskMapper.getOutgoingStatisticList(page,callTaskStatistic,list));
|
}
|
|
/**
|
* 查询智能外呼统计数据-行政区统计
|
* @param callTaskStatistic
|
* @return
|
*/
|
@Override
|
public Object getOutgoingStatisticByArea(CallTaskStatistic callTaskStatistic) {
|
return callTaskMapper.getOutgoingStatisticByArea(callTaskStatistic);
|
}
|
|
/**
|
* 查询所有下级区域
|
* @param areaCode
|
* @return
|
*/
|
private List<String> getAllChildrenAreaByAreaCode(String areaCode) {
|
return callTaskMapper.getAllChildrenAreaByAreaCode(areaCode);
|
}
|
|
/**
|
* 通过水库编码获取行政区名称
|
* @param adCode
|
* @return
|
*/
|
private String getAreaNameByAdCode(String adCode) {
|
// 通过adCode查询区域名称
|
return callTaskMapper.getAreaNameByAdCode(adCode);
|
}
|
|
/**
|
* 通过行政区编号获取水库名称
|
* @param areaCode
|
* @return
|
*/
|
private List<String> getWaterNameByAreaCode(String areaCode) {
|
// 通过行政区编号获取水库名称
|
return callTaskMapper.getWaterNameByAreaCode(areaCode);
|
}
|
|
/**
|
* 根据条件自动创建外呼任务
|
* @param params
|
* @return
|
*/
|
@Override
|
public List<String> createCallTaskByParam(Map<String, Object> params) {
|
// 自动创建任务的 4个条件 1:呼叫人数 2:调度时间 3:是否超汛限 4:多少天内呼叫过的不呼叫(天数)
|
//1. 调用中台接口查询出所有需要发任务的水库巡查责任人相关信息(该处需要将是否超汛限条件传入)
|
JSONArray jsonArray = getZtWaterMangerData(params);
|
// 过滤得到手机号集合(排查手机号为null的数据)
|
List<Scene> totalList = filterJsonToList(jsonArray);
|
//2. 查询外呼多少天内已呼叫过的人员手机号集合信息
|
// 计算开始时间,结束时间
|
computationTime(params);
|
// 查询对应数据
|
List<String> list = callTaskMapper.getCallUserPhoneByParam(params);
|
//3. 比对中台得到的数据和外呼已呼叫信息,从中随机取出呼叫人数的信息
|
Set<Scene> taskRandomList = getCreateTaskRandomList(params, totalList, list);
|
//4. 传入调度时间和随机得到的人员信息进行任务的创建并返回
|
params.put("list",setForListMap(taskRandomList));
|
// 调度时间处理
|
params.put("taskScheduleTime",new SimpleDateFormat("yyyy-MM-dd").format(new Date())+ " " +params.get("taskScheduleTime") + ":00");
|
// 创建任务并返回结果
|
return createTask(params);
|
}
|
|
/**
|
* hashSet 转 List<Map<String,String>>
|
* @param taskRandomList
|
* @return
|
*/
|
private List<Map<String,String>> setForListMap(Set<Scene> taskRandomList) {
|
List<Map<String,String>> list = new ArrayList<>();
|
for (Scene scene : taskRandomList) {
|
Map<String, String> map = new HashMap<>();
|
map.put("phone",scene.getPhone());
|
map.put("name",scene.getWaterName());
|
map.put("code",scene.getWaterCode());
|
map.put("userName",scene.getUsername());
|
map.put("userId",scene.getPhone());
|
map.put("over","");
|
// 加入集合
|
list.add(map);
|
}
|
return list;
|
}
|
|
/**
|
* 获取随机呼叫的相关信息
|
* @param params
|
* @param totalList
|
* @param list
|
*/
|
private Set<Scene> getCreateTaskRandomList(Map<String, Object> params, List<Scene> totalList, List<String> list) {
|
Integer personNumber = ZtConstant.person_number;
|
if (!params.get("personNumber").equals("")){
|
personNumber = Integer.parseInt(params.get("personNumber").toString());
|
}
|
// 先取差集
|
List<Scene> sceneList = totalList.stream().filter(item -> !list.contains(item.getPhone())).collect(Collectors.toList());
|
// 如果差集数量不够 预定的数量(超汛情况),则从未超汛情况数据中补齐预定的数量
|
if (sceneList.size()<personNumber){
|
Map<String, Object> map = new HashMap<>();
|
map.put("isOver",0);
|
// 查询未超汛的数据
|
JSONArray jsonArray = getZtWaterMangerData(map);
|
// 过滤得到手机号集合(排查手机号为null的数据)
|
List<Scene> sceneListNoOver = filterJsonToList(jsonArray);
|
// 取差集
|
List<Scene> differenceSceneListNoOver = sceneListNoOver.stream().filter(item -> !list.contains(item.getPhone())).collect(Collectors.toList());
|
// 再随机抽取对应人员手机号
|
Set<Scene> hashSet = new HashSet<>();
|
// 先将超汛的加入集合
|
for (Scene scene : sceneList) {
|
hashSet.add(scene);
|
}
|
// 未超汛的补充
|
personNumber = personNumber - sceneList.size();
|
Random random = new Random();
|
for (int i = 0; i < differenceSceneListNoOver.size(); i++) {
|
// 获取随机数作为下标索引
|
int num = random.nextInt(differenceSceneListNoOver.size());
|
// 保存到 hashSet ,保证唯一
|
hashSet.add(differenceSceneListNoOver.get(num));
|
// 数量达到预期设定退出循环
|
if (hashSet.size() == personNumber) {
|
break;
|
}
|
}
|
return hashSet;
|
}else {
|
// 再随机抽取对应人员手机号
|
Set<Scene> hashSet = new HashSet<>();
|
Random random = new Random();
|
for (int i = 0; i < sceneList.size(); i++) {
|
// 获取随机数作为下标索引
|
int num = random.nextInt(sceneList.size());
|
// 保存到 hashSet ,保证唯一
|
hashSet.add(sceneList.get(num));
|
// 数量达到预期设定退出循环
|
if (hashSet.size() == personNumber) {
|
break;
|
}
|
}
|
return hashSet;
|
}
|
}
|
|
/**
|
* 将 jsonArray 取出手机号得到新的集合
|
* @param jsonArray
|
* @return
|
*/
|
private List<Scene> filterJsonToList(JSONArray jsonArray) {
|
List<Scene> list = new ArrayList<>();
|
for (int i = 0; i < jsonArray.size(); i++) {
|
if (null != jsonArray.getJSONObject(i).get("user_phone") &&
|
!jsonArray.getJSONObject(i).get("user_phone").equals("")){
|
Scene scene = new Scene();
|
scene.setPhone(jsonArray.getJSONObject(i).get("user_phone").toString());
|
scene.setUsername(jsonArray.getJSONObject(i).get("user_name").toString());
|
scene.setUserId(jsonArray.getJSONObject(i).get("user_phone").toString());
|
scene.setWaterName(jsonArray.getJSONObject(i).get("res_nm").toString());
|
scene.setWaterCode(jsonArray.getJSONObject(i).get("res_cd").toString());
|
// 加入集合
|
list.add(scene);
|
}
|
}
|
return list;
|
}
|
|
/**
|
* 计算时间
|
* @param params
|
*/
|
private void computationTime(Map<String, Object> params) {
|
long l = System.currentTimeMillis();
|
Integer day = ZtConstant.day_number;
|
// 获取天数
|
if (null != params.get("day")){
|
day = Integer.parseInt(params.get("day").toString());
|
}
|
long m = 60*60*24*1000*day;
|
// 相减得到时间
|
Long x = l-m;
|
// 格式化日期
|
String startTime = new SimpleDateFormat("yyyy-MM-dd").format(new Date(x));
|
String endTime = new SimpleDateFormat("yyyy-MM-dd").format(new Date(l));
|
// 加入到参数中
|
params.put("startTime",startTime);
|
params.put("endTime",endTime);
|
}
|
|
/**
|
* 调用中台接口查询出所有需要发任务的水库巡查责任人相关信息(该处需要将是否超汛限条件传入)
|
* @param params
|
* @return
|
*/
|
private JSONArray getZtWaterMangerData(Map<String, Object> params) {
|
//设置请求头
|
HttpHeaders headers = new HttpHeaders();
|
headers.add(ZtConstant.header_key, ZtConstant.header_value);
|
//封装请求头
|
HttpEntity<MultiValueMap<String, Object>> formEntity = new HttpEntity<MultiValueMap<String, Object>>(headers);
|
try {
|
//有请求头,有参数请求
|
ResponseEntity<String> responseEntity =
|
restTemplate.exchange(ZtConstant.patr_person_api+"?is_over="+params.get("isOver"),
|
HttpMethod.GET,
|
formEntity,
|
String.class);
|
JSONObject jsonObject = JSON.parseObject(responseEntity.getBody());
|
JSONArray jsonArray = JSONArray.parseArray(jsonObject.get("resultList").toString());
|
// 返回
|
return jsonArray;
|
} catch (Exception e) {
|
e.printStackTrace();
|
}
|
return null;
|
}
|
}
|