新增cti通话

master
wjt 1 year ago
parent 2b36f99c27
commit d4663ba6c0

@ -0,0 +1,15 @@
package com.baiye.http;
import lombok.Data;
/**
* @author wjt
* @date 2023/6/2
*/
@Data
public class CtiCallResponse {
private String content;
private String reqId;
private Boolean result;
private String action;
}

@ -4,6 +4,7 @@ import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baiye.annotation.Inner;
import com.baiye.annotation.Log;
import com.baiye.common.CommonLog;
import com.baiye.constant.DefaultNumberConstants;
@ -86,7 +87,7 @@ public class TelephoneCallController {
telephoneCallReqDTO.setXGroup(company.getXGroup());
return telephoneCallService.axbDialNumber(telephoneCallReqDTO, companyId);
case DefaultNumberConstants.ONE_NUMBER:
return telephoneCallService.rollCallReq(telephoneCallReqDTO, companyId);
return telephoneCallService.ctiCallReq(telephoneCallReqDTO, companyId);
default:
return CommonResponse.createByErrorMessage("未配置呼叫");
}
@ -155,4 +156,34 @@ public class TelephoneCallController {
public CommonResponse<Object> rollCallStop(@RequestBody TelephoneCallStopDTO telephoneCallStopDTO) {
return telephoneCallService.rollCallStop(telephoneCallStopDTO);
}
@PostMapping("/cti/cdrUrl")
@ApiOperation("cti系统回调话单")
@Inner(value = false)
public CommonResponse<String> ctiCallBack(@RequestBody NfpCallInfoResponseDTO json) {
telephoneCallService.ctiCallBackInfo(json);
return CommonResponse.createBySuccess();
}
@ApiOperation("cti系统回调状态")
@Inner(value = false)
@PostMapping("/cti/status")
public CommonResponse<String> ctiCallBackStatus(@RequestBody NfpCallStatusResponseDTO json) {
log.info("cti回调状态{}", json);
return CommonResponse.createBySuccess();
}
@PostMapping("/cti/stop")
@ApiOperation("cti挂断呼叫")
public CommonResponse<Object> ctiCallStop(@RequestBody TelephoneCallStopDTO telephoneCallStopDTO) {
return telephoneCallService.ctiCallStop(telephoneCallStopDTO);
}
@GetMapping("/cti/set")
@ApiOperation("cti配置")
@Inner(value = false)
public CommonResponse<Object> ctiSet(@RequestParam("callBackUrl") String callBackUrl, @RequestParam("notifyUrl") String notifyUrl) {
return telephoneCallService.ctiSet(callBackUrl, notifyUrl);
}
}

@ -0,0 +1,44 @@
package com.baiye.modules.telemarkting.entity.dto;
import lombok.Data;
/**
* @author wjt
* @date 2023/6/2
*/
@Data
public class NfpCallInfoResponseDTO {
private String action;
private String orgCode;
private String reqId;
private Calllog calllog;
@Data
public static class Calllog {
private Boolean answer;
private String callBeginTime;
private String callId;
private String callInNum;
private Integer callTime;
private Integer callType;
private String calledAnswerTime;
private String calledBeginTime;
private String calledCity;
private String calledCityCode;
private String calledId;
private String calledIsp;
private String calledNum;
private String calledProvince;
private String calledRingTime;
private String caller;
private String callerAnswerTime;
private String callerRingTime;
private String crmStr;
private String hangupCode;
private String hangupTime;
private Boolean rec;
private String recPath;
private String showNum;
}
}

@ -0,0 +1,30 @@
package com.baiye.modules.telemarkting.entity.dto;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
/**
* @author wjt
* @date 2023/5/30
*/
@Data
public class NfpCallReqDTO {
private String reqId;
private String caller;
private String callee;
private String showNum;
@Value("${nfp.call.orgCode}")
private String orgCode;
private String crmStr;
private String action;
public NfpCallReqDTO addNfpCallReq(String reqId, String caller, String callee, String showNum) {
this.setReqId(reqId);
this.setCaller(caller);
this.setCallee(callee);
this.setShowNum(showNum);
this.setCrmStr(reqId);
this.setAction("1000");
return this;
}
}

