complete campaign to agent
This commit is contained in:
parent
c6c15c3c7e
commit
bab4c70c96
@ -3,8 +3,10 @@ package com.pudonghot.yo.fsagent.controller;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import com.wacai.tigon.mybatis.Search;
|
||||
import com.pudonghot.yo.model.domain.Tenant;
|
||||
import org.apache.commons.lang3.time.DateFormatUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.util.DigestUtils;
|
||||
import com.pudonghot.yo.fsagent.service.LocalApiService;
|
||||
import com.pudonghot.yo.fsagent.service.RecordingService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.servlet.view.freemarker.FreeMarkerView;
|
||||
|
||||
@ -15,8 +17,10 @@ import org.springframework.web.servlet.view.freemarker.FreeMarkerView;
|
||||
@Slf4j
|
||||
@RequestMapping(value = "/xml/dialplan")
|
||||
public class BaseDialplanController extends BaseXmlController {
|
||||
@Value("${yo.fsagent.recording.file-ext:.ogg}")
|
||||
protected String recordingFileExt;
|
||||
@Autowired
|
||||
private LocalApiService localApiService;
|
||||
@Autowired
|
||||
private RecordingService recordingService;
|
||||
|
||||
protected FreeMarkerView view(final String domain, final String viewFile) {
|
||||
final Tenant tenant = tenantService.find(
|
||||
@ -28,17 +32,18 @@ public class BaseDialplanController extends BaseXmlController {
|
||||
return empty();
|
||||
}
|
||||
|
||||
protected String recordingFile(
|
||||
protected FreeMarkerView recFile(
|
||||
final FreeMarkerView view,
|
||||
final String connId,
|
||||
final String thisDn,
|
||||
final String otherDn) {
|
||||
final String callerNumber,
|
||||
final String calledNumber) {
|
||||
|
||||
return "$${recordings_dir}/" +
|
||||
connId + "_" +
|
||||
DateFormatUtils.format(System.currentTimeMillis(),
|
||||
"yyyy-MM-dd_HH-mm-ss") + "_" +
|
||||
thisDn + "-" +
|
||||
otherDn + recordingFileExt;
|
||||
attr(view, "postRecUrl", localApiService.getPostRecUrl());
|
||||
return attr(view, "recLoc", recordingService.recLoc(connId, callerNumber, calledNumber));
|
||||
}
|
||||
|
||||
protected String md5(final String val) {
|
||||
return DigestUtils.md5DigestAsHex(val.getBytes());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -8,12 +8,9 @@ import com.pudonghot.yo.model.domain.Agent;
|
||||
import com.pudonghot.yo.model.domain.Trunk;
|
||||
import com.wacai.tigon.sequence.IdSequence;
|
||||
import com.pudonghot.yo.model.domain.Tenant;
|
||||
import org.springframework.util.DigestUtils;
|
||||
import com.pudonghot.yo.model.domain.AgentGroup;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import com.pudonghot.yo.cellphone.privacy.PrivacyLevel;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import com.pudonghot.yo.cellphone.privacy.NumberPrivacyUtils;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.servlet.view.freemarker.FreeMarkerView;
|
||||
@ -35,7 +32,7 @@ public class DialplanInboundController extends BaseDialplanController {
|
||||
@Autowired
|
||||
private AgentGroupService agentGroupService;
|
||||
@Autowired
|
||||
private LocalApiService localApiService;
|
||||
private NumberPrivacyService numberPrivacyService;
|
||||
|
||||
@RequestMapping(params = "Caller-Context=public")
|
||||
public FreeMarkerView inbound(
|
||||
@ -119,26 +116,9 @@ public class DialplanInboundController extends BaseDialplanController {
|
||||
|
||||
final String connId = idSeq.get();
|
||||
attr(view, "connId", connId);
|
||||
attr(view, "recordingLoc",
|
||||
recordingFile(connId,
|
||||
DigestUtils.md5DigestAsHex(ani.getBytes()), dnis + "-" + calledAgent.getAgent()));
|
||||
attr(view, "postRecUrl", localApiService.getPostRecUrl());
|
||||
recFile(view, connId, md5(ani), dnis + "-" + calledAgent.getAgent());
|
||||
|
||||
final AgentGroup agentGroup = agentGroupService.find(
|
||||
calledAgent.getGroupId());
|
||||
|
||||
final AgentGroup.PrivacyLevel privacyLevel = agentGroup != null ?
|
||||
agentGroup.getPrivacyLevel() : AgentGroup.PrivacyLevel.NONE;
|
||||
|
||||
if (privacyLevel != AgentGroup.PrivacyLevel.NONE) {
|
||||
attr(view, "callerIdNumber", "PRIVACY_INBOUND");
|
||||
attr(view, "callerIdName", NumberPrivacyUtils.mask(ani,
|
||||
PrivacyLevel.valueOf(privacyLevel.name())));
|
||||
}
|
||||
else {
|
||||
attr(view, "callerIdNumber", ani);
|
||||
attr(view, "callerIdName", ani);
|
||||
}
|
||||
attr(view, "privacyNumber", numberPrivacyService.generate(calledAgent, ani));
|
||||
|
||||
return view;
|
||||
}
|
||||
|
@ -7,8 +7,8 @@ import com.pudonghot.yo.model.domain.Queue;
|
||||
import com.pudonghot.yo.model.domain.Trunk;
|
||||
import com.pudonghot.yo.model.domain.Agent;
|
||||
import com.wacai.tigon.sequence.IdSequence;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.springframework.util.DigestUtils;
|
||||
import com.pudonghot.yo.model.domain.AgentGroup;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import com.pudonghot.yo.fsagent.util.CallStrUtils;
|
||||
@ -32,16 +32,17 @@ public class DialplanInternalController extends BaseDialplanController {
|
||||
@Autowired
|
||||
private QueueService queueService;
|
||||
@Autowired
|
||||
private LocalApiService localApiService;
|
||||
@Autowired
|
||||
private IdSequence idSeq;
|
||||
@Autowired
|
||||
private TrunkService trunkService;
|
||||
@Autowired
|
||||
private GatewayService gatewayService;
|
||||
@Autowired
|
||||
private NumberPrivacyService numberPrivacyService;
|
||||
|
||||
@RequestMapping(params = {
|
||||
"variable_origination_caller_id_number",
|
||||
"Channel-Name",
|
||||
"Caller-Context",
|
||||
"Caller-Callee-ID-Number",
|
||||
"Caller-Destination-Number"
|
||||
@ -50,16 +51,46 @@ public class DialplanInternalController extends BaseDialplanController {
|
||||
@RequestParam("Caller-Context")
|
||||
final String domain,
|
||||
@RequestParam("Caller-Callee-ID-Number")
|
||||
final String user,
|
||||
final String callerNumber,
|
||||
@RequestParam("Channel-Name")
|
||||
final String channel,
|
||||
@RequestParam("Caller-Destination-Number")
|
||||
final String calledNumber,
|
||||
@RequestParam
|
||||
final Map<String, String> params) {
|
||||
|
||||
log.info("XML originate dialplan of domain [{}] [{}] -> [{}].", domain, user, calledNumber);
|
||||
log.info("XML originate dialplan of domain [{}] [{}] -> [{}].", domain, callerNumber, calledNumber);
|
||||
log.debug("XML dialplan params [{}].", params);
|
||||
|
||||
return genDialplan(domain, user, calledNumber);
|
||||
final CallStrUtils.ChannelInfo channelInfo =
|
||||
CallStrUtils.parseChannelName(channel);
|
||||
|
||||
// campaign to agent
|
||||
if (!channelInfo.isAgent()) {
|
||||
final Pair<Agent, Boolean> targetAgent = findTargetAgent(calledNumber);
|
||||
// All is OK
|
||||
if (targetAgent.getRight()) {
|
||||
final Agent calledAgent = targetAgent.getLeft();
|
||||
if (calledAgent != null) {
|
||||
final FreeMarkerView view = view(domain, "campaign-to-agent.xml");
|
||||
final String connId = idSeq.get();
|
||||
attr(view, "connId", connId);
|
||||
attr(view, "calledAgent", calledAgent);
|
||||
|
||||
final String otherDn = StringUtils.defaultIfBlank(
|
||||
params.get("variable_x_called_number"), callerNumber);
|
||||
attr(view, "privacyNumber",
|
||||
numberPrivacyService.generate(calledAgent, otherDn));
|
||||
recFile(view, connId, calledAgent.getAccount(), md5(otherDn));
|
||||
return view;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return empty();
|
||||
}
|
||||
}
|
||||
|
||||
return genCallerAgentDialplan(domain, callerNumber, calledNumber);
|
||||
}
|
||||
|
||||
@RequestMapping(params = {
|
||||
@ -71,85 +102,45 @@ public class DialplanInternalController extends BaseDialplanController {
|
||||
@RequestParam("Caller-Context")
|
||||
final String domain,
|
||||
@RequestParam("Caller-Username")
|
||||
final String user,
|
||||
final String callerUser,
|
||||
@RequestParam("Caller-Destination-Number")
|
||||
final String calledNumber,
|
||||
@RequestParam
|
||||
final Map<String, String> params) {
|
||||
|
||||
log.info("XML dialplan of domain [{}] [{}] -> [{}].", domain, user, calledNumber);
|
||||
log.info("XML dialplan of domain [{}] [{}] -> [{}].", domain, callerUser, calledNumber);
|
||||
log.debug("XML dialplan params [{}].", params);
|
||||
return genDialplan(domain, user, calledNumber);
|
||||
return genCallerAgentDialplan(domain, callerUser, calledNumber);
|
||||
}
|
||||
|
||||
public FreeMarkerView genDialplan(
|
||||
public FreeMarkerView genCallerAgentDialplan(
|
||||
final String domain,
|
||||
final String user,
|
||||
final String callerUser,
|
||||
final String calledNumber) {
|
||||
|
||||
final Agent callerAgent =
|
||||
agentService.findByDomainAndAgent(domain, user);
|
||||
agentService.findByDomainAndAgent(domain, callerUser);
|
||||
|
||||
if (callerAgent == null) {
|
||||
log.warn("Not caller agent [{}@{}] found.", user, domain);
|
||||
log.warn("Not caller agent [{}@{}] found.", callerUser, domain);
|
||||
return empty();
|
||||
}
|
||||
|
||||
if (!callerAgent.getActive()) {
|
||||
log.warn("Caller agent [{}@{}] is not active.", user, domain);
|
||||
log.warn("Caller agent [{}@{}] is not active.", callerUser, domain);
|
||||
return empty();
|
||||
}
|
||||
|
||||
final DialTarget dialTarget =
|
||||
CallStrUtils.parseDialTarget(calledNumber);
|
||||
|
||||
if (dialTarget != null) {
|
||||
final Integer dialTargetId = dialTarget.getId();
|
||||
final DialTarget.Type dialTargetType = dialTarget.getType();
|
||||
|
||||
if (dialTargetType == DialTarget.Type.AGENT) {
|
||||
final Agent calledAgent = agentService.find(dialTargetId);
|
||||
if (calledAgent != null) {
|
||||
return agentCallAgent(domain, callerAgent, calledAgent);
|
||||
}
|
||||
|
||||
log.warn("No agent found of dial target [{}].", calledNumber);
|
||||
return empty();
|
||||
final Pair<Agent, Boolean> targetAgent = findTargetAgent(calledNumber);
|
||||
// All is OK
|
||||
if (targetAgent.getRight()) {
|
||||
final Agent calledAgent = targetAgent.getLeft();
|
||||
if (calledAgent != null) {
|
||||
return agentCallAgent(domain, callerAgent, calledAgent);
|
||||
}
|
||||
else if (dialTargetType == DialTarget.Type.AGENT_GROUP) {
|
||||
final AgentGroup agentGroup = agentGroupService.find(dialTargetId);
|
||||
if (agentGroup != null) {
|
||||
final Agent calledAgent =
|
||||
agentService.lockIdleOfGroup(dialTargetId);
|
||||
if (calledAgent != null) {
|
||||
return agentCallAgent(domain, callerAgent, calledAgent);
|
||||
}
|
||||
|
||||
log.warn("No idle agent found of group [{}].", calledNumber);
|
||||
return empty();
|
||||
}
|
||||
|
||||
log.warn("No agent group found of dial target [{}].", calledNumber);
|
||||
return empty();
|
||||
}
|
||||
else if (dialTargetType == DialTarget.Type.QUEUE) {
|
||||
final Queue queue = queueService.find(dialTargetId);
|
||||
if (queue != null) {
|
||||
final Agent calledAgent =
|
||||
agentService.lockIdleOfQueue(dialTargetId);
|
||||
if (calledAgent != null) {
|
||||
return agentCallAgent(domain, callerAgent, calledAgent);
|
||||
}
|
||||
|
||||
log.warn("No idle agent found of queue [{}].", calledNumber);
|
||||
return empty();
|
||||
}
|
||||
|
||||
log.warn("No queue found of dial target [{}].", calledNumber);
|
||||
return empty();
|
||||
}
|
||||
|
||||
log.error("Never here!!!");
|
||||
}
|
||||
else {
|
||||
return empty();
|
||||
}
|
||||
|
||||
final Pair<Trunk, String> trunkInfo = trunkService.parseDialTarget(
|
||||
@ -157,7 +148,6 @@ public class DialplanInternalController extends BaseDialplanController {
|
||||
if (trunkInfo != null) {
|
||||
log.info("Trunk outbound call found.");
|
||||
final Trunk trunk = trunkInfo.getLeft();
|
||||
final String targetNumber = trunkInfo.getRight();
|
||||
|
||||
final FreeMarkerView view = view(domain, "trunk-outbound.xml");
|
||||
attr(view, "trunk", trunk);
|
||||
@ -166,12 +156,11 @@ public class DialplanInternalController extends BaseDialplanController {
|
||||
attr(view, "callerAgent", callerAgent);
|
||||
attr(view, "gatewayName",
|
||||
gatewayService.genGatewayName(trunk.getGatewayId()));
|
||||
attr(view, "calledNumber", calledNumber);
|
||||
|
||||
// number without trunk prefix
|
||||
final String targetNumber = trunkInfo.getRight();
|
||||
attr(view, "targetNumber", targetNumber);
|
||||
attr(view, "recordingLoc", recordingFile(connId,
|
||||
callerAgent.getAccount(),
|
||||
DigestUtils.md5DigestAsHex(targetNumber.getBytes())));
|
||||
attr(view, "postRecUrl", localApiService.getPostRecUrl());
|
||||
recFile(view, connId, callerAgent.getAccount(), md5(targetNumber));
|
||||
return view;
|
||||
}
|
||||
|
||||
@ -187,16 +176,75 @@ public class DialplanInternalController extends BaseDialplanController {
|
||||
return empty();
|
||||
}
|
||||
|
||||
private FreeMarkerView agentCallAgent(final String domain, Agent callerAgent, Agent calledAgent) {
|
||||
final FreeMarkerView view = view(domain, "local-extension.xml");
|
||||
private FreeMarkerView agentCallAgent(final String domain, final Agent callerAgent, final Agent calledAgent) {
|
||||
final FreeMarkerView view = view(domain, "agent-to-agent.xml");
|
||||
final String connId = idSeq.get();
|
||||
attr(view, "connId", connId);
|
||||
attr(view, "callerAgent", callerAgent);
|
||||
attr(view, "calledAgent", calledAgent);
|
||||
attr(view, "recordingLoc", recordingFile(connId,
|
||||
callerAgent.getAccount(),
|
||||
calledAgent.getAccount()));
|
||||
attr(view, "postRecUrl", localApiService.getPostRecUrl());
|
||||
recFile(view, connId, callerAgent.getAccount(), calledAgent.getAccount());
|
||||
return view;
|
||||
}
|
||||
|
||||
/**
|
||||
* find target agent
|
||||
* @param calledNumber called number
|
||||
* @return <Agent found, All is OK>
|
||||
*/
|
||||
private Pair<Agent, Boolean> findTargetAgent(final String calledNumber) {
|
||||
final DialTarget dialTarget =
|
||||
CallStrUtils.parseDialTarget(calledNumber);
|
||||
|
||||
if (dialTarget != null) {
|
||||
final Integer dialTargetId = dialTarget.getId();
|
||||
final DialTarget.Type dialTargetType = dialTarget.getType();
|
||||
|
||||
if (dialTargetType == DialTarget.Type.AGENT) {
|
||||
final Agent calledAgent = agentService.find(dialTargetId);
|
||||
if (calledAgent != null) {
|
||||
return Pair.of(calledAgent, true);
|
||||
}
|
||||
|
||||
log.warn("No agent found of dial target [{}].", calledNumber);
|
||||
return Pair.of(null, false);
|
||||
}
|
||||
else if (dialTargetType == DialTarget.Type.AGENT_GROUP) {
|
||||
final AgentGroup agentGroup = agentGroupService.find(dialTargetId);
|
||||
if (agentGroup != null) {
|
||||
final Agent calledAgent =
|
||||
agentService.lockIdleOfGroup(dialTargetId);
|
||||
if (calledAgent != null) {
|
||||
return Pair.of(calledAgent, true);
|
||||
}
|
||||
|
||||
log.warn("No idle agent found of group [{}].", calledNumber);
|
||||
return Pair.of(null, false);
|
||||
}
|
||||
|
||||
log.warn("No agent group found of dial target [{}].", calledNumber);
|
||||
return Pair.of(null, false);
|
||||
}
|
||||
else if (dialTargetType == DialTarget.Type.QUEUE) {
|
||||
final Queue queue = queueService.find(dialTargetId);
|
||||
if (queue != null) {
|
||||
final Agent calledAgent =
|
||||
agentService.lockIdleOfQueue(dialTargetId);
|
||||
if (calledAgent != null) {
|
||||
return Pair.of(calledAgent, true);
|
||||
}
|
||||
|
||||
log.warn("No idle agent found of queue [{}].", calledNumber);
|
||||
return Pair.of(null, false);
|
||||
}
|
||||
|
||||
log.warn("No queue found of dial target [{}].", calledNumber);
|
||||
return Pair.of(null, false);
|
||||
}
|
||||
|
||||
log.error("Never here!!!");
|
||||
}
|
||||
|
||||
return Pair.of(null, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.pudonghot.yo.fsagent.controller;
|
||||
|
||||
import com.pudonghot.yo.fsagent.service.PostRecordingService;
|
||||
import com.pudonghot.yo.fsagent.service.RecordingService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@ -15,7 +15,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
@Controller
|
||||
public class PostRecordingController {
|
||||
@Autowired
|
||||
private PostRecordingService postRecordingService;
|
||||
private RecordingService recordingService;
|
||||
|
||||
@PostMapping("/post-rec")
|
||||
public void postRecording(
|
||||
@ -23,6 +23,6 @@ public class PostRecordingController {
|
||||
final Integer tenantId,
|
||||
@RequestParam("loc")
|
||||
final String location) {
|
||||
postRecordingService.postRecording(tenantId, location);
|
||||
recordingService.postRecording(tenantId, location);
|
||||
}
|
||||
}
|
||||
|
@ -48,10 +48,10 @@ public class ChannelDestroy {
|
||||
public void onChannelDestroy(final Event event) {
|
||||
log.debug("On channel destroy event [{}] [{}].", event, event.getHeaders());
|
||||
|
||||
final String presenceId = event.getHeader("variable_presence_id");
|
||||
final String callUuid = event.getCallUuid();
|
||||
log.info("On channel [{}][{}] destroy event.", presenceId, callUuid);
|
||||
final ChannelInfo channelInfo = CallStrUtils.getChannelInfo(event);
|
||||
final String channelName = event.getChannelName();
|
||||
log.info("On channel [{}][{}] destroy event.", channelName, callUuid);
|
||||
final ChannelInfo channelInfo = CallStrUtils.parseChannelName(channelName);
|
||||
|
||||
if (channelInfo.isAgent()) {
|
||||
|
||||
@ -60,7 +60,8 @@ public class ChannelDestroy {
|
||||
channelInfo.getNumber());
|
||||
|
||||
boolean acw = false;
|
||||
final String strDialType = event.getHeader("x_dial_type");
|
||||
final String strDialType = event.getHeader("variable_x_dial_type");
|
||||
|
||||
if (StringUtils.isNotBlank(strDialType)) {
|
||||
final DialType dialType = DialType.valueOf(strDialType);
|
||||
|
||||
|
@ -0,0 +1,19 @@
|
||||
package com.pudonghot.yo.fsagent.service;
|
||||
|
||||
import com.pudonghot.yo.model.domain.Agent;
|
||||
import com.pudonghot.yo.basic.model.PrivacyNumber;
|
||||
|
||||
/**
|
||||
* @author Donghuang
|
||||
* @date Jul 12, 2020 12:24:54
|
||||
*/
|
||||
public interface NumberPrivacyService {
|
||||
|
||||
/**
|
||||
* generate privacy number
|
||||
* @param agent agent
|
||||
* @param number raw number
|
||||
* @return number privacy
|
||||
*/
|
||||
PrivacyNumber generate(Agent agent, String number);
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package com.pudonghot.yo.fsagent.service;
|
||||
|
||||
/**
|
||||
* @author Donghuang <br>
|
||||
* Jan 06, 2020 18:09:36
|
||||
*/
|
||||
public interface PostRecordingService {
|
||||
|
||||
/**
|
||||
* post recording
|
||||
*
|
||||
* @param tenantId tenant id
|
||||
* @param location recording location
|
||||
*/
|
||||
void postRecording(Integer tenantId, String location);
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package com.pudonghot.yo.fsagent.service;
|
||||
|
||||
/**
|
||||
* @author Donghuang <br>
|
||||
* Jan 06, 2020 18:09:36
|
||||
*/
|
||||
public interface RecordingService {
|
||||
|
||||
/**
|
||||
* post recording
|
||||
*
|
||||
* @param tenantId tenant id
|
||||
* @param location recording location
|
||||
*/
|
||||
void postRecording(Integer tenantId, String location);
|
||||
|
||||
/**
|
||||
* generate recording location
|
||||
*
|
||||
* @param connId conn id
|
||||
* @param callerNumber caller number
|
||||
* @param calledNumber called number
|
||||
* @return recording location
|
||||
*/
|
||||
String recLoc(String connId, String callerNumber, String calledNumber);
|
||||
}
|
@ -12,8 +12,8 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import com.pudonghot.yo.mapper.AgentGroupMapper;
|
||||
import com.pudonghot.yo.model.domain.AgentGroup;
|
||||
import org.freeswitch.esl.client.inbound.Client;
|
||||
import org.apache.commons.lang3.time.DateFormatUtils;
|
||||
import com.pudonghot.yo.model.domain.CallDetailRecord;
|
||||
import com.pudonghot.yo.fsagent.service.RecordingService;
|
||||
import com.pudonghot.yo.fsagent.service.LocalApiService;
|
||||
import static org.apache.commons.lang3.StringUtils.join;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@ -33,14 +33,12 @@ abstract class AbstractDialer<LegB> {
|
||||
private IdSequence idSeq;
|
||||
@Autowired
|
||||
private AgentGroupMapper agentGroupMapper;
|
||||
@Value("${yo.fsagent.recording.dir:/var/lib/freeswitch/recordings}")
|
||||
private String recDir;
|
||||
@Value("${yo.fsagent.recording.file-ext:.ogg}")
|
||||
private String recordingFileExt;
|
||||
@Value("${yo.fsagent.default-codec:PCMA}")
|
||||
private String defaultCodec;
|
||||
@Autowired
|
||||
private LocalApiService localApiService;
|
||||
@Autowired
|
||||
private RecordingService recordingService;
|
||||
|
||||
/**
|
||||
* dial
|
||||
@ -93,8 +91,6 @@ abstract class AbstractDialer<LegB> {
|
||||
* @return dial string
|
||||
*/
|
||||
protected String dialStrLegA(final DialArg<LegB> arg) {
|
||||
// TODO sofia/internal/
|
||||
// sofia/internal/700001@d1.wacai.info
|
||||
return "user/" + arg.getAgentLegA().getAgent() + "@" + arg.getTenant().getRealm();
|
||||
}
|
||||
|
||||
@ -281,11 +277,6 @@ abstract class AbstractDialer<LegB> {
|
||||
}
|
||||
|
||||
private String recLoc(final DialArg<LegB> arg) {
|
||||
return recDir + "/" + arg.getConnId() + "_" +
|
||||
DateFormatUtils.format(
|
||||
System.currentTimeMillis(), "yyyy-MM-dd_HH-mm-ss") + "_" +
|
||||
arg.getAgentLegA().getAccount() + "-" +
|
||||
recNumLegB(arg) +
|
||||
recordingFileExt;
|
||||
return recordingService.recLoc(arg.getConnId(), arg.getAgentLegA().getAccount(), recNumLegB(arg));
|
||||
}
|
||||
}
|
||||
|
@ -135,7 +135,6 @@ public class DialServiceImpl implements DialService {
|
||||
Assert.state(otherAgent.getActive(),
|
||||
() -> "Other agent [" + otherAgentAccount + "] is not active");
|
||||
req.setCalledNumber(otherAgent.getAgent());
|
||||
req.setCalledName(otherAgentAccount);
|
||||
return dialInternal(idSeq.get(), req, tenant, agent, otherAgent);
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,10 @@
|
||||
package com.pudonghot.yo.fsagent.service.dubbo.impl;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.pudonghot.yo.model.domain.Agent;
|
||||
import com.pudonghot.yo.model.domain.CallDetailRecord;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import com.pudonghot.yo.model.domain.Agent;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.pudonghot.yo.model.domain.CallDetailRecord;
|
||||
|
||||
/**
|
||||
* @author Donghuang <br>
|
||||
@ -30,9 +29,10 @@ public class DialerAgent extends AbstractDialer<Agent> {
|
||||
protected void extVarsLegA(final DialArg<Agent> arg, final List<String> vars) {
|
||||
final Agent legB = arg.getLegB();
|
||||
final String calledNumber = legB.getAgent();
|
||||
vars.add("sip_contact_user=" + calledNumber);
|
||||
|
||||
vars.add("origination_caller_id_number=" + calledNumber);
|
||||
vars.add("origination_caller_id_name=" + legB.getAccount());
|
||||
vars.add("sip_contact_user=" + calledNumber);
|
||||
|
||||
vars.add(callTypeVar(CallDetailRecord.CallType.INTERNAL));
|
||||
vars.add(headerCallTypeVar(CallDetailRecord.CallType.INTERNAL));
|
||||
|
@ -2,17 +2,15 @@ package com.pudonghot.yo.fsagent.service.dubbo.impl;
|
||||
|
||||
import java.util.List;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import com.pudonghot.yo.model.domain.Agent;
|
||||
import com.pudonghot.yo.model.domain.Trunk;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.util.DigestUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.pudonghot.yo.basic.model.PrivacyNumber;
|
||||
import com.pudonghot.yo.fsagent.service.TrunkService;
|
||||
import com.pudonghot.yo.model.domain.CallDetailRecord;
|
||||
import com.pudonghot.yo.cellphone.privacy.PrivacyLevel;
|
||||
import com.pudonghot.yo.fsagent.api.request.BaseReqDial;
|
||||
import com.pudonghot.yo.cellphone.privacy.NumberPrivacyUtils;
|
||||
import com.pudonghot.yo.fsagent.service.NumberPrivacyService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
@ -24,6 +22,8 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
public class DialerOutside extends AbstractDialer<Pair<Integer, String>> {
|
||||
@Autowired
|
||||
private TrunkService trunkService;
|
||||
@Autowired
|
||||
private NumberPrivacyService numberPrivacyService;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
@ -36,11 +36,13 @@ public class DialerOutside extends AbstractDialer<Pair<Integer, String>> {
|
||||
vars.add(callTypeVar(CallDetailRecord.CallType.OUTBOUND));
|
||||
vars.add(headerCallTypeVar(CallDetailRecord.CallType.OUTBOUND));
|
||||
vars.add(headerCallDirectionVar(CallDetailRecord.CallDirection.OUTGOING));
|
||||
|
||||
final PrivacyNumber privacyNumber =
|
||||
numberPrivacyService.generate(arg.getAgentLegA(), arg.getLegB().getRight());
|
||||
// hide customer info
|
||||
vars.add("sip_contact_user=SYSTEM");
|
||||
vars.add("origination_caller_id_number=SYSTEM");
|
||||
vars.add("origination_caller_id_name=" +
|
||||
calledName(arg.getReq(), arg.getAgentLegA(), arg.getLegB().getRight()));
|
||||
vars.add("origination_caller_id_number=" + privacyNumber.getNumber());
|
||||
vars.add("origination_caller_id_name=" + privacyNumber.getName());
|
||||
vars.add("sip_contact_user=" + privacyNumber.getNumber());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -78,13 +80,4 @@ public class DialerOutside extends AbstractDialer<Pair<Integer, String>> {
|
||||
protected String dialStrLegB(DialArg<Pair<Integer, String>> arg) {
|
||||
return ((Pair<Trunk, String>) arg.getData()).getRight();
|
||||
}
|
||||
|
||||
String calledName(final BaseReqDial req, final Agent agent, final String calledNumber) {
|
||||
final String calledName = req.getCalledName();
|
||||
if (StringUtils.isNotBlank(calledName)) {
|
||||
return calledName;
|
||||
}
|
||||
return NumberPrivacyUtils.mask(calledNumber,
|
||||
PrivacyLevel.valueOf(findValidAgentGroup(agent).getPrivacyLevel().name()));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,46 @@
|
||||
package com.pudonghot.yo.fsagent.service.impl;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import com.pudonghot.yo.model.domain.Agent;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.pudonghot.yo.mapper.AgentGroupMapper;
|
||||
import com.pudonghot.yo.model.domain.AgentGroup;
|
||||
import com.pudonghot.yo.basic.model.PrivacyLevel;
|
||||
import com.pudonghot.yo.basic.model.PrivacyNumber;
|
||||
import com.pudonghot.yo.cellphone.privacy.NumberPrivacyUtils;
|
||||
import com.pudonghot.yo.fsagent.service.NumberPrivacyService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* @author Donghuang
|
||||
* @date Jul 12, 2020 12:28:01
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class NumberPrivacyServiceImpl implements NumberPrivacyService {
|
||||
@Autowired
|
||||
private AgentGroupMapper agentGroupMapper;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public PrivacyNumber generate(final Agent agent, final String number) {
|
||||
final AgentGroup agentGroup =
|
||||
agentGroupMapper.find(agent.getGroupId());
|
||||
|
||||
if (agentGroup != null && agentGroup.getActive()) {
|
||||
final PrivacyLevel privacyLevel = agentGroup.getPrivacyLevel();
|
||||
if (privacyLevel != PrivacyLevel.NONE) {
|
||||
return new PrivacyNumber(PrivacyNumber.PRIVACY,
|
||||
NumberPrivacyUtils.mask(number, privacyLevel));
|
||||
}
|
||||
return new PrivacyNumber(number, number);
|
||||
}
|
||||
|
||||
// default medium
|
||||
log.warn("No valid agent [{}] group found, privacy number medium.", agent);
|
||||
return new PrivacyNumber(PrivacyNumber.PRIVACY,
|
||||
NumberPrivacyUtils.mask(number, PrivacyLevel.MEDIUM));
|
||||
}
|
||||
}
|
@ -3,22 +3,22 @@ package com.pudonghot.yo.fsagent.service.impl;
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.pudonghot.yo.fsagent.service.PostRecordingService;
|
||||
import com.pudonghot.yo.mapper.CallRecordingMapper;
|
||||
import com.pudonghot.yo.mapper.TenantMapper;
|
||||
import com.pudonghot.yo.model.domain.CallRecording;
|
||||
import com.pudonghot.yo.model.domain.Tenant;
|
||||
import com.pudonghot.yo.common.httpclient.HttpClient;
|
||||
import com.pudonghot.yo.common.httpclient.HttpRequestResult;
|
||||
import com.pudonghot.yo.common.httpclient.RequestBodyParams;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.Assert;
|
||||
import com.pudonghot.yo.mapper.TenantMapper;
|
||||
import com.pudonghot.yo.model.domain.Tenant;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.pudonghot.yo.mapper.CallRecordingMapper;
|
||||
import com.pudonghot.yo.model.domain.CallRecording;
|
||||
import com.pudonghot.yo.common.httpclient.HttpClient;
|
||||
import org.apache.commons.lang3.time.DateFormatUtils;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import com.pudonghot.yo.fsagent.service.RecordingService;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import com.pudonghot.yo.common.httpclient.HttpRequestResult;
|
||||
import com.pudonghot.yo.common.httpclient.RequestBodyParams;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
@ -27,8 +27,8 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class PostRecordingServiceImpl
|
||||
implements PostRecordingService, InitializingBean {
|
||||
public class RecordingServiceImpl
|
||||
implements RecordingService, InitializingBean {
|
||||
@Autowired
|
||||
private HttpClient httpClient;
|
||||
@Value("${yo.fsagent.recording-server.base-path}")
|
||||
@ -40,6 +40,10 @@ public class PostRecordingServiceImpl
|
||||
private TenantMapper tenantMapper;
|
||||
@Autowired
|
||||
private CallRecordingMapper callRecordingMapper;
|
||||
@Value("${yo.fsagent.recording.dir:/var/lib/freeswitch/recordings}")
|
||||
private String recDir;
|
||||
@Value("${yo.fsagent.recording.file-ext:.ogg}")
|
||||
private String recordingFileExt;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
@ -86,6 +90,19 @@ public class PostRecordingServiceImpl
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String recLoc(final String connId, final String callerNumber, final String calledNumber) {
|
||||
return recDir + "/" + connId + "_" +
|
||||
DateFormatUtils.format(
|
||||
System.currentTimeMillis(), "yyyy-MM-dd_HH-mm-ss") + "_" +
|
||||
callerNumber + "-" +
|
||||
calledNumber +
|
||||
recordingFileExt;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
@ -6,8 +6,9 @@ spring.jackson.serialization.write-dates-as-timestamps=true
|
||||
spring.jackson.serialization.fail-on-empty-beans=false
|
||||
site.context-path=
|
||||
|
||||
# spring.freemarker.template-loader-path=classpath:/templates
|
||||
# spring.freemarker.suffix=.ftl
|
||||
spring.freemarker.template-loader-path=classpath:/templates
|
||||
spring.freemarker.suffix=
|
||||
spring.freemarker.cache=false
|
||||
|
||||
# Datasource
|
||||
yo.datasource.url=jdbc:mysql://172.18.4.35/yoqw?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai
|
||||
|
@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="freeswitch/xml">
|
||||
<section name="dialplan" description="Dialplan Agent To Agent">
|
||||
<context name="${tenant.realm}">
|
||||
<extension name="AgentToAgent">
|
||||
<condition>
|
||||
<!-- <#noparse> -->
|
||||
<action application="set" data="odbc-cdr-ignore-leg=true" />
|
||||
<action application="set" data="absolute_codec_string=PCMA" />
|
||||
<!--<action application="set" data="ringback=%(2000,4000,440,480)"/>-->
|
||||
<action application="set" data="ringback=$${us-ring}" />
|
||||
<!--<action application="set" data="transfer_ringback=$${hold_music}" />-->
|
||||
<action application="set" data="call_timeout=30" />
|
||||
<action application="set" data="hangup_after_bridge=true" />
|
||||
<action application="set" data="continue_on_fail=true" />
|
||||
<!-- </#noparse> -->
|
||||
|
||||
<action application="set" data="x_role=CALLER" />
|
||||
<action application="set" data="x_tenant_id=${tenant.id}" />
|
||||
<action application="set" data="x_tenant_code=${tenant.code}" />
|
||||
<action application="set" data="x_account=${callerAgent.account}" />
|
||||
<action application="set" data="x_conn_id=${connId}" />
|
||||
|
||||
<action application="set" data="x_dial_type=MANUAL" />
|
||||
<action application="set" data="x_call_type=INTERNAL" />
|
||||
<action application="set" data="x_agent_type=${callerAgent.type}" />
|
||||
|
||||
<!-- Recording -->
|
||||
<#include "/dialplan/rec.xml">
|
||||
<!-- /Recording -->
|
||||
|
||||
<!--
|
||||
<#assign bridgeVars = [
|
||||
"originate_timeout=30",
|
||||
"odbc-cdr-ignore-leg=false",
|
||||
"origination_uuid=${connId}",
|
||||
"sip_invite_call_id=${r'${sip_call_id}'}",
|
||||
"sip_contact_user=${r'${caller_id_number}'}",
|
||||
|
||||
"x_role=CALLED",
|
||||
"x_tenant_id=${tenant.id}",
|
||||
"x_tenant_code=${tenant.code}",
|
||||
"x_account=${callerAgent.account}",
|
||||
"x_agent_type=${callerAgent.type}",
|
||||
"x_called_number=${calledAgent.agent}",
|
||||
|
||||
"x_conn_id=${connId}",
|
||||
"x_dial_type=MANUAL",
|
||||
"x_call_type=INTERNAL",
|
||||
|
||||
"sip_h_X-Dial-Type=MANUAL",
|
||||
"sip_h_X-Call-Type=INTERNAL",
|
||||
"sip_h_X-Call-Direction=INCOMING"
|
||||
]>
|
||||
-->
|
||||
|
||||
<action application="bridge" data="[${bridgeVars?join(',')}]user/${calledAgent.agent}@${tenant.realm}" />
|
||||
</condition>
|
||||
</extension>
|
||||
</context>
|
||||
</section>
|
||||
</document>
|
||||
|
@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="freeswitch/xml">
|
||||
<section name="dialplan" description="Campain To Agent">
|
||||
<context name="${tenant.realm}">
|
||||
<extension name="CampaignToAgent">
|
||||
<condition>
|
||||
<!-- <#noparse> -->
|
||||
<action application="set" data="absolute_codec_string=PCMA" />
|
||||
<!--<action application="set" data="ringback=$${us-ring}" />-->
|
||||
<!--<action application="set" data="transfer_ringback=$${hold_music}" />-->
|
||||
<action application="set" data="call_timeout=30" />
|
||||
<action application="set" data="hangup_after_bridge=true" />
|
||||
<action application="set" data="continue_on_fail=true" />
|
||||
<!-- </#noparse> -->
|
||||
|
||||
<action application="set" data="x_conn_id=${connId}" />
|
||||
<action application="set" data="x_role=CALLED" />
|
||||
<action application="set" data="x_tenant_id=${tenant.id}" />
|
||||
<action application="set" data="x_tenant_code=${tenant.code}" />
|
||||
<action application="set" data="x_account=${calledAgent.account}" />
|
||||
|
||||
<action application="set" data="x_dial_type=CAMPAIGN" />
|
||||
<action application="set" data="x_call_type=OUTBOUND" />
|
||||
<action application="set" data="x_agent_type=${calledAgent.type}" />
|
||||
|
||||
<!-- Recording -->
|
||||
<#include "/dialplan/rec.xml">
|
||||
<!-- /Recording -->
|
||||
|
||||
<!--
|
||||
<#assign bridgeVars = [
|
||||
"sip_auto_answer=true",
|
||||
"originate_timeout=30",
|
||||
"odbc-cdr-ignore-leg=true",
|
||||
"origination_uuid=${connId}",
|
||||
|
||||
"origination_caller_id_number=${privacyNumber.number}",
|
||||
"origination_caller_id_name=${privacyNumber.name}",
|
||||
"sip_contact_user=${privacyNumber.number}",
|
||||
|
||||
"sip_invite_call_id=${r'${sip_call_id}'}",
|
||||
|
||||
"x_role=CALLER",
|
||||
"x_account=${calledAgent.account}",
|
||||
"x_agent_type=${calledAgent.type}",
|
||||
|
||||
"x_tenant_id=${tenant.id}",
|
||||
"x_tenant_code=${tenant.code}",
|
||||
|
||||
"x_conn_id=${connId}",
|
||||
"x_dial_type=CAMPAIGN",
|
||||
"x_call_type=OUTBOUND",
|
||||
|
||||
"sip_h_X-Dial-Type=CAMPAIGN",
|
||||
"sip_h_X-Call-Type=OUTBOUND",
|
||||
"sip_h_X-Call-Direction=OUTGOING"
|
||||
]>
|
||||
-->
|
||||
|
||||
<action application="bridge" data="[${bridgeVars?join(',')}]user/${calledAgent.agent}@${tenant.realm}" />
|
||||
<action application="hangup" />
|
||||
</condition>
|
||||
</extension>
|
||||
</context>
|
||||
</section>
|
||||
</document>
|
||||
|
@ -5,54 +5,69 @@
|
||||
<extension name="InboundToAgent">
|
||||
<condition>
|
||||
<!-- <#noparse> -->
|
||||
<action application="set" data="odbc-cdr-ignore-leg=true" />
|
||||
<action application="set" data="absolute_codec_string=PCMA" />
|
||||
<action application="set" data="ringback=${us-ring}" />
|
||||
<action application="set" data="transfer_ringback=$${hold_music}" />
|
||||
<action application="set" data="hangup_after_bridge=true" />
|
||||
<action application="set" data="continue_on_fail=true" />
|
||||
|
||||
<action application="set" data="media_bug_answer_req=true" />
|
||||
<action application="set" data="RECORD_STEREO=true" />
|
||||
<!-- </#noparse> -->
|
||||
|
||||
<!--"origination_caller_id_number=${callerIdNumber}",-->
|
||||
<!--"origination_caller_id_name=${callerIdName}",-->
|
||||
<!-- Recording -->
|
||||
<#include "/dialplan/rec.xml">
|
||||
<!-- /Recording -->
|
||||
|
||||
<action application="set" data="x_conn_id=${connId}" />
|
||||
<action application="set" data="x_role=CALLER" />
|
||||
<action application="set" data="x_tenant_id=${tenant.id}" />
|
||||
<action application="set" data="x_tenant_code=${tenant.code}" />
|
||||
<action application="set" data="x_account=${calledAgent.account}" />
|
||||
<action application="set" data="x_called_number=${calledAgent.agent}" />
|
||||
|
||||
<action application="set" data="x_trunk_id=${trunk.id}" />
|
||||
<action application="set" data="x_cpn=${trunk.cpn}" />
|
||||
|
||||
<action application="set" data="x_dial_type=INBOUND" />
|
||||
<action application="set" data="x_call_type=INBOUND" />
|
||||
<action application="set" data="x_agent_type=${calledAgent.type}" />
|
||||
|
||||
<!--
|
||||
<#assign bridgeVars = [
|
||||
"originate_timeout=60",
|
||||
"odbc-cdr-ignore-leg=false",
|
||||
"odbc-cdr-ignore-leg=true",
|
||||
|
||||
"x_conn_id=${connId}",
|
||||
"x_role=CALLED",
|
||||
|
||||
"x_tenant_id=${tenant.id}",
|
||||
"x_tenant_code=${tenant.code}",
|
||||
|
||||
"x_account=${calledAgent.account}",
|
||||
"x_conn_id=${connId}",
|
||||
"x_dial_type=INBOUND",
|
||||
"x_call_type=INBOUND",
|
||||
"x_agent_type=${calledAgent.type}",
|
||||
"x_called_number=${calledAgent.agent}",
|
||||
|
||||
"x_trunk_id=${trunk.id}",
|
||||
"x_cpn=${trunk.cpn}",
|
||||
"x_called_number=${calledAgent.agent}",
|
||||
|
||||
"origination_uuid=${connId}",
|
||||
"sip_invite_call_id=${r'${sip_call_id}'}",
|
||||
|
||||
"callee_id_number=${calleeIdNumber}",
|
||||
"callee_id_name=${calleeIdName}",
|
||||
"effective_caller_id_number=${callerIdNumber}",
|
||||
"effective_caller_id_name=${callerIdName}",
|
||||
"sip_contact_user=${callerIdNumber}",
|
||||
"callee_id_number=${privacyNumber.number}",
|
||||
"callee_id_name=${privacyNumber.name}",
|
||||
|
||||
"effective_caller_id_number=${privacyNumber.number}",
|
||||
"effective_caller_id_name=${privacyNumber.name}",
|
||||
|
||||
"origination_caller_id_number=${privacyNumber.number}",
|
||||
"origination_caller_id_name=${privacyNumber.name}",
|
||||
"sip_contact_user=${privacyNumber.number}",
|
||||
|
||||
"x_dial_type=INBOUND",
|
||||
"x_call_type=INBOUND",
|
||||
"sip_h_X-Dial-Type=INBOUND",
|
||||
"sip_h_X-Call-Type=INBOUND"
|
||||
]>
|
||||
-->
|
||||
|
||||
<!-- Recording -->
|
||||
<action application="set" data="record_post_process_exec_api=curl:${postRecUrl} post tenantId=${tenant.id}&loc=${recordingLoc}" />
|
||||
<action application="record_session" data="${recordingLoc}" />
|
||||
<!-- /Recording -->
|
||||
|
||||
<action application="bridge" data="[${bridgeVars?join(',')}]user/${calledAgent.agent}@${tenant.realm}" />
|
||||
</condition>
|
||||
</extension>
|
||||
|
@ -13,9 +13,6 @@
|
||||
<action application="set" data="call_timeout=30" />
|
||||
<action application="set" data="hangup_after_bridge=true" />
|
||||
<action application="set" data="continue_on_fail=true" />
|
||||
|
||||
<action application="set" data="media_bug_answer_req=true" />
|
||||
<action application="set" data="RECORD_STEREO=true" />
|
||||
<!-- </#noparse> -->
|
||||
|
||||
<action application="set" data="x_role=CALLER" />
|
||||
@ -29,8 +26,7 @@
|
||||
<action application="set" data="x_agent_type=${callerAgent.type}" />
|
||||
|
||||
<!-- Recording -->
|
||||
<action application="set" data="record_post_process_exec_api=curl:${postRecUrl} post tenantId=${tenant.id}&loc=${recordingLoc}" />
|
||||
<action application="record_session" data="${recordingLoc}" />
|
||||
<#include "/dialplan/rec.xml">
|
||||
<!-- /Recording -->
|
||||
|
||||
<!--
|
||||
|
4
fsagent/src/main/resources/templates/dialplan/rec.xml
Normal file
4
fsagent/src/main/resources/templates/dialplan/rec.xml
Normal file
@ -0,0 +1,4 @@
|
||||
<action application="set" data="media_bug_answer_req=true" />
|
||||
<action application="set" data="RECORD_STEREO=true" />
|
||||
<action application="set" data="record_post_process_exec_api=curl:${postRecUrl} post tenantId=${tenant.id}&loc=${recLoc}" />
|
||||
<action application="record_session" data="${recLoc}" />
|
@ -4,7 +4,6 @@
|
||||
<context name="${tenant.realm}">
|
||||
<extension name="TrunkOutbound">
|
||||
<condition>
|
||||
|
||||
<!-- <#noparse> -->
|
||||
<action application="set" data="odbc-cdr-ignore-leg=true" />
|
||||
<action application="set" data="call_timeout=30" />
|
||||
@ -20,10 +19,7 @@
|
||||
<action application="set" data="x_call_type=OUTBOUND" />
|
||||
|
||||
<!-- Recording -->
|
||||
<action application="set" data="media_bug_answer_req=true" />
|
||||
<action application="set" data="RECORD_STEREO=true" />
|
||||
<action application="set" data="record_post_process_exec_api=curl:${postRecUrl} post tenantId=${tenant.id}&loc=${recordingLoc}" />
|
||||
<action application="record_session" data="${recordingLoc}" />
|
||||
<#include "/dialplan/rec.xml">
|
||||
<!-- /Recording -->
|
||||
|
||||
<!--
|
||||
|
1
lib/basic-model/README.md
Normal file
1
lib/basic-model/README.md
Normal file
@ -0,0 +1 @@
|
||||
# Yo Basic Model
|
42
lib/basic-model/pom.xml
Normal file
42
lib/basic-model/pom.xml
Normal file
@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
|
||||
http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>yo-basic-model</artifactId>
|
||||
<name>Yo Basic Model</name>
|
||||
<description>Yo Basic Model</description>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>com.pudonghot.yo</groupId>
|
||||
<artifactId>yo-library</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- Test Dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-log4j2</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
@ -0,0 +1,12 @@
|
||||
package com.pudonghot.yo.basic.model;
|
||||
|
||||
/**
|
||||
* @author Donghuang
|
||||
* @date Jul 12, 2020 14:12:04
|
||||
*/
|
||||
public enum PrivacyLevel {
|
||||
NONE,
|
||||
MEDIUM,
|
||||
HEAVY,
|
||||
ABSOLUTE
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.pudonghot.yo.basic.model;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* @author Donghuang
|
||||
* @date Jul 12, 2020 14:12:04
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class PrivacyNumber {
|
||||
public static final String PRIVACY = "PRIVACY";
|
||||
|
||||
private String number;
|
||||
private String name;
|
||||
}
|
0
lib/basic-model/src/main/resources/.gitkeep
Normal file
0
lib/basic-model/src/main/resources/.gitkeep
Normal file
0
lib/basic-model/src/main/resources/spring/.gitkeep
Normal file
0
lib/basic-model/src/main/resources/spring/.gitkeep
Normal file
@ -0,0 +1,17 @@
|
||||
package com.pudonghot.yo.basic.model;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Donghuang <br>
|
||||
* Oct 26, 2019 15:41:02
|
||||
*/
|
||||
@Slf4j
|
||||
public class TestDriver {
|
||||
|
||||
@Test
|
||||
public void run() {
|
||||
log.info("Run.");
|
||||
}
|
||||
}
|
38
lib/basic-model/src/test/resources/log4j2.xml
Normal file
38
lib/basic-model/src/test/resources/log4j2.xml
Normal file
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="WARN">
|
||||
<Properties>
|
||||
<Property name="log.level">DEBUG</Property>
|
||||
<Property name="log.dir">.logs</Property>
|
||||
<Property name="pattern">%-d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%t][%c{1}] %m%n</Property>
|
||||
</Properties>
|
||||
<Appenders>
|
||||
<Console name="Console" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%highlight{%-d{yyyy-MM-dd HH:mm:ss,SSS}}{FATAL=magenta, ERROR=magenta, WARN=magenta, INFO=magenta, DEBUG=magenta, TRACE=magenta} %highlight{%-5p}{FATAL=red blink, ERROR=red, WARN=yellow bold, INFO=black, DEBUG=green bold, TRACE=blue} [%t][%highlight{%c{1.}}{FATAL=cyan, ERROR=cyan, WARN=cyan, INFO=cyan, DEBUG=cyan, TRACE=cyan}] %m%n"/>
|
||||
</Console>
|
||||
<RollingFile name="File"
|
||||
fileName="${log.dir}/${project.artifactId}.log"
|
||||
filePattern="${log.dir}/$${date:yyyy-MM}/${project.artifactId}-%d{yyyy-MM-dd}-%i.log">
|
||||
<PatternLayout pattern="${pattern}" />
|
||||
<Policies>
|
||||
<TimeBasedTriggeringPolicy />
|
||||
<SizeBasedTriggeringPolicy size="16 MB" />
|
||||
</Policies>
|
||||
<DefaultRolloverStrategy max="32" />
|
||||
</RollingFile>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Logger name="org.springframework" level="INFO" additivity="false">
|
||||
<AppenderRef ref="File" />
|
||||
</Logger>
|
||||
<Logger name="org.apache" level="WARN" additivity="false">
|
||||
<AppenderRef ref="File" />
|
||||
</Logger>
|
||||
<Logger name="org.hibernate.validator" level="WARN" additivity="false">
|
||||
<AppenderRef ref="File" />
|
||||
</Logger>
|
||||
<Root level="${log.level}" additivity="false">
|
||||
<AppenderRef ref="File" level="${log.level}" />
|
||||
<AppenderRef ref="Console" level="${log.level}" />
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
@ -57,6 +57,11 @@
|
||||
<artifactId>yo-util</artifactId>
|
||||
<version>${yo.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.pudonghot.yo</groupId>
|
||||
<artifactId>yo-basic-model</artifactId>
|
||||
<version>${yo.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.pudonghot.yo</groupId>
|
||||
<artifactId>yo-fs-model</artifactId>
|
||||
|
@ -21,6 +21,10 @@
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.pudonghot.yo</groupId>
|
||||
<artifactId>yo-basic-model</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
|
@ -3,6 +3,7 @@ package com.pudonghot.yo.cellphone.privacy;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import com.pudonghot.yo.basic.model.PrivacyLevel;
|
||||
|
||||
/**
|
||||
* mask phone number utils
|
||||
|
@ -1,12 +0,0 @@
|
||||
package com.pudonghot.yo.cellphone.privacy;
|
||||
|
||||
/**
|
||||
* @author Donghuang <br>
|
||||
* Dec 13, 2019 10:13:05
|
||||
*/
|
||||
public enum PrivacyLevel {
|
||||
NONE,
|
||||
MEDIUM,
|
||||
HEAVY,
|
||||
ABSOLUTE
|
||||
}
|
@ -2,7 +2,7 @@ package com.pudonghot.yo.cellphone;
|
||||
|
||||
import org.junit.Test;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import com.pudonghot.yo.cellphone.privacy.PrivacyLevel;
|
||||
import com.pudonghot.yo.basic.model.PrivacyLevel;
|
||||
import com.pudonghot.yo.cellphone.privacy.NumberPrivacyUtils;
|
||||
|
||||
/**
|
||||
|
@ -1 +1 @@
|
||||
# Yo BR FreeSWITCH Model
|
||||
# Yo FreeSWITCH Model
|
||||
|
@ -5,7 +5,7 @@
|
||||
http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>yo-fs-model</artifactId>
|
||||
<name>Yo BR FreeSWITCH Model</name>
|
||||
<name>Yo FreeSWITCH Model</name>
|
||||
<description>Yo FreeSWITCH Model</description>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
|
@ -19,7 +19,6 @@ public class BaseReqDial implements Serializable {
|
||||
private Integer tenantId;
|
||||
@NotNull
|
||||
private DialType dialType = DialType.MANUAL;
|
||||
private String calledName;
|
||||
|
||||
private String bizKey;
|
||||
private String attachedData;
|
||||
|
@ -3,7 +3,6 @@ package com.pudonghot.yo.fsagent.api.request;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
|
@ -21,6 +21,10 @@
|
||||
<groupId>com.wacai.tigon</groupId>
|
||||
<artifactId>tigon-model</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.pudonghot.yo</groupId>
|
||||
<artifactId>yo-basic-model</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
|
@ -7,6 +7,7 @@ import com.wacai.tigon.mybatis.Transient;
|
||||
import com.wacai.tigon.mybatis.NotUpdate;
|
||||
import lombok.experimental.FieldNameConstants;
|
||||
import com.wacai.tigon.mybatis.UseGeneratedKeys;
|
||||
import com.pudonghot.yo.basic.model.PrivacyLevel;
|
||||
|
||||
/**
|
||||
* @author Donghuang <br>
|
||||
@ -28,11 +29,4 @@ public class AgentGroup extends TaggableDomain {
|
||||
private String identifier;
|
||||
@Transient
|
||||
private String trunkStrategies;
|
||||
|
||||
public enum PrivacyLevel {
|
||||
NONE,
|
||||
MEDIUM,
|
||||
HEAVY,
|
||||
ABSOLUTE
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
<modules>
|
||||
<module>bom</module>
|
||||
<module>basic-model</module>
|
||||
<module>model</module>
|
||||
<module>mapper</module>
|
||||
<module>service-common</module>
|
||||
|
@ -0,0 +1,17 @@
|
||||
package com.pudonghot.yo.service;
|
||||
|
||||
import com.pudonghot.yo.model.domain.Agent;
|
||||
|
||||
/**
|
||||
* @author Donghuang
|
||||
* @date Jul 11, 2020 16:40:51
|
||||
*/
|
||||
public interface CommonAcwScheduleService {
|
||||
|
||||
/**
|
||||
* mark agent acw
|
||||
*
|
||||
* @param agent agent
|
||||
*/
|
||||
void acw(Agent agent);
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
package com.pudonghot.yo.service.impl;
|
||||
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.redisson.api.RMapCache;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.springframework.util.Assert;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import com.pudonghot.yo.mapper.AgentMapper;
|
||||
import com.pudonghot.yo.model.domain.Agent;
|
||||
import org.redisson.api.map.event.EntryEvent;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import org.redisson.api.map.event.EntryExpiredListener;
|
||||
import com.pudonghot.yo.service.CommonAgentStatusService;
|
||||
import com.pudonghot.yo.service.CommonAcwScheduleService;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* @author Donghuang
|
||||
* @date Jul 11, 2020 16:40:42
|
||||
*/
|
||||
@Slf4j
|
||||
public class CommonAcwScheduleServiceImpl implements CommonAcwScheduleService, InitializingBean {
|
||||
private final RMapCache<Integer, Boolean> ACW_AGENTS;
|
||||
private final AtomicBoolean started = new AtomicBoolean(false);
|
||||
|
||||
@Autowired
|
||||
private AgentMapper agentMapper;
|
||||
@Setter(onMethod_ = {@Autowired})
|
||||
private CommonAgentStatusService agentStatusService;
|
||||
@Value("${yo.service-common.acw-schedule.enable-expired-listener:false}")
|
||||
private boolean enableExpiredListener;
|
||||
|
||||
/**
|
||||
* init
|
||||
*
|
||||
* @param redisson redisson
|
||||
*/
|
||||
public CommonAcwScheduleServiceImpl(
|
||||
@Autowired
|
||||
final RedissonClient redisson) {
|
||||
|
||||
ACW_AGENTS = redisson.getMapCache("AGENT_ACW");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void acw(final Agent agent) {
|
||||
ACW_AGENTS.put(agent.getId(), true, agent.getWrapUpTime(), TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
if (enableExpiredListener) {
|
||||
log.info("Start agent ACW entry expired listener.");
|
||||
Assert.state(started.compareAndSet(false, true),
|
||||
"Agent ACW schedule listener added");
|
||||
ACW_AGENTS.addListener((EntryExpiredListener) this::onAcwEnd);
|
||||
}
|
||||
else {
|
||||
log.info("Ignore start agent ACW entry expired listener.");
|
||||
}
|
||||
}
|
||||
|
||||
private void onAcwEnd(final EntryEvent<Integer, Boolean> event) {
|
||||
final Integer agentId = event.getKey();
|
||||
log.debug("Agent [{}] acw end.", agentId);
|
||||
|
||||
final Agent agent = agentMapper.find(agentId);
|
||||
if (agent != null) {
|
||||
agentStatusService.endAcw(agent);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package com.pudonghot.yo.service.impl;
|
||||
|
||||
import lombok.Setter;
|
||||
import java.util.Date;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import com.wacai.tigon.mybatis.Search;
|
||||
@ -11,6 +12,7 @@ import com.pudonghot.yo.mapper.AgentStatusMapper;
|
||||
import com.pudonghot.yo.model.domain.AgentStatus;
|
||||
import com.pudonghot.yo.model.exception.ErrorCode;
|
||||
import com.pudonghot.yo.model.exception.AssertUtils;
|
||||
import com.pudonghot.yo.service.CommonAcwScheduleService;
|
||||
import com.pudonghot.yo.service.CommonAgentStatusService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import static com.pudonghot.yo.model.exception.ErrorCode.AGENT_UPDATE_STATUS_FAIL;
|
||||
@ -29,6 +31,8 @@ public class CommonAgentStatusServiceImpl
|
||||
private AgentStatusMapper agentStatusMapper;
|
||||
@Autowired
|
||||
private IdSequence idSeq;
|
||||
@Setter(onMethod_ = {@Autowired})
|
||||
private CommonAcwScheduleService acwScheduleService;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
@ -221,10 +225,11 @@ public class CommonAgentStatusServiceImpl
|
||||
|
||||
clear(agentStatus);
|
||||
agentStatus.setCallUuid(null);
|
||||
|
||||
if (agent.getWrapUpTime() > 0) {
|
||||
agentStatus.setState(AgentStatus.State.ACW);
|
||||
agentStatus.setAcwTime(new Date());
|
||||
// TODO clear ACW
|
||||
acwScheduleService.acw(agent);
|
||||
}
|
||||
else {
|
||||
agentStatus.setState(AgentStatus.State.IDLE);
|
||||
|
@ -4,6 +4,7 @@
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<bean class="com.pudonghot.yo.service.impl.CommonAcwScheduleServiceImpl" />
|
||||
<bean class="com.pudonghot.yo.service.impl.CommonAgentStatusServiceImpl" />
|
||||
<bean class="com.pudonghot.yo.service.impl.CommonAgentEventQueueServiceImpl" />
|
||||
</beans>
|
||||
|
@ -1,10 +1,10 @@
|
||||
package com.pudonghot.yo.state.service.impl;
|
||||
|
||||
import com.pudonghot.yo.state.service.TenantService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.pudonghot.yo.mapper.TenantMapper;
|
||||
import com.pudonghot.yo.model.domain.Tenant;
|
||||
import com.pudonghot.yo.state.service.TenantService;
|
||||
import com.wacai.tigon.service.support.BaseCrudServiceSupport;
|
||||
|
||||
/**
|
||||
|
@ -1,4 +1,4 @@
|
||||
server.port=8080
|
||||
server.port=8086
|
||||
spring.application.name=yo-state
|
||||
spring.jackson.time-zone=GMT+8
|
||||
spring.jackson.serialization.write-dates-as-timestamps=true
|
||||
@ -14,3 +14,5 @@ spring.redis.host=172.16.92.232
|
||||
spring.redis.port=6379
|
||||
spring.redis.password=123456
|
||||
|
||||
yo.service-common.acw-schedule.enable-expired-listener=true
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user