@ -0,0 +1,18 @@
package com.baiye.modules.telemarkting.entity.dto;
import lombok.Data;
/**
* @author wjt
* @date 2023/6/2
*/
@Data
public class NfpCallStatusResponseDTO {
private String action;
private String called;
private String caller;
private String content;
private String orgCode;
private String reqId;
private Integer state;
}

@ -8,6 +8,7 @@ import lombok.Data;
*/
@Data
public class TelephoneCallStopDTO {
private String sessionid;
private String content;
private String callId;
private String sessionid;
}

@ -0,0 +1,85 @@
package com.baiye.modules.telemarkting.httpRequest;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baiye.common.CommonLog;
import com.baiye.exception.BadRequestException;
import com.baiye.http.CtiCallResponse;
import com.baiye.modules.telemarkting.entity.dto.NfpCallReqDTO;
import com.baiye.modules.telemarkting.entity.dto.TelephoneCallReqDTO;
import com.baiye.modules.telemarkting.entity.dto.TelephoneCallStopDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @author wjt
* @date 2023/5/30
*/
@Slf4j
@Component
public class CtiCallReq {
@Value("${cti.call.reqUrl}")
private String reqUrl;
@Value("${cti.call.setUrl}")
private String setUrl;
@Value("${cti.call.orgCode}")
private String orgCode;
public String startReq(TelephoneCallReqDTO req) {
NfpCallReqDTO reqDTO = new NfpCallReqDTO().addNfpCallReq(req.getRequestId(), req.getTelA(), req.getTelB(), String.valueOf(req.getDisplay()));
reqDTO.setOrgCode(orgCode);
CommonLog.info("CTI请求参数: " + BeanUtil.beanToMap(reqDTO));
try {
String httpResponse = sendCallReq(JSONUtil.toJsonPrettyStr(reqDTO), reqUrl).body();
CtiCallResponse ctiCallResponse = JSONUtil.toBean(httpResponse, CtiCallResponse.class);
if (ctiCallResponse.getResult()) {
return ctiCallResponse.getContent();
}
CommonLog.error("nfp请求失败,response===" + ctiCallResponse.getContent());
throw new BadRequestException("呼叫失败");
} catch (Exception e) {
log.error("method【reqTask】 roll_call error {}", e.getMessage());
e.printStackTrace();
}
throw new BadRequestException("呼叫失败");
}
public Boolean stopReq(TelephoneCallStopDTO telephoneCallStopDTO) {
JSONObject json = new JSONObject();
json.putOpt("reqId", telephoneCallStopDTO.getCallId());
json.putOpt("callId", telephoneCallStopDTO.getSessionid());
json.putOpt("orgCode", orgCode);
json.putOpt("action", "1001");
HttpResponse httpResponse = HttpRequest.post(reqUrl).body(JSONUtil.toJsonPrettyStr(json)).execute();
log.info("请求挂断,session={},{}", telephoneCallStopDTO.getSessionid(), httpResponse.body());
if (httpResponse.isOk()) {
return Boolean.TRUE;
}
return Boolean.FALSE;
}
public Boolean setReq(String callBackUrl, String notifyUrl) {
JSONObject json = new JSONObject();
json.putOpt("reqId", RandomUtil.randomString(10));
json.putOpt("orgCode", orgCode);
json.putOpt("callBackUrl", callBackUrl);
json.putOpt("notifyUrl", notifyUrl);
HttpResponse httpResponse = HttpRequest.post(setUrl).body(JSONUtil.toJsonPrettyStr(json)).execute();
log.info("请求配置,{}", httpResponse.body());
if (httpResponse.isOk()) {
return Boolean.TRUE;
}
return Boolean.FALSE;
}
private HttpResponse sendCallReq(String json, String url) {
return HttpRequest.post(url).body(json).execute();
}
}

@ -48,4 +48,39 @@ public interface TelephoneCallService {
*
*/
void rollCallBack(RollCallBackDTO rollCallBackDTO);
/**
* cti
*
* @param telephoneCallReqDTO
* @param companyId
* @return
*/
CommonResponse<TelephoneCallStopDTO> ctiCallReq(TelephoneCallReqDTO telephoneCallReqDTO, Long companyId);
/**
* cti
*
* @param telephoneCallStopDTO
* @return
*/
CommonResponse<Object> ctiCallStop(TelephoneCallStopDTO telephoneCallStopDTO);
/**
* cti
*
* @param callBackUrl
* @param notifyUrl
* @return
*/
CommonResponse<Object> ctiSet(String callBackUrl, String notifyUrl);
/**
* cti
*
* @param json
*/
void ctiCallBackInfo(NfpCallInfoResponseDTO json);
}

@ -78,7 +78,6 @@ public class ClueBoostServiceImpl implements ClueBoostService {
CommonLog.info("赔付线索:" + integer + "条");
}
@EventListener(ClueBoostDTO.class)
@Transactional(rollbackFor = Exception.class)
public void boost(ClueBoostDTO clueBoostDTO) {

@ -25,6 +25,7 @@ import com.baiye.modules.telemarkting.entity.*;
import com.baiye.modules.telemarkting.entity.dto.*;
import com.baiye.modules.telemarkting.httpRequest.AxbRequest;
import com.baiye.modules.telemarkting.httpRequest.DoubleCallReq;
import com.baiye.modules.telemarkting.httpRequest.CtiCallReq;
import com.baiye.modules.telemarkting.httpRequest.RollCallReq;
import com.baiye.modules.telemarkting.service.TelephoneCallService;
import com.baiye.util.RedisUtils;
@ -56,6 +57,8 @@ public class TelephoneCallServiceImpl implements TelephoneCallService {
@Resource
private RollCallReq rollCallReq;
@Resource
private CtiCallReq ctiCallReq;
@Resource
private AllCallInfoRepository allCallInfoRepository;
@Resource
private CallClueRepository callClueRepository;
@ -135,7 +138,7 @@ public class TelephoneCallServiceImpl implements TelephoneCallService {
UserDto user = userService.findById(allCallInfo.getMemberId());
Long whichUserId = user.getWhichUserId();
//实时扣费
callCostCount(allCallInfo.getMemberId(), allCallInfo.getClueId(), allCallInfo.getClueType(), allCallInfo.getDuration(), DefaultNumberConstants.TWO_NUMBER, whichUserId);
callCostCount(allCallInfo.getMemberId(), allCallInfo.getClueId(), allCallInfo.getDuration(), whichUserId);
//更新资源通话状态
CompletableFuture.runAsync(() -> updateSourceCallStatus(userDate, DefaultNumberConstants.TWO_NUMBER, allCallInfo.getClueType(), null));
} else {
@ -219,7 +222,7 @@ public class TelephoneCallServiceImpl implements TelephoneCallService {
//请求呼叫
String reqId = rollCallReq.startReq(telephoneCallReqDTO);
AllCallInfo allCallInfo = new AllCallInfo().init(requestId, requestId, Long.parseLong(telephoneCallReqDTO.getUserData()), telephoneCallReqDTO.getClueType(), telephoneCallReqDTO.getMemberId(), DefaultNumberConstants.ONE_NUMBER, DefaultNumberConstants.TWO_NUMBER);
AllCallInfo allCallInfo = new AllCallInfo().init(reqId, requestId, Long.parseLong(telephoneCallReqDTO.getUserData()), telephoneCallReqDTO.getClueType(), telephoneCallReqDTO.getMemberId(), DefaultNumberConstants.ONE_NUMBER, DefaultNumberConstants.TWO_NUMBER);
allCallInfoRepository.save(allCallInfo);
CallClueInfo clueInfo = new CallClueInfo().init(Long.parseLong(telephoneCallReqDTO.getUserData()), telephoneCallReqDTO.getTeamId(), telephoneCallReqDTO.getMemberId(), telephoneCallReqDTO.getTaskId(), companyId, DefaultNumberConstants.ONE_NUMBER);
@ -314,7 +317,7 @@ public class TelephoneCallServiceImpl implements TelephoneCallService {
//更新资源通话状态
CompletableFuture.runAsync(() -> updateSourceCallStatus(allCallInfo.getClueId(), DefaultNumberConstants.TWO_NUMBER, allCallInfo.getClueType(), traceId));
//实时扣除话费
callCostCount(allCallInfo.getMemberId(), allCallInfo.getClueId(), allCallInfo.getClueType(), Integer.valueOf(rollCallBackDTO.getDuration()), 2, whichUserId);
callCostCount(allCallInfo.getMemberId(), allCallInfo.getClueId(), Integer.valueOf(rollCallBackDTO.getDuration()), whichUserId);
status = true;
} else {
//更新资源通话状态
@ -330,6 +333,119 @@ public class TelephoneCallServiceImpl implements TelephoneCallService {
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public CommonResponse<TelephoneCallStopDTO> ctiCallReq(TelephoneCallReqDTO telephoneCallReqDTO, Long companyId) {
//检查余量
UserDto user = userService.findById(telephoneCallReqDTO.getMemberId());
Long whichUserId = user.getWhichUserId();
Integer surplusPhoneBillByUserId = channelCustomRepository.findSurplusPhoneBillByUserId(whichUserId);
if (surplusPhoneBillByUserId == null || surplusPhoneBillByUserId <= 0) {
CommonLog.error("话费不足");
return CommonResponse.createByErrorMessage("话费不足");
}
String requestId = RandomUtil.randomString(10);
telephoneCallReqDTO.setRequestId(requestId);
if (telephoneCallReqDTO.getDisplay() == null || telephoneCallReqDTO.getTelA() == null) {
//获取去显号和分机号
getNumberAndDisplay(telephoneCallReqDTO, companyId);
}
//获取被叫号
CallClueDTO body;
try {
body = sourceClueClient.queryDetailsByClueType(Long.parseLong(telephoneCallReqDTO.getUserData()), telephoneCallReqDTO.getClueType()).getBody();
} catch (Exception e) {
CommonLog.error("呼叫失败");
throw new BadRequestException("呼叫失败");
}
if (ObjectUtil.isNull(body) || StrUtil.isEmpty(body.getNid())) {
CommonLog.error("未获取到号码");
return CommonResponse.createByErrorMessage("未获取到号码");
}
if (body.getMemberId() == null || body.getIsFreeze()) {
CommonLog.error("线索异常");
return CommonResponse.createByErrorMessage("线索异常,请刷新后重试");
}
telephoneCallReqDTO.setTelB(body.getNid());
//请求呼叫
String content = ctiCallReq.startReq(telephoneCallReqDTO);
AllCallInfo allCallInfo = new AllCallInfo().init(requestId, requestId, Long.parseLong(telephoneCallReqDTO.getUserData()), telephoneCallReqDTO.getClueType(), telephoneCallReqDTO.getMemberId(), DefaultNumberConstants.ONE_NUMBER, DefaultNumberConstants.TWO_NUMBER);
allCallInfoRepository.save(allCallInfo);
CallClueInfo clueInfo = new CallClueInfo().init(Long.parseLong(telephoneCallReqDTO.getUserData()), telephoneCallReqDTO.getTeamId(), telephoneCallReqDTO.getMemberId(), telephoneCallReqDTO.getTaskId(), companyId, DefaultNumberConstants.ONE_NUMBER);
callClueRepository.save(clueInfo);
redisUtils.set(requestId, CommonLog.getTraceId(), 3, TimeUnit.HOURS);
TelephoneCallStopDTO telephoneCallStopDTO = new TelephoneCallStopDTO();
telephoneCallStopDTO.setSessionid(requestId);
telephoneCallStopDTO.setContent(content);
telephoneCallStopDTO.setCallId(requestId);
return CommonResponse.createBySuccess(telephoneCallStopDTO);
}
@Override
public CommonResponse<Object> ctiCallStop(TelephoneCallStopDTO telephoneCallStopDTO) {
if (ctiCallReq.stopReq(telephoneCallStopDTO)) {
return CommonResponse.createBySuccess();
}
return CommonResponse.createByError();
}
@Override
public CommonResponse<Object> ctiSet(String callBackUrl, String notifyUrl) {
if (ctiCallReq.setReq(callBackUrl, notifyUrl)) {
return CommonResponse.createBySuccess();
}
return CommonResponse.createByError();
}
@Override
@Transactional(rollbackFor = Exception.class)
public void ctiCallBackInfo(NfpCallInfoResponseDTO response) {
CommonLog.infoBusinessPartyType("CTI话单:" + response, BusinessPartyType.DB);
String sessionId = response.getCalllog().getCrmStr();
String traceId = String.valueOf(redisUtils.get(sessionId));
AllCallInfo allCallInfo = allCallInfoRepository.findBySessionId(sessionId);
if (ObjectUtil.isNotEmpty(allCallInfo)) {
UserDto user = userService.findById(allCallInfo.getMemberId());
Long whichUserId = user.getWhichUserId();
boolean status;
//拨打线索号的回调
if (response.getCalllog().getAnswer()) {
//表示接通,更新线索状态
callClueRepository.updateByStatus(DefaultNumberConstants.TWO_NUMBER, allCallInfo.getClueId());
allCallInfo.setStatus(DefaultNumberConstants.TWO_NUMBER);
allCallInfo.setDuration(response.getCalllog().getCallTime());
//更新资源通话状态
CompletableFuture.runAsync(() -> updateSourceCallStatus(allCallInfo.getClueId(), DefaultNumberConstants.TWO_NUMBER, allCallInfo.getClueType(), traceId));
//实时扣除话费
callCostCount(allCallInfo.getMemberId(), allCallInfo.getClueId(), response.getCalllog().getCallTime(), whichUserId);
status = true;
} else {
allCallInfo.setStatus(DefaultNumberConstants.ONE_NUMBER);
//更新资源通话状态
CompletableFuture.runAsync(() -> updateSourceCallStatus(allCallInfo.getClueId(), DefaultNumberConstants.ONE_NUMBER, allCallInfo.getClueType(), traceId));
status = false;
}
if (Arrays.asList(ClueTypeConstants.TOKER_TYPE).contains(allCallInfo.getClueType())) {
ClueBoostDTO clueBoostDTO = new ClueBoostDTO().addClueBoostDTO(allCallInfo.getClueId(), allCallInfo.getMemberId(), whichUserId, allCallInfo.getClueType(), null);
clueBoostDTO.setStatus(status);
applicationContext.publishEvent(clueBoostDTO);
}
if (response.getCalllog().getRec()) {
allCallInfo.setRecordFlag(1);
allCallInfo.setRecordFileDownloadUrl(response.getCalllog().getRecPath());
} else {
allCallInfo.setRecordFlag(0);
allCallInfo.setRecordFileDownloadUrl(null);
}
allCallInfoRepository.save(allCallInfo);
}
}
/**
*
*
@ -348,13 +464,10 @@ public class TelephoneCallServiceImpl implements TelephoneCallService {
*
* @param memberId id
* @param clueId 线id
* @param clueType 线
* @param duration
* @param callType 1- 2-
*/
public void callCostCount(Long memberId, Long clueId, Integer clueType, Integer duration, Integer callType, Long whichUserId) {
public void callCostCount(Long memberId, Long clueId, Integer duration, Long whichUserId) {
Long companyId = userService.findUserInfo(memberId).getCompanyId();
// Company company = companyService.findById(companyId);
int minDuration = dealDuration(duration);
CallDeduct callDeduct = new CallDeduct();
callDeduct.setDeductDuration(minDuration);
@ -366,52 +479,14 @@ public class TelephoneCallServiceImpl implements TelephoneCallService {
callDeduct.setVersion(0);
callDeduct.setType(DefaultNumberConstants.ONE_NUMBER);
//模板费用
// double fee = 0;
// String typeStr;
// if (callType == DefaultNumberConstants.ONE_NUMBER) {
// //双呼
// if (company.getDoubleCallFee() != null) {
// fee = company.getDoubleCallFee();
// }
// typeStr = "双呼套餐";
// } else {
// //点呼
// if (clueType == DefaultNumberConstants.THREE_NUMBER) {
// if (company.getTiktokCallFee() != null) {
// fee = company.getTiktokCallFee();
// }
// typeStr = "抖音套餐";
// } else if (clueType == DefaultNumberConstants.FOUR_NUMBER) {
// if (company.getDeliveryCallFee() != null) {
// fee = company.getDeliveryCallFee();
// }
// typeStr = "投流套餐";
// } else if (clueType == DefaultNumberConstants.FIVE_NUMBER) {
// if (company.getTalkCallFee() != null) {
// fee = company.getTalkCallFee();
// }
// typeStr = "拓客套餐";
// } else {
// if (company.getRollCallFee() != null) {
// fee = company.getRollCallFee();
// }
// typeStr = "crm套餐";
// }
// }
// double mul = NumberUtil.mul(minDuration, fee);
// callDeduct.setDeductAmount(mul);
callDeduct.setDeductAmount(0.0);
callDeduct.setComboType("话费");
CallDeduct save = callDeductRepository.save(callDeduct);
// updateBalance(mul, companyId, company.getVersion(), save, whichUserId);
updateBalance(save, whichUserId, minDuration);
}
private void updateBalance(CallDeduct save, Long whichUserId, Integer bill) {
// int num = companyService.updateBalanceOptimistic(mul, companyId, version);
int num = channelCustomRepository.updateSurplusBill(whichUserId, bill);
if (num > 0) {
save.setStatus(true);
@ -419,9 +494,6 @@ public class TelephoneCallServiceImpl implements TelephoneCallService {
} else {
CommonLog.error("话费扣除失败,记录id" + save.getId());
}
// else {
// updateBalance(mul, companyId, version + 1, save);
// }
}
/**

@ -148,6 +148,11 @@ roll:
#cdrUrl: https://baiyee.vip/api/roll/cdrUrl
#cdrUrl: http://localhost:8866/api/roll/cdrUrl
stopUrl: http://api.hzdaba.cn/v3/Accounts/default/Esl/Process
cti:
call:
reqUrl: https://cti.hzdaba.cn/cti/call-api
orgCode: org_by
setUrl: https://cti.hzdaba.cn/cti/call-set
alipay:
protocol: https
# 不需要加/gateway.do这是新旧SDK的区别切记

@ -152,6 +152,12 @@ roll:
cdrUrl: https://baiyee.vip/api/roll/cdrUrl
stopUrl: http://api.hzdaba.cn/v3/Accounts/default/Esl/Process
cti:
call:
reqUrl: https://cti.hzdaba.cn/cti/call-api
orgCode: org_by
setUrl: https://cti.hzdaba.cn/cti/call-set
alipay:
protocol: https
# 不需要加/gateway.do这是新旧SDK的区别切记

@ -972,7 +972,6 @@ public class ClueServiceImpl implements ClueService {
@Override
@Transactional(rollbackFor = Exception.class)
public void updateCallStatus(Long clueId, Integer clueCallStatus, Integer clueType) {
CommonLog.info("修改线索:" + clueId + " 通话状态为:" + clueCallStatus);
if (Arrays.asList(ClueTypeConstants.TOKER_TYPE).contains(clueType)) {
clueTalkRepository.updateCallStatus(clueId, clueCallStatus, DateUtil.now());
} else {

@ -3,21 +3,17 @@ package com.baiye.util;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Console;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONUtil;
import com.baiye.constant.DefaultNumberConstants;
import com.baiye.exception.BadRequestException;
import com.baiye.module.constant.SecretConstant;
import com.baiye.module.service.dto.SecretResponseBean;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.mchange.v2.log.log4j2.Log4j2MLog;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

Loading…
Cancel
Save