commit campaign dial

This commit is contained in:
东皇大叔 2020-07-23 20:10:53 +08:00
parent 896f4c7d11
commit 5ac2544e12
65 changed files with 1324 additions and 300 deletions

View File

@ -42,6 +42,19 @@
<groupId>com.pudonghot.yo</groupId>
<artifactId>yo-web-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-jackson</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>

View File

@ -1,12 +1,14 @@
package com.pudonghot.yo.campaign;
import org.springframework.boot.SpringApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author Donghuang
* @date Jul 18, 2020 17:25:36
*/
@EnableFeignClients
@SpringBootApplication
public class YoCampaign {

View File

@ -0,0 +1,54 @@
package com.pudonghot.yo.campaign.feign.config;
import feign.Retryer;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.RequestInterceptor;
import lombok.extern.slf4j.Slf4j;
import feign.jackson.JacksonDecoder;
import feign.form.spring.SpringFormEncoder;
import org.springframework.context.annotation.Bean;
import com.fasterxml.jackson.databind.ObjectMapper;
import static java.util.concurrent.TimeUnit.SECONDS;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.support.ResponseEntityDecoder;
/**
* @author Donghuang
* @date Jul 22, 2020 14:08:16
*/
@Slf4j
@Configuration
public class FeignClientConfiguration {
@Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
@Bean
public RequestInterceptor requestInterceptor() {
return reqTpl -> {
log.debug("Add OpenAPI access token [{}].");
reqTpl.header("x-ac-token", "YoQinwei");
};
}
@Bean
public Encoder feignEncoder() {
return new SpringFormEncoder(new SpringEncoder(messageConverters));
}
@Bean
public Decoder feignDecoder(final ObjectMapper objectMapper) {
return new ResponseEntityDecoder(new JacksonDecoder(objectMapper));
}
@Bean
public Retryer feignRetryer() {
return new Retryer.Default(100, SECONDS.toMillis(1), 4);
}
}

View File

@ -0,0 +1,60 @@
package com.pudonghot.yo.campaign.feign.response;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.commons.lang3.tuple.Pair;
/**
* @author Donghuang
* @date Jul 22, 2020 14:17:40
*/
@Getter
@Setter
@ToString
public class RespCallingList {
@JsonAlias("rtncode")
@JsonProperty("rtncode")
private String code;
@JsonAlias("message")
@JsonProperty("message")
private String error;
@JsonAlias("taskid")
@JsonProperty("taskid")
private String campaignKey;
@JsonAlias("taskname")
@JsonProperty("taskname")
private String campaignName;
@JsonAlias("taskdata")
@JsonProperty("taskdata")
private CallingData[] data;
/**
* is response success
* @return true if code == 0
*/
public boolean isSuccess() {
return "0".equals(code);
}
@Getter
@Setter
@ToString
public static class CallingData {
private String phone;
@JsonAlias("outid")
@JsonProperty("outid")
private String id;
/**
* to pair (phone, id)
* @return pair
*/
public Pair<String, String> toPair() {
return Pair.of(phone, id);
}
}
}

View File

@ -0,0 +1,34 @@
package com.pudonghot.yo.campaign.feign.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestMapping;
import com.pudonghot.yo.campaign.feign.response.RespCallingList;
import com.pudonghot.yo.campaign.feign.config.FeignClientConfiguration;
/**
* @author Donghuang <br>
* donghuang@wacai.com <br>
* Jan 07, 2020 14:36:47
*/
@FeignClient(url = "${yo.campaign.feign.calling-list.base-url}",
name = "CampaignFeign",
configuration = FeignClientConfiguration.class)
public interface FeignCallingListService {
/**
* fetch calling list
* @param numData num data
* @param campaignKey campaign key
* @param campaignName campaign name
* @return calling list
*/
@RequestMapping("/${yo.campaign.feign.calling-list.channel}")
RespCallingList fetchCallingList(
@RequestParam("datanum")
int numData,
@RequestParam("taskid")
String campaignKey,
@RequestParam("taskname")
String campaignName);
}

View File

@ -1,8 +1,11 @@
package com.pudonghot.yo.campaign.service;
import com.pudonghot.yo.model.domain.Campaign;
import com.wacai.tigon.service.BaseQueryService;
/**
* @author Donghuang
* @date Jul 18, 2020 17:24:50
*/
public interface CampaignService {
public interface CampaignService extends BaseQueryService<Integer, Campaign> {
}

View File

@ -1,12 +1,89 @@
package com.pudonghot.yo.campaign.service.impl;
import java.util.stream.Stream;
import lombok.extern.slf4j.Slf4j;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import com.wacai.tigon.mybatis.Search;
import com.pudonghot.yo.mapper.CampaignMapper;
import com.pudonghot.yo.model.domain.Campaign;
import org.springframework.stereotype.Service;
import com.pudonghot.yo.mapper.AgentStatusMapper;
import com.pudonghot.yo.fsagent.api.CampaignDialService;
import com.pudonghot.yo.campaign.service.CampaignService;
import org.springframework.scheduling.annotation.Scheduled;
import com.pudonghot.yo.fsagent.api.request.ReqCampaignDial;
import org.springframework.beans.factory.annotation.Autowired;
import com.wacai.tigon.service.support.BaseQueryServiceSupport;
import com.pudonghot.yo.campaign.feign.response.RespCallingList;
import com.pudonghot.yo.campaign.feign.service.FeignCallingListService;
import static com.pudonghot.yo.model.domain.Campaign.TargetType.QUEUE;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
/**
* @author Donghuang
* @date Jul 18, 2020 17:25:17
*/
@Slf4j
@Service
public class CampaignServiceImpl implements CampaignService {
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class CampaignServiceImpl
extends BaseQueryServiceSupport<Integer, Campaign, CampaignMapper>
implements CampaignService {
private final ThreadPoolTaskExecutor taskExecutor;
@Autowired
private AgentStatusMapper agentStatusMapper;
@Autowired
private FeignCallingListService callingListService;
@Autowired
private CampaignDialService dialService;
@Scheduled(fixedRateString = "${yo.campaign.task-scheduler.fixed-rate:180000}", initialDelayString = "${yo.campaign.task-scheduler.init-delay:32000}")
public void taskScheduler() {
scan(new Search(Campaign.ACTIVE, true)
.eq(Campaign.STATUS, Campaign.Status.RUNNING), campaign -> {
if (campaign.getTargetType() == QUEUE) {
log.info("Queue campaign [{}] is running, dial.", campaign);
taskExecutor.execute(() -> dial(campaign));
}
else {
log.info("Campaign [{}] target is not queue, ignore.", campaign);
}
});
}
void dial(final Campaign campaign) {
log.info("Campaign [{}] dial.", campaign);
final Integer queueId = campaign.getTargetId();
final int countOnlineAgentOfQueue =
agentStatusMapper.countOnlineOfQueue(queueId);
if (countOnlineAgentOfQueue == 0) {
log.warn("Campaign [{}] has no online agent, ignore.");
return;
}
final int countIdleAgentOfQueue =
agentStatusMapper.countIdleOfQueue(queueId);
if (countIdleAgentOfQueue == 0) {
log.warn("Campaign [{}] has no idle agent, ignore.");
return;
}
// TODO campaign dial
// Outbound = (readyAgent + expNum) * z * speedAdjust - taskNum;
final RespCallingList callingList =
callingListService.fetchCallingList(
countIdleAgentOfQueue, campaign.getCampaignKey(), campaign.getName());
if (callingList.isSuccess()) {
final ReqCampaignDial req= new ReqCampaignDial();
req.setCampaignId(campaign.getId());
req.setCallingList(Stream.of(callingList.getData()).map(RespCallingList.CallingData::toPair).collect(Collectors.toList()));
dialService.queueDial(req);
}
else {
log.error("Fetch calling list error caused. code [{}], message [{}].", callingList.getCode(), callingList.getError());
}
}
}

View File

@ -21,3 +21,14 @@ spring.redis.host=172.18.4.35
spring.redis.port=6379
spring.redis.password=123456
# Dubbo
## Dubbo Registry
dubbo.registry.address=zookeeper://172.18.4.35:2181
dubbo.registry.file=${user.home}/dubbo-cache/${spring.application.name}/dubbo.cache
yo.fsagent.dubbo.service.version=1.0.0
# Calling List
yo.campaign.feign.calling-list.base-url=http://localhost:1116
yo.campaign.feign.calling-list.channel=campaign.json

View File

@ -2,10 +2,13 @@
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task.xsd">
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task.xsd
http://dubbo.apache.org/schema/dubbo
http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<task:executor id="taskExecutor"
pool-size="${yo.task.executor.pool-size:8}"
@ -14,4 +17,11 @@
<task:scheduler id="taskScheduler" pool-size="${yo.task.scheduler.pool-size:8}" />
<task:annotation-driven executor="taskExecutor"
scheduler="taskScheduler" proxy-target-class="true" />
<dubbo:reference id="dialService"
interface="com.pudonghot.yo.fsagent.api.DialService"
version="${yo.fsagent.dubbo.service.version}" />
<dubbo:reference id="campaignDialService"
interface="com.pudonghot.yo.fsagent.api.CampaignDialService"
version="${yo.fsagent.dubbo.service.version}" />
</beans>

View File

@ -0,0 +1,30 @@
package com.pudonghot.yo.campaign;
import org.junit.Test;
import org.junit.runner.RunWith;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.beans.factory.annotation.Autowired;
import com.pudonghot.yo.campaign.feign.response.RespCallingList;
import com.pudonghot.yo.campaign.feign.service.FeignCallingListService;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Donghuang <br>
* Dec 12, 2019 23:59:37
*/
@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = YoCampaign.class)
public class FeignCallingListServiceTest {
@Autowired
private FeignCallingListService callingListService;
@Test
public void testFetchCallingList() {
final RespCallingList callingList =
callingListService.fetchCallingList(1, "11223", "44556");
log.info("Calling list [{}].", callingList);
}
}

View File

@ -1,14 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://dubbo.apache.org/schema/dubbo
http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<dubbo:reference id="demoService"
interface="com.pudonghot.yo.fsagent.api.DemoService"
version="${yo.fsagent.dubbo.service.version}"
/>
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>

View File

@ -9,12 +9,12 @@ import com.wacai.tigon.mybatis.Search;
import com.pudonghot.yo.model.domain.*;
import com.wacai.tigon.model.ViewModel;
import org.springframework.util.Assert;
import com.pudonghot.yo.util.PhoneNumberUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.StringUtils;
import com.pudonghot.yo.cms.form.SessionForm;
import org.springframework.stereotype.Service;
import com.pudonghot.yo.cellphone.CellphoneInfo;
import com.pudonghot.yo.common.util.MobileUtils;
import com.pudonghot.yo.cms.service.TrunkService;
import com.pudonghot.yo.cellphone.CellphoneService;
import com.pudonghot.yo.cms.service.AreaCodeService;
@ -172,7 +172,7 @@ public class TrunkServiceImpl
}
private String getTrunk(final String callerNumber) {
if (MobileUtils.checkMobile(callerNumber)) {
if (PhoneNumberUtils.isMobile(callerNumber)) {
final CellphoneInfo cellphoneInfo =
cellphoneService.lookup(callerNumber);
Assert.state(cellphoneInfo != null,

View File

@ -19,11 +19,17 @@ deploy_server() {
local server="$1"
local app="$2"
local jar="$3"
local port="$4"
if [ -z "$port" ]; then
port='22'
fi
local target_dir="/data/program/$app"
local target_jar="$target_dir/lib/main.jar"
scp $jar $server:"${target_jar}_new"
ssh $server "$target_dir/bin/stop.sh; mv $target_jar {$target_jar}_prev"
ssh $server "mv ${target_jar}_new $target_jar && $target_dir/bin/start.sh"
scp -P $port $jar $server:"${target_jar}_new"
ssh -p $port $server "$target_dir/bin/stop.sh;
mv $target_jar {$target_jar}_prev;
mv ${target_jar}_new $target_jar && $target_dir/bin/start.sh"
}
prg_path=$(get_real_path "$0")
@ -35,7 +41,7 @@ WORK_DIR=$(pwd)
echo "Work dir [$WORK_DIR]"
if [ -z "$1" ] && [ -z "$2" ]; then
echo 'Usage: ./deploy.sh cms|fsagent|openapi test|prod'
echo 'Usage: ./deploy.sh cms|fsagent|openapi test|prod [nb]'
exit 1
fi
@ -58,8 +64,9 @@ echo "JAR [$JAR] found"
if [ "$2" == "test" ]; then
echo "Deploy test."
deploy_server 'appweb@118.24.251.131' $APP $JAR
elif ["$2" == "prod" ]; then
elif [ "$2" == "prod" ]; then
echo "Deploy prod."
deploy_server 'xiandou@113.87.175.72' $APP $JAR '51022'
fi
popd > /dev/null

View File

@ -1,12 +1,18 @@
package com.pudonghot.yo.fsagent.controller;
import java.util.List;
import java.util.Arrays;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.apache.commons.lang3.tuple.Pair;
import com.pudonghot.yo.fsagent.api.DialService;
import org.springframework.stereotype.Controller;
import com.pudonghot.yo.fsagent.api.response.RespDial;
import com.pudonghot.yo.fsagent.api.CampaignDialService;
import com.pudonghot.yo.fsagent.api.request.ReqAgentDial;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import com.pudonghot.yo.fsagent.api.request.ReqAgentDial;
import org.springframework.web.bind.annotation.RequestParam;
import com.pudonghot.yo.fsagent.api.request.ReqCampaignDial;
import org.springframework.beans.factory.annotation.Autowired;
/**
@ -18,9 +24,24 @@ import org.springframework.beans.factory.annotation.Autowired;
public class DialController {
@Autowired
private DialService dialService;
@Autowired
private CampaignDialService campaignDialService;
@PostMapping("/dial")
public RespDial dial(@RequestBody final ReqAgentDial req) {
return dialService.agentDial(req);
}
@PostMapping("/campaign-dial")
public List<String> campaignDial(
@RequestParam("campaignId")
final Integer campaignId,
@RequestParam("phone")
final String calledNumber) {
final ReqCampaignDial req = new ReqCampaignDial();
req.setCampaignId(campaignId);
req.setCallingList(Arrays.asList(Pair.of(calledNumber, calledNumber)));
return campaignDialService.queueDial(req);
}
}

View File

@ -38,6 +38,7 @@ public class DialplanController extends BaseDialplanController {
final Map<String, String> params) {
log.info("XML dialplan of domain [{}] [{}] -> [{}].", domain, channel, calledNumber);
log.debug("XML dialplan params [{}].", params);
for (final DialplanService dialplanService : dialplanServices) {
final DialplanConfig dialplanConfig = dialplanService.find(params);

View File

@ -10,6 +10,7 @@ import org.springframework.stereotype.Controller;
import com.pudonghot.yo.model.agentevent.AgentEvent;
import com.pudonghot.yo.service.CommonChannelService;
import com.pudonghot.yo.fsagent.service.AgentService;
import com.pudonghot.yo.service.CommonCallDataService;
import org.freeswitch.esl.client.transport.event.Event;
import com.pudonghot.yo.fsagent.api.IvrTransferService;
import org.springframework.web.bind.annotation.PostMapping;
@ -36,6 +37,8 @@ public class IvrController {
private CommonAgentEventQueueService agentEventQueueService;
@Autowired
private CommonChannelService channelService;
@Autowired
private CommonCallDataService commonCallDataService;
@PostMapping("/ivr/confirm")
public void confirm(
@ -103,7 +106,8 @@ public class IvrController {
}
eventData.put("callid", callId);
eventData.put("calldata", ivrTransferService.getCallData(strAgentId));
eventData.put("calldata",
commonCallDataService.getIvrCallData(strAgentId));
agentEventQueueService.publish(new AgentEvent(
AgentEvent_IVR_Result,

View File

@ -0,0 +1,29 @@
package com.pudonghot.yo.fsagent.event;
import lombok.Getter;
import com.pudonghot.yo.model.domain.Agent;
import org.springframework.context.ApplicationEvent;
/**
* @author Donghuang
* @date Jul 22, 2020 19:53:59
*/
public class CampaignCallEstablishedEvent extends ApplicationEvent {
@Getter
private final String callUuid;
/**
* Create a new {@code ApplicationEvent}.
*
* @param agent the object on which the event initially occurred or with
* which the event is associated (never {@code null})
*/
public CampaignCallEstablishedEvent(final Agent agent, String callUuid) {
super(agent);
this.callUuid = callUuid;
}
public Agent getAgent() {
return (Agent) getSource();
}
}

View File

@ -0,0 +1,42 @@
package com.pudonghot.yo.fsagent.listener;
import lombok.extern.slf4j.Slf4j;
import com.pudonghot.yo.model.domain.Agent;
import org.springframework.stereotype.Component;
import com.pudonghot.yo.model.agentevent.AgentEvent;
import com.pudonghot.yo.service.CommonCallDataService;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import com.pudonghot.yo.service.CommonAgentEventQueueService;
import static com.pudonghot.yo.model.agentevent.EventType.*;
import org.springframework.beans.factory.annotation.Autowired;
import com.pudonghot.yo.fsagent.event.CampaignCallEstablishedEvent;
/**
* @author Donghuang
* @date Jul 23, 2020 09:50:06
*/
@Slf4j
@Component
public class CampaignCallEstablishedListener {
@Autowired
private CommonAgentEventQueueService agentEventQueueService;
@Autowired
private CommonCallDataService commonCallDataService;
/**
* {@inheritDoc}
*/
@Async
@EventListener(value = CampaignCallEstablishedEvent.class)
public void onCampaignCallEstablished(final CampaignCallEstablishedEvent event) {
final Agent agent = event.getAgent();
final String callUuid = event.getCallUuid();
agentEventQueueService.publish(
new AgentEvent(AgentOther_TaskData,
agent.getId(),
agent.getAccount(),
commonCallDataService.getCampaignCallData(callUuid)));
}
}

View File

@ -0,0 +1,39 @@
package com.pudonghot.yo.fsagent.listener;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import com.pudonghot.yo.service.CommonCallDataService;
import org.freeswitch.esl.client.transport.event.Event;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.beans.factory.annotation.Autowired;
/**
* @author Donghuang
* @date Jul 23, 2020 19:51:09
*/
@Slf4j
@Component
public class CampaignChannelDestroy {
@Autowired
private CommonCallDataService callDataService;
/**
* {@inheritDoc}
*/
@Async
@EventListener(value = Event.class,
condition = "#root.args[0].getName() == 'CHANNEL_DESTROY'" +
" and #root.args[0].getDialType() == 'CAMPAIGN'")
public void onCampaignChannelDestroy(final Event event) {
log.debug("On campaign channel destroy event [{}].", event.getHeaders());
final String strCampaignId = event.getHeader("variable_x_campaign_id");
if (StringUtils.isNotBlank(strCampaignId)) {
log.info("Campaign [{}] call hangup, decrease campaign channel.", strCampaignId);
final Integer campaignId = Integer.parseInt(strCampaignId);
callDataService.decrCampaignChannel(campaignId);
}
}
}

View File

@ -4,11 +4,12 @@ import java.util.Map;
import java.util.HashMap;
import lombok.extern.slf4j.Slf4j;
import com.pudonghot.yo.model.domain.Agent;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import com.pudonghot.yo.fsagent.util.CallStrUtils;
import com.pudonghot.yo.fsagent.util.EslEventUtils;
import com.pudonghot.yo.model.agentevent.AgentEvent;
import com.pudonghot.yo.fsagent.service.AgentService;
import com.pudonghot.yo.fsagent.util.CallStrUtils;
import org.freeswitch.esl.client.transport.event.Event;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
@ -37,7 +38,7 @@ public class ChannelAnswer {
@EventListener(value = Event.class,
condition = "#root.args[0].getName() == 'CHANNEL_ANSWER'")
public void onChannelAnswer(final Event event) {
log.debug("On channel answer event [{}] [{}].", event, event.getHeaders());
log.debug("On channel answer event [{}].", event.getHeaders());
final CallStrUtils.ChannelInfo channelInfo =
CallStrUtils.getChannelInfo(event);
@ -58,7 +59,12 @@ public class ChannelAnswer {
// feature0普通呼入7普通外呼 51内部求助
eventData.put("feature", "7");
eventData.put("caller", channelInfo.getNumber());
eventData.put("called", event.getOtherNumber());
String calledNumber = event.getCalledNumber();
if (StringUtils.isBlank(calledNumber)) {
calledNumber = event.getOtherNumber();
}
eventData.put("called", calledNumber);
}
else {
// feature0普通呼入7普通外呼 51内部求助

View File

@ -27,7 +27,7 @@ public class ChannelCreate {
@EventListener(value = Event.class,
condition = "#root.args[0].getName() == 'CHANNEL_CREATE'")
public void onChannelCreate(final Event event) {
log.debug("On channel create event [{}] [{}].", event, event.getHeaders());
log.debug("On channel create event [{}].", event.getHeaders());
final CallStrUtils.ChannelInfo channelInfo = CallStrUtils.getChannelInfo(event);

View File

@ -44,7 +44,7 @@ public class ChannelDestroy {
@EventListener(value = Event.class,
condition = "#root.args[0].getName() == 'CHANNEL_DESTROY'")
public void onChannelDestroy(final Event event) {
log.debug("On channel destroy event [{}] [{}].", event, event.getHeaders());
log.debug("On channel destroy event [{}].", event.getHeaders());
final String callUuid = event.getCallUuid();
final String channelName = event.getChannelName();

View File

@ -52,7 +52,9 @@ public class ChannelHangupComplete {
final Map<String, Object> eventData = new HashMap<>(8);
eventData.put("callid", event.getCallUuid());
if (EslEventUtils.isCaller(event)) {
final String hangupCause = event.getHangupCause();
if (EslEventUtils.isCaller(event) &&
!"NORMAL_UNSPECIFIED".equalsIgnoreCase(hangupCause)) {
agentEventQueueService.publish(
new AgentEvent(
AgentEvent_Connect_Fail,
@ -60,7 +62,7 @@ public class ChannelHangupComplete {
agent.getAccount(),
eventData));
}
else if ("NO_ANSWER".equals(event.getHangupCause())) {
else if ("NO_ANSWER".equalsIgnoreCase(hangupCause)) {
agentEventQueueService.publish(
new AgentEvent(
AgentEvent_No_Answer,

View File

@ -36,7 +36,7 @@ public class ChannelProgress {
@EventListener(value = Event.class,
condition = "#root.args[0].getName() == 'CHANNEL_PROGRESS'")
public void onChannelProgress(final Event event) {
log.debug("On channel progress event [{}] [{}].", event, event.getHeaders());
log.debug("On channel progress event [{}].", event.getHeaders());
final String channelName = event.getChannelName();
log.info("On channel [{}] answer event.", channelName);

View File

@ -65,15 +65,15 @@ public abstract class BaseDialplanService implements DialplanService {
protected String getParam(final Map<String, String> params, final String name, final String... names) {
String val = params.get(name);
final String val = params.get(name);
if (StringUtils.isNotBlank(val)) {
return val;
}
for (String n : names) {
val = params.get(n);
if (StringUtils.isNotBlank(val)) {
return val;
for (final String alterName : names) {
final String alterVal = params.get(alterName);
if (StringUtils.isNotBlank(alterVal)) {
return alterVal;
}
}
@ -88,6 +88,10 @@ public abstract class BaseDialplanService implements DialplanService {
return getParam(params, "Channel-Presence-ID", "variable_presence_id");
}
public String getConnId(final Map<String, String> params) {
return getParam(params, "variable_x_conn_id", "variable_call_uuid", "variable_uuid");
}
protected ChannelInfo getChannelInfo(final Map<String, String> params) {
return CallStrUtils.parseChannelName(getChannelName(params), getPresenceId(params));
}

View File

@ -3,12 +3,16 @@ package com.pudonghot.yo.fsagent.service.dialplan.impl;
import java.util.Map;
import java.util.HashMap;
import com.pudonghot.yo.model.domain.Agent;
import com.pudonghot.yo.util.PhoneNumberUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.stereotype.Service;
import org.springframework.core.annotation.Order;
import org.springframework.context.ApplicationContext;
import com.pudonghot.yo.fsagent.service.dialplan.Call;
import com.pudonghot.yo.model.domain.CallDetailRecord;
import com.pudonghot.yo.fsagent.event.CampaignCallEstablishedEvent;
import org.springframework.beans.factory.annotation.Autowired;
import com.pudonghot.yo.fsagent.service.dialplan.DialplanConfig;
/**
@ -18,6 +22,8 @@ import com.pudonghot.yo.fsagent.service.dialplan.DialplanConfig;
@Order(20)
@Service
public class DialplanService20CampaignToIdAndType extends BaseDialplanService {
@Autowired
private ApplicationContext appContext;
/**
* {@inheritDoc}
@ -36,14 +42,20 @@ public class DialplanService20CampaignToIdAndType extends BaseDialplanService {
model.put("connId", connId);
model.put("calledAgent", calledAgent);
final String otherDn = StringUtils.defaultIfBlank(
getVarCalledNumber(call),
call.getCallerChannel().getNumber());
String otherDn = getVarCalledNumber(call);
if (StringUtils.isBlank(otherDn)) {
otherDn = PhoneNumberUtils.cleanupMobile(
call.getCallerChannel().getNumber());
}
model.put("calledNumber", otherDn);
model.put("privacyNumber",
numberPrivacyService.generate(calledAgent, otherDn));
recFile(model, connId, calledAgent.getAccount(), md5(otherDn));
appContext.publishEvent(
new CampaignCallEstablishedEvent(
calledAgent, getConnId(call.getParams())));
return new DialplanConfig(call.getTenant(), "campaign-to-agent.xml", model);
}

View File

@ -27,10 +27,10 @@ public class DialplanService28AgentToTrunkPrefix extends BaseDialplanService {
return onCallerAgent(call, callerAgent -> {
final String calledNumber = call.getCalledNumber();
final String dialedNumber = call.getCalledNumber();
final Pair<Trunk, String> trunkInfo =
trunkService.parseDialTarget(
callerAgent.getTenantId(), calledNumber);
callerAgent.getTenantId(), dialedNumber);
if (trunkInfo != null) {
final Trunk trunk = trunkInfo.getLeft();
@ -45,9 +45,10 @@ public class DialplanService28AgentToTrunkPrefix extends BaseDialplanService {
gatewayService.genGatewayName(trunk.getGatewayId()));
// number without trunk prefix
final String targetNumber = trunkInfo.getRight();
model.put("targetNumber", targetNumber);
recFile(model, connId, callerAgent.getAccount(), md5(targetNumber));
final String calledNumber = trunkInfo.getRight();
model.put("calledNumber", calledNumber);
model.put("targetNumber", trunkService.calledNumber(trunk, calledNumber));
recFile(model, connId, callerAgent.getAccount(), md5(calledNumber));
return new DialplanConfig(call.getTenant(), "trunk-outbound.xml", model);
}
return null;

View File

@ -0,0 +1,148 @@
package com.pudonghot.yo.fsagent.service.dubbo.impl;
import java.util.*;
import com.pudonghot.yo.mapper.*;
import com.wacai.tigon.json.JSON;
import lombok.extern.slf4j.Slf4j;
import com.pudonghot.yo.model.domain.*;
import org.springframework.util.Assert;
import com.pudonghot.yo.model.domain.Queue;
import com.wacai.tigon.sequence.IdSequence;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import com.pudonghot.yo.fsagent.api.request.*;
import org.freeswitch.esl.client.inbound.Client;
import org.apache.dubbo.config.annotation.Service;
import com.pudonghot.yo.fsagent.service.TrunkService;
import com.pudonghot.yo.service.CommonCallDataService;
import com.pudonghot.yo.fsagent.api.CampaignDialService;
import static org.slf4j.helpers.MessageFormatter.arrayFormat;
import org.springframework.beans.factory.annotation.Autowired;
/**
* @author Donghuang <br>
* Dec 09, 2019 16:30:25
*/
@Slf4j
@Service(version = "${yo.fsagent.dubbo.service.version}", register = false)
public class CampaignDialServiceImpl implements CampaignDialService {
@Autowired
private CampaignMapper campaignMapper;
@Autowired
private Client fsClient;
@Autowired
private IdSequence idSeq;
@Autowired
private TenantMapper tenantMapper;
@Autowired
private TrunkStrategyMapper trunkStrategyMapper;
@Autowired
private QueueMapper queueMapper;
@Autowired
private TrunkService trunkService;
@Autowired
private CommonCallDataService commonCallDataService;
@Autowired
private JSON json;
/**
* {@inheritDoc}
*/
@Override
public List<String> queueDial(final ReqCampaignDial req) {
final Integer campaignId = req.getCampaignId();
final Campaign campaign = campaignMapper.find(campaignId);
Assert.state(campaign != null,
() -> "No campaign [" + campaignId + "] found");
Assert.state(campaign.getActive(),
() -> "Campaign [" + campaignId + "] is not active");
Assert.state(campaign.getStatus() == Campaign.Status.RUNNING,
() -> "Campaign [" + campaignId + "] is not running");
Assert.state(campaign.getTargetType() == Campaign.TargetType.QUEUE,
() -> "Campaign [" + campaignId + "] target type is not QUEUE");
final Queue queue = queueMapper.find(campaign.getTargetId());
Assert.state(queue != null,
() -> "Campaign [" + campaignId + "] queue not found");
Assert.state(queue.getActive(),
() -> "Campaign [" + campaignId + "] queue is not active");
final Tenant tenant = tenantMapper.find(campaign.getTenantId());
Assert.state(tenant != null,
() -> "Campaign [" + campaignId + "] tenant not found");
Assert.state(tenant.getActive(),
() -> "Campaign [" + campaignId + "] tenant is not active");
final List<Pair<String, String>> callingList = req.getCallingList();
final List<String> callUuidList = new ArrayList<>(callingList.size());
for (final Pair<String, String> callingRec : callingList) {
final String calledNumber = callingRec.getLeft();
final List<TrunkStrategy> trunkStrategies =
trunkStrategyMapper.listOfCampaign(campaignId);
Assert.state(!trunkStrategies.isEmpty(),
() -> "Campaign [" + campaignId + "] has no trunk strategies configured");
final TrunkStrategy trunkStrategy =
trunkStrategies.size() == 1 ?
trunkStrategies.iterator().next() :
trunkStrategies.get(
RandomUtils.nextInt(0, trunkStrategies.size()));
final Pair<Trunk, String> trunkDialStr =
trunkService.dialStr(trunkStrategy.getId(), calledNumber, null);
final Trunk trunk = trunkDialStr.getLeft();
final List<String> globalVars = Arrays.asList(
"ignore_early_media=true",
"x_dial_type=CAMPAIGN",
"x_call_type=OUTBOUND",
"x_tenant_id=" + tenant.getId(),
"x_tenant_code=" + tenant.getCode(),
"x_account=SYSTEM",
"x_called_number=" + calledNumber,
"x_trunk_id=" + trunk.getId(),
"x_cpn=" + trunk.getCpn()
);
final String uuid = idSeq.get();
callUuidList.add(uuid);
final List<String> channelVars = Arrays.asList(
"x_campaign_id=" + campaignId,
"x_conn_id=" + uuid,
"origination_uuid=" + uuid,
"sip_invite_call_id=" + uuid,
"originate_timeout=30",
"x_role=CALLED",
"origination_caller_id_number=" + trunk.getCpn(),
"continue_on_fail=true",
"hangup_after_bridge=true"
);
// originate {x_dial_type=CAMPAIGN,x_tenant_id=13,x_tenant_code=GOBLIN,x_account=SYSTEM,x_called_number=13764268709,x_trunk_id=12,x_cpn=28165766}[originate_timeout=30,x_role=CALLED,origination_caller_id_number=28165766]sofia/gateway/GW000001/013764268709 4.Q XML d1.wacai.info
fsClient.bgApi(format("originate {{}}[{}]{} {}.Q XML {}",
StringUtils.join(globalVars, ","),
StringUtils.join(channelVars, ","),
trunkDialStr.getRight(),
queue.getId(),
tenant.getRealm()));
final Map<String, Object> callData = new HashMap<>(4);
callData.put("outid", callingRec.getRight());
callData.put("phone", calledNumber);
callData.put("taskid", campaign.getCampaignKey());
callData.put("taskname", campaign.getName());
commonCallDataService.saveCampaignCallData(uuid,
json.toJSONString(callData));
commonCallDataService.incrCampaignChannel(campaignId);
}
return callUuidList;
}
String format(final String tpl, final Object... args) {
return arrayFormat(tpl, args).getMessage();
}
}

View File

@ -43,8 +43,6 @@ public class DialServiceImpl implements DialService {
private QueueMapper queueMapper;
@Autowired
private AgentService agentService;
@Value("${yo.fsagent.recording.file-ext:.ogg}")
private String recordingFileExt;
@Autowired
private DialerAgent dialerAgent;

View File

@ -1,9 +1,6 @@
package com.pudonghot.yo.fsagent.service.dubbo.impl;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RMapCache;
import java.util.concurrent.TimeUnit;
import org.redisson.api.RedissonClient;
import com.pudonghot.yo.model.domain.Agent;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.StringUtils;
@ -11,8 +8,8 @@ import com.pudonghot.yo.fs.model.domain.Channel;
import org.freeswitch.esl.client.inbound.Client;
import org.apache.dubbo.config.annotation.Service;
import com.pudonghot.yo.service.CommonChannelService;
import com.pudonghot.yo.service.CommonCallDataService;
import com.pudonghot.yo.fsagent.api.IvrTransferService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.annotation.Autowired;
import com.pudonghot.yo.fsagent.service.dialplan.DialplanService;
@ -23,21 +20,13 @@ import com.pudonghot.yo.fsagent.service.dialplan.DialplanService;
@Slf4j
@Service(version = "${yo.fsagent.dubbo.service.version}", register = false)
public class IvrTransferServiceImpl implements IvrTransferService {
private final RMapCache<String, String> CALL_DATA_CACHE;
@Autowired
private Client fsClient;
@Autowired
private CommonChannelService commonChannelService;
@Value("${yo.fsagent.ivr-transfer.call-data-timeout-seconds:120}")
private long callDataTimeoutSeconds;
public IvrTransferServiceImpl(
@Autowired
final RedissonClient redisson) {
this.CALL_DATA_CACHE = redisson.getMapCache("CALL_DATA_CACHE");
}
@Autowired
private CommonCallDataService commonCallDataService;
/**
* {@inheritDoc}
@ -82,7 +71,7 @@ public class IvrTransferServiceImpl implements IvrTransferService {
"confirm.IVR/XML/" + realm);
if (StringUtils.isNotBlank(callData)) {
saveCallData(agentId, callData);
commonCallDataService.saveIvrCallData(agentId, callData);
}
}
@ -102,26 +91,6 @@ public class IvrTransferServiceImpl implements IvrTransferService {
return agentFifo(realm, agentId, DialplanService.Suffix.CHANNEL_UNHOLD);
}
/**
* {@inheritDoc}
*/
@Override
public void saveCallData(final String agentId, final String callData) {
CALL_DATA_CACHE.put(
cacheKey(agentId),
callData,
callDataTimeoutSeconds,
TimeUnit.SECONDS);
}
/**
* {@inheritDoc}
*/
@Override
public String getCallData(final String agentId) {
return CALL_DATA_CACHE.get(cacheKey(agentId));
}
/**
* {@inheritDoc}
*/
@ -131,8 +100,4 @@ public class IvrTransferServiceImpl implements IvrTransferService {
final DialplanService.Suffix suffix) {
return "AGENT_" + agentId + suffix.getSuffix() + "/XML/" + realm;
}
String cacheKey(final String agentId) {
return "IVR_CALL_DATA:" + agentId;
}
}

View File

@ -2,26 +2,27 @@ package com.pudonghot.yo.fsagent.service.impl;
import java.util.List;
import com.pudonghot.yo.fsagent.service.GatewayService;
import com.pudonghot.yo.fsagent.service.TrunkService;
import com.pudonghot.yo.mapper.GatewayMapper;
import com.pudonghot.yo.mapper.SequenceMapper;
import com.pudonghot.yo.mapper.TrunkMapper;
import com.pudonghot.yo.mapper.TrunkStrategyMapper;
import com.pudonghot.yo.model.domain.Gateway;
import com.pudonghot.yo.model.domain.Sequence;
import com.pudonghot.yo.model.domain.Trunk;
import com.pudonghot.yo.model.domain.TrunkStrategy;
import com.wacai.tigon.mybatis.Search;
import com.pudonghot.yo.util.PhoneNumberUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import com.wacai.tigon.mybatis.Search;
import org.springframework.util.Assert;
import com.pudonghot.yo.mapper.TrunkMapper;
import com.pudonghot.yo.model.domain.Trunk;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.RandomUtils;
import com.pudonghot.yo.mapper.GatewayMapper;
import com.pudonghot.yo.model.domain.Gateway;
import com.pudonghot.yo.mapper.SequenceMapper;
import com.pudonghot.yo.model.domain.Sequence;
import org.springframework.stereotype.Service;
import com.pudonghot.yo.cellphone.CellphoneInfo;
import com.pudonghot.yo.mapper.TrunkStrategyMapper;
import com.pudonghot.yo.model.domain.TrunkStrategy;
import com.pudonghot.yo.cellphone.CellphoneService;
import com.pudonghot.yo.fsagent.service.TrunkService;
import com.pudonghot.yo.fsagent.service.GatewayService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.annotation.Autowired;
import com.wacai.tigon.service.support.BaseQueryServiceSupport;
@ -69,7 +70,7 @@ public class TrunkServiceImpl
Assert.state(TrunkStrategy.Strategy.RANDOM == trunkStrategy.getStrategy(),
"Random trunk strategy supports only");
final List<Trunk> trunks = mapper.listOfStrategyId(strategyId);
final List<Trunk> trunks = mapper.listOfStrategy(strategyId);
Assert.state(!trunks.isEmpty(),
() -> "No trunk found of strategy [" + trunkStrategy.getName() + "]");
@ -127,8 +128,11 @@ public class TrunkServiceImpl
new Search(Trunk.TENANT_ID, tenantId)
.eq(Trunk.PREFIX, prefix)
.eq(Trunk.ACTIVE, true));
if (trunk != null) {
return Pair.of(trunk, calledNumber.substring(prefixLength));
return Pair.of(trunk,
PhoneNumberUtils.cleanupMobile(
calledNumber.substring(prefixLength)));
}
}

View File

@ -41,7 +41,7 @@ dubbo.application.qos-port=22222
dubbo.application.qos-accept-foreign-ip=false
## Dubbo Registry
dubbo.registry.address=zookeeper://172.16.67.223:2181
dubbo.registry.address=zookeeper://172.18.4.35:2181
dubbo.registry.file=${user.home}/dubbo-cache/${spring.application.name}/dubbo.cache
# Service Version

View File

@ -5,22 +5,11 @@
<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 -->
@ -33,16 +22,16 @@
"originate_timeout=30",
"odbc-cdr-ignore-leg=true",
"origination_uuid=${connId}",
"sip_invite_call_id=${r'${sip_call_id}'}",
"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_called_number=${calledNumber}",
"x_tenant_id=${tenant.id}",
"x_tenant_code=${tenant.code}",

View File

@ -18,6 +18,9 @@
<action application="set" data="x_dial_type=MANUAL" />
<action application="set" data="x_call_type=OUTBOUND" />
<action application="set" data="effective_caller_id_number=${trunk.cpn}" />
<action application="set" data="effective_caller_id_name=${trunk.cpn}" />
<!-- Recording -->
<#include "rec.xml">
<!-- /Recording -->
@ -34,20 +37,16 @@
"x_dial_type=MANUAL",
"x_call_type=OUTBOUND",
"x_agent_type=${callerAgent.type}",
"x_called_number=${targetNumber}",
"x_called_number=${calledNumber}",
"x_trunk_id=${trunk.id}",
"x_cpn=${trunk.cpn}",
"origination_uuid=${connId}",
"sip_invite_call_id=${r'${sip_call_id}'}",
"sip_contact_user=${callerAgent.agent}",
"callee_id_number=${callerAgent.agent}",
"callee_id_name=${callerAgent.account}",
"sip_h_Call-Info=${callerAgent.agent}@${tenant.realm}",
"sip_from_user=${trunk.cpn}",
"origination_caller_id_number=${trunk.cpn}",
"origination_caller_id_name=${callerAgent.account}"
"origination_caller_id_name=${trunk.cpn}"
]>
-->

View File

@ -0,0 +1,18 @@
package com.pudonghot.yo.fsagent.api;
import java.util.List;
import com.pudonghot.yo.fsagent.api.request.ReqCampaignDial;
/**
* @author Donghuang <br>
* Dec 09, 2019 16:25:19
*/
public interface CampaignDialService {
/**
* campaign dial
* @param req request
* @return call uuid
*/
List<String> queueDial(ReqCampaignDial req);
}

View File

@ -33,20 +33,4 @@ public interface IvrTransferService {
* @return agent unhold fifo dialplan name
*/
String agentUnholdFifo(String realm, String agentId);
/**
* save call data
*
* @param agentId agent id
* @param callData call data
*/
void saveCallData(String agentId, String callData);
/**
* get call data
*
* @param agentId agent id
* @return call data
*/
String getCallData(String agentId);
}

View File

@ -0,0 +1,20 @@
package com.pudonghot.yo.fsagent.api.request;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.List;
import java.io.Serializable;
import org.apache.commons.lang3.tuple.Pair;
/**
* @author Donghuang
* @date Mar 24, 2020 15:22:13
*/
@Getter
@Setter
@ToString
public class ReqCampaignDial implements Serializable {
private Integer campaignId;
private List<Pair<String, String>> callingList;
}

View File

@ -141,6 +141,26 @@ public class Event {
return getHeader("variable_x_conn_id");
}
/**
* Convenience method.
* get called number
*
* @return called number
*/
public String getCalledNumber() {
return getHeader("variable_x_called_number");
}
/**
* Convenience method.
* get dial type
*
* @return dial type
*/
public String getDialType() {
return getHeader("variable_x_dial_type");
}
/**
* Convenience method.
* get other number

View File

@ -32,6 +32,34 @@ public interface AgentStatusMapper extends BaseMapper<Integer, AgentStatus> {
@Param("lockKey") String lockKey,
@Param("limit") int limit);
/**
* count online agent of queue
* @param queueId queue id
* @return online agent count of queue
*/
int countOnlineOfQueue(@Param("queueId") Integer queueId);
/**
* count idle agent of queue
* @param queueId queue id
* @return idle agent count of queue
*/
int countIdleOfQueue(@Param("queueId") Integer queueId);
/**
* count online agent of group
* @param groupId group id
* @return online agent count of group
*/
int countOnlineOfGroup(@Param("groupId") Integer groupId);
/**
* count idle agent of group
* @param groupId group id
* @return idle agent count of group
*/
int countIdleOfGroup(@Param("groupId") Integer groupId);
/**
* ACW clean up
*

View File

@ -32,27 +32,7 @@
update <include refid="table" /> s
join (
select s.id
from <include refid="table"/> s
join br_agent a
on a.id = s.agent_id
join br_queue_agent qa
on qa.agent_id = a.id
where qa.queue_id = #{queueId}
and a.active = 1
and s.registered = 1
and s.state = 'IDLE'
and (s.status = 'READY' or
(s.status = 'ACW' and
<![CDATA[
s.acw_time <= now() - interval a.wrap_up_time second
]]>
)
)
and s.lock_key is null
<include refid="idleOfQueue" />
<if test="limit > 0">
order by rand()
limit #{limit}
@ -60,7 +40,6 @@
) t
on s.id = t.id
set s.lock_key = #{lockKey},
s.status = 'READY',
s.acw_time = null,
s.updated_time = now()
</update>
@ -69,23 +48,7 @@
update <include refid="table" /> s
join (
select s.id
from <include refid="table"/> s
join br_agent a
on a.id = s.agent_id
where a.group_id = #{groupId}
and a.active = 1
and s.registered = 1
and s.state = 'IDLE'
and (s.status = 'READY' or
(s.status = 'ACW' and
<![CDATA[
s.acw_time < now() - interval a.wrap_up_time second
]]>
)
)
and s.lock_key is null
<include refid="idleOfGroup" />
<if test="limit > 0">
order by rand()
@ -94,11 +57,49 @@
) t
on s.id = t.id
set s.lock_key = #{lockKey},
s.status = 'READY',
s.acw_time = null,
s.updated_time = now()
</update>
<select id="countOnlineOfQueue" resultType="int">
select count(s.id)
from <include refid="table"/> s
join br_agent a
on a.id = s.agent_id
join br_queue_agent qa
on qa.agent_id = a.id
where qa.queue_id = #{queueId}
and a.active = 1
and s.registered = 1
and s.status = 'READY'
</select>
<select id="countIdleOfQueue" resultType="int">
select count(s.id)
<include refid="idleOfQueue" />
</select>
<select id="countOnlineOfGroup" resultType="int">
select count(s.id)
from <include refid="table"/> s
join br_agent a
on a.id = s.agent_id
where a.group_id = #{groupId}
and a.active = 1
and s.registered = 1
and s.status = 'READY'
</select>
<select id="countIdleOfGroup" resultType="int">
select count(s.id)
<include refid="idleOfGroup" />
</select>
<update id="acwCleanup">
update <include refid="table" /> s
@ -115,4 +116,41 @@
where s.state = 'ACW'
and s.acw_time is not null
</update>
<sql id="idleOfQueue">
from <include refid="table"/> s
join br_agent a
on a.id = s.agent_id
join br_queue_agent qa
on qa.agent_id = a.id
where qa.queue_id = #{queueId}
<include refid="idleCondition" />
</sql>
<sql id="idleOfGroup">
from <include refid="table"/> s
join br_agent a
on a.id = s.agent_id
where a.group_id = #{groupId}
<include refid="idleCondition" />
</sql>
<sql id="idleCondition">
and a.active = 1
and s.registered = 1
and s.status = 'READY'
and s.lock_key is null
and (s.state = 'IDLE' or
(s.status = 'ACW' and
<![CDATA[
s.acw_time < now() - interval a.wrap_up_time second
]]>
)
)
</sql>
</mapper>

View File

@ -12,22 +12,43 @@ import com.pudonghot.yo.model.domain.Trunk;
public interface TrunkMapper extends BaseMapper<Integer, Trunk> {
/**
* list trunks of agent group id
* list trunks of strategy
*
* @param strategyId strategy id
* @return outbound prefixes
* @return trunks
*/
List<Trunk> listOfStrategyId(
List<Trunk> listOfStrategy(
@Param("strategyId")
Integer strategyId);
/**
* find trunk of strategy
*
* @param strategyId strategy id
* @return trunk found
*/
Trunk findOfStrategy(
@Param("strategyId")
Integer strategyId);
/**
* list trunks of agent group id
* list trunks of agent group
*
* @param agentGroupId agent group id
* @return outbound prefixes
* @return trunks found
*/
List<Trunk> listOfAgentGroupId(
List<Trunk> listOfAgentGroup(
@Param("agentGroupId")
Integer agentGroupId);
/**
* find trunk of agent group
*
* @param agentGroupId agent group id
* @return trunk found
*/
Trunk findOfAgentGroup(
@Param("agentGroupId")
Integer agentGroupId);
}

View File

@ -9,7 +9,17 @@
-->
<mapper namespace="com.pudonghot.yo.mapper.TrunkMapper">
<select id="listOfStrategyId" resultType="com.pudonghot.yo.model.domain.Trunk">
<select id="listOfStrategy" resultType="com.pudonghot.yo.model.domain.Trunk">
<include refid="selectOfStrategy" />
</select>
<select id="findOfStrategyId" resultType="com.pudonghot.yo.model.domain.Trunk">
<include refid="selectOfStrategy" />
order by rand()
limit 1
</select>
<sql id="selectOfStrategy">
select t.* from br_trunk t
join br_gateway gw
@ -30,9 +40,19 @@
where ts.id = #{strategyId}
and t.active = 1
</sql>
<select id="listOfAgentGroup" resultType="com.pudonghot.yo.model.domain.Trunk">
<include refid="selectOfStrategy" />
</select>
<select id="listOfAgentGroupId" resultType="com.pudonghot.yo.model.domain.Trunk">
<select id="findOfAgentGroup" resultType="com.pudonghot.yo.model.domain.Trunk">
<include refid="selectOfStrategy" />
order by rand()
limit 1
</select>
<sql id="listOfAgentGroup">
select t.* from br_trunk t
join br_gateway gw
@ -55,7 +75,7 @@
where agts.agent_group_id = #{agentGroupId}
and t.active = 1
</select>
</sql>
<select id="list" resultType="com.pudonghot.yo.model.domain.Trunk">
select

View File

@ -1,11 +1,33 @@
package com.pudonghot.yo.mapper;
import com.pudonghot.yo.model.domain.TrunkStrategy;
import java.util.List;
import com.wacai.tigon.mybatis.BaseMapper;
import org.apache.ibatis.annotations.Param;
import com.pudonghot.yo.model.domain.TrunkStrategy;
/**
* @author Donghuang <br>
* Nov 30, 2019 18:42:04
*/
public interface TrunkStrategyMapper extends BaseMapper<Integer, TrunkStrategy> {
/**
* list trunk strategies of campaign
*
* @param campaignId campaign id
* @return trunk strategies of campaign
*/
List<TrunkStrategy> listOfCampaign(
@Param("campaignId")
Integer campaignId);
/**
* list trunk strategies of agent group
*
* @param agentGroupId agent group id
* @return trunk strategies of agent group
*/
List<TrunkStrategy> listOfAgentGroup(
@Param("agentGroupId")
Integer agentGroupId);
}

View File

@ -8,4 +8,23 @@
*/
-->
<mapper namespace="com.pudonghot.yo.mapper.TrunkStrategyMapper">
<select id="listOfCampaign" resultType="com.pudonghot.yo.model.domain.TrunkStrategy">
select t.* from
<include refid="table" /> t
join br_campaign_trunk_strategy cts
on t.id = cts.trunk_strategy_id
where cts.campaign_id = #{campaignId}
and t.active = 1
</select>
<select id="listOfAgentGroup" resultType="com.pudonghot.yo.model.domain.TrunkStrategy">
select t.* from
<include refid="table" /> t
join br_agent_group_trunk_strategy agts
on t.id = agts.trunk_strategy_id
where agts.agent_group_id = #{agentGroupId}
and t.active = 1
</select>
</mapper>

View File

@ -32,6 +32,10 @@ public class AgentStatusMapperTest {
@Test
public void testAcwCleanup() {
mapper.countOnlineOfQueue(1);
mapper.countIdleOfQueue(1);
mapper.countOnlineOfGroup(1);
mapper.countIdleOfGroup(1);
mapper.acwCleanup();
}
}

View File

@ -1,7 +1,10 @@
package com.pudonghot.yo.model;
import lombok.Getter;
import java.util.Date;
import java.time.ZoneId;
import java.io.Serializable;
import java.time.LocalDateTime;
import com.pudonghot.yo.util.TimeUtils;
import org.apache.commons.lang3.StringUtils;
import com.fasterxml.jackson.annotation.JsonValue;
@ -34,6 +37,16 @@ public class DailyTime implements Serializable {
this.val = val;
}
public boolean before(final Date date) {
return secondOfDay < LocalDateTime.ofInstant(date.toInstant(),
ZoneId.systemDefault()).toLocalTime().toSecondOfDay();
}
public boolean after(final Date date) {
return secondOfDay > LocalDateTime.ofInstant(date.toInstant(),
ZoneId.systemDefault()).toLocalTime().toSecondOfDay();
}
/**
* {@inheritDoc}
*/

View File

@ -1,10 +1,14 @@
package com.pudonghot.yo;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.junit.Test;
import java.util.Date;
import java.time.LocalTime;
import lombok.extern.slf4j.Slf4j;
import com.pudonghot.yo.model.DailyTime;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.pudonghot.yo.model.agentevent.AgentEvent;
import org.junit.Test;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateFormatUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
/**
* @author Donghuang <br>
@ -26,4 +30,20 @@ public class TestDriver {
final String result = objectMapper.writeValueAsString(agentEvent);
log.info("Result: [{}].", result);
}
@Test
public void testDailyTime() {
log.info("ToSecondOfDay: [{}].", LocalTime.now().toSecondOfDay());
log.info("SecondOfDay: [{}].",
new DailyTime(DateFormatUtils.format(new Date(), "HH:mm")).getSecondOfDay());
log.info("DailyTime: [{}].", new DailyTime("09:11").getSecondOfDay());
log.info("DailyTime: [{}].", new DailyTime("00:00"));
log.info("DailyTime: [{}].", new DailyTime("19:00:04"));
log.info("DailyTime: [{}].", new DailyTime("23:00").getSecondOfDay());
log.info("DailyTime: [{}].", new DailyTime("09:00:04").getSecondOfDay());
log.info("DailyTime: [{}].", new DailyTime("00:00").getSecondOfDay());
log.info("DailyTime: [{}].", new DailyTime("09:00").before(new Date()));
log.info("DailyTime: [{}].", new DailyTime("23:00:03").after(new Date()));
}
}

Binary file not shown.

Binary file not shown.

View File

@ -16,13 +16,20 @@ get_real_path() {
}
install_server() {
SERVER="$1"
APP="$2"
TARGET_DIR="/data/program/$APP"
LOGS_DIR="/data/program/logs/$APP"
ssh "$SERVER" "mkdir -p $TARGET_DIR"
ssh "$SERVER" "tar -xzv -C $TARGET_DIR" < springboot-app.tar.gz
ssh "$SERVER" "mkdir -p $LOGS_DIR && ln -sf $LOGS_DIR $TARGET_DIR/logs"
local SERVER="$1"
local APP="$2"
local PORT="$3"
if [ -z "$PORT" ]; then
PORT='22'
fi
local BASE_DIR='/data/program'
local TARGET_DIR="$BASE_DIR/$APP"
local LOGS_DIR="$BASE_DIR/logs/$APP"
ssh -p "$PORT" "$SERVER" "mkdir -p $TARGET_DIR"
ssh -p "$PORT" "$SERVER" "tar -xzv -C $TARGET_DIR" < springboot-app.tar.gz
ssh -p "$PORT" "$SERVER" "mkdir -p $LOGS_DIR && ln -sf $LOGS_DIR $TARGET_DIR/logs"
}
@ -42,8 +49,9 @@ fi
if [ "$2" == "test" ]; then
echo "Install test."
install_server 'appweb@118.24.251.131' $1
elif ["$2" == "prod" ]; then
echo "Deploy prod."
elif [ "$2" == "prod" ]; then
echo "Install prod."
install_server 'xiandou@113.87.175.72' $1 '51022'
fi
popd > /dev/null

View File

@ -0,0 +1,63 @@
package com.pudonghot.yo.service;
/**
* @author Donghuang
* @date Jul 23, 2020 09:57:09
*/
public interface CommonCallDataService {
/**
* save call data
*
* @param agentId agent id
* @param callData call data
*/
void saveIvrCallData(String agentId, String callData);
/**
* get call data
*
* @param agentId agent id
* @return call data
*/
String getIvrCallData(String agentId);
/**
* save call data
*
* @param callUuid call uuid
* @param callData call data
*/
void saveCampaignCallData(String callUuid, String callData);
/**
* get call data
*
* @param callUuid call uuid
* @return call data
*/
String getCampaignCallData(String callUuid);
/**
* get campaign channel
*
* @param campaignId campaign id
* @return count of campaign channel
*/
int getCampaignChannel(Integer campaignId);
/**
* increase campaign channel
*
* @param campaignId campaign id
*/
int incrCampaignChannel(Integer campaignId);
/**
* decrease campaign channel
*
* @param campaignId campaign id
*/
int decrCampaignChannel(Integer campaignId);
}

View File

@ -16,7 +16,6 @@ import com.pudonghot.yo.service.CommonChannelService;
import com.pudonghot.yo.service.CommonDelayScheduleService;
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;
/**
* @author Donghuang <br>
@ -198,7 +197,7 @@ public class CommonAgentStatusServiceImpl
@Override
public void inACall(final Agent agent) {
log.info("Agent [{}] in a call.", agent);
updateState(agent);
updateState(agent, AgentStatus.State.IN_A_CALL);
}
/**
@ -216,8 +215,11 @@ public class CommonAgentStatusServiceImpl
public void acw(final Agent agent) {
log.info("ACW agent [{}].", agent);
final AgentStatus agentStatus = findInACallAgentStatus(agent);
final AgentStatus agentStatus =
findInACallAgentStatus(agent);
if (agentStatus == null) {
log.info("No agent status found, ignore.");
return;
}
@ -227,7 +229,7 @@ public class CommonAgentStatusServiceImpl
delayScheduleService.acw(agent);
}
else {
setState(agent, agentStatus);
agentStatus.setState(AgentStatus.State.IDLE);
}
log.info("Update agent status [{}].", agentStatus);
@ -260,8 +262,12 @@ public class CommonAgentStatusServiceImpl
log.info("End ACW agent [{}].", agent);
final AgentStatus agentStatus = findValidAgentStatus(agent);
AssertUtils.state(agentStatus.getState() == AgentStatus.State.ACW,
AGENT_UPDATE_STATUS_FAIL);
if (agentStatus.getState() != AgentStatus.State.ACW) {
log.info("Current state is not ACW, ignore.");
return;
}
clear(agentStatus);
setState(agent, agentStatus);
agentStatus.setAcwTime(null);
@ -283,8 +289,7 @@ public class CommonAgentStatusServiceImpl
@Override
public void idle(final Agent agent) {
log.info("Idle agent [{}] call [{}].", agent);
updateState(agent);
updateState(agent, AgentStatus.State.IDLE);
}
/**
@ -319,10 +324,10 @@ public class CommonAgentStatusServiceImpl
return agentStatus;
}
private void updateState(final Agent agent) {
private void updateState(final Agent agent, final AgentStatus.State state) {
final AgentStatus agentStatus = findValidAgentStatus(agent);
clear(agentStatus);
setState(agent, agentStatus);
agentStatus.setState(state);
log.info("Update agent status [{}].", agentStatus);
agentStatusMapper.update(agentStatus);

View File

@ -0,0 +1,101 @@
package com.pudonghot.yo.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RMapCache;
import java.util.concurrent.TimeUnit;
import org.redisson.api.RedissonClient;
import com.pudonghot.yo.service.CommonCallDataService;
import org.springframework.beans.factory.annotation.Value;
/**
* @author Donghuang
* @date Jul 23, 2020 10:04:23
*/
@Slf4j
public class CommonCallDataServiceImpl implements CommonCallDataService {
private final RMapCache<String, String> CACHE;
private final RMapCache<Integer, Integer> CAMPAIGN_CHANNEL;
@Value("${yo.common-service.call-data.timeout-seconds:120}")
private long callDataTimeoutSeconds;
public CommonCallDataServiceImpl(final RedissonClient redisson) {
this.CACHE = redisson.getMapCache("CALL_DATA");
this.CAMPAIGN_CHANNEL = redisson.getMapCache("CAMPAIGN_CHANNEL");
}
/**
* {@inheritDoc}
*/
@Override
public void saveIvrCallData(final String agentId, final String callData) {
log.info("Save IVR call data [{}] -> [{}].", agentId, callData);
put(ivrCacheKey(agentId), callData);
}
/**
* {@inheritDoc}
*/
@Override
public String getIvrCallData(final String agentId) {
log.debug("Get IVR call data [{}].", agentId);
return CACHE.get(ivrCacheKey(agentId));
}
/**
* {@inheritDoc}
*/
@Override
public void saveCampaignCallData(String callUuid, String callData) {
log.info("Save campaign call data [{}] -> [{}].", callUuid, callData);
put(callUuid, callData);
}
/**
* {@inheritDoc}
*/
@Override
public String getCampaignCallData(final String callUuid) {
log.debug("Get campaign call data [{}].", callUuid);
return CACHE.get(callUuid);
}
/**
* {@inheritDoc}
*/
@Override
public int getCampaignChannel(final Integer campaignId) {
final int val = CAMPAIGN_CHANNEL.getOrDefault(campaignId, 0);
log.debug("Get campaign [{}] channel [{}].", campaignId, val);
return val;
}
/**
* {@inheritDoc}
*/
@Override
public int incrCampaignChannel(final Integer campaignId) {
final int val = CAMPAIGN_CHANNEL.addAndGet(campaignId, 1);
log.debug("Increase campaign [{}] channel [{}].", campaignId, val);
return val;
}
/**
* {@inheritDoc}
*/
@Override
public int decrCampaignChannel(final Integer campaignId) {
final int val = CAMPAIGN_CHANNEL.addAndGet(campaignId, -1);
log.debug("Decrease campaign [{}] channel [{}].", campaignId, val);
return val;
}
private void put(final String key, final String value) {
log.debug("Call data cache [{}] -> [{}].", key, value);
CACHE.put(
key, value, callDataTimeoutSeconds, TimeUnit.SECONDS);
}
private String ivrCacheKey(final String agentId) {
return "IVR_CALL_DATA:" + agentId;
}
}

View File

@ -8,4 +8,5 @@
<bean class="com.pudonghot.yo.service.impl.CommonAgentStatusServiceImpl" />
<bean class="com.pudonghot.yo.service.impl.CommonAgentEventQueueServiceImpl" />
<bean class="com.pudonghot.yo.service.impl.CommonChannelServiceImpl" />
<bean class="com.pudonghot.yo.service.impl.CommonCallDataServiceImpl" />
</beans>

View File

@ -0,0 +1,26 @@
package com.pudonghot.yo.service;
import org.junit.Test;
import org.junit.runner.RunWith;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Donghuang
* @date Jul 23, 2020 19:44:47
*/
@Slf4j
@SpringBootTest(classes = TestDriver.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class CommonCallDataServiceTest {
@Autowired
private CommonCallDataService callDataService;
@Test
public void testRun() {
log.info("Channels [{}].", callDataService.incrCampaignChannel(1));
log.info("Channels [{}].", callDataService.decrCampaignChannel(2));
}
}

View File

@ -1,56 +0,0 @@
package com.pudonghot.yo.common.util;
import org.apache.commons.lang3.StringUtils;
/**
* 手机号工具类
* Created by bingpo on 17/12/28.
*/
public class MobileUtils {
public static final int MOBILE_LENGTH = 11;
private static final String MOBILE_REG_EXP = "^1(3[0-9]|4[579]|5[0-35-9]|7[0135678]|8[0-9])\\d{8}$";
/**
* 验证手机号码
* <p>
* 移动号码段:134,135,136,137,138,139,147,150,151,152,157,158,159,170,178,182,183,184,187,188
* 联通号码段:130,131,132,145,155,156,171,175,176,185,186
* 电信号码段:133,149,153,170,173,177,180,181,189
*
* @param mobile 手机号
* @return 是否为手机号
*/
public static boolean checkMobile(String mobile) {
return StringUtils.length(mobile) == MOBILE_LENGTH && mobile.matches(MOBILE_REG_EXP);
}
/**
* 处理手机号
*
* @param mobile 手机号
* @return 处理后的手机号
*/
public static String formatMobile(String mobile) {
if (StringUtils.length(mobile) > MOBILE_LENGTH) {
return mobile.substring(mobile.length() - MOBILE_LENGTH);
}
return mobile;
}
/**
* 去掉手机号的前缀
*
* @param mobileStr mobileStr
* @return 去掉前缀后的手机号
*/
public static String trimMobilePrefix(String mobileStr) {
if (StringUtils.length(mobileStr) > MOBILE_LENGTH) {
final String mobile = mobileStr.substring(mobileStr.length() - MOBILE_LENGTH);
return checkMobile(mobile) ? mobile : mobileStr;
}
return mobileStr;
}
}

View File

@ -0,0 +1,41 @@
package com.pudonghot.yo.util;
/**
* @author Donghuang
* @date Jul 20, 2020 18:27:52
*/
public class PhoneNumberUtils {
public static final int MOBILE_LENGTH = 11;
private static final String MOBILE_REG_EXP = "^1[3-9]\\d{8}$";
/**
* 验证手机号码
* <p>
* 移动号码段:134,135,136,137,138,139,147,150,151,152,157,158,159,170,178,182,183,184,187,188
* 联通号码段:130,131,132,145,155,156,171,175,176,185,186
* 电信号码段:133,149,153,170,173,177,180,181,189
*
* @param mobile 手机号
* @return 是否为手机号
*/
public static boolean isMobile(final String mobile) {
return mobile.length() == MOBILE_LENGTH
&& mobile.matches(MOBILE_REG_EXP);
}
/**
* cleanup mobile
* 01378888666 -> 1378888666
* @param mobile mobile number
* @return mobile cleaned
*/
public static String cleanupMobile(final String mobile) {
if (mobile.length() > MOBILE_LENGTH) {
final String trim = mobile.substring(
mobile.length() - MOBILE_LENGTH);
return isMobile(trim) ? trim : mobile;
}
return mobile;
}
}

View File

@ -13,7 +13,7 @@ public class TimeUtils {
* 00:00 - 23:59
*/
public static final Pattern TIME_PATTERN =
Pattern.compile("^((?:[01]?[0-9])|(?:2[0-3])):([0-5]?[0-9])$");
Pattern.compile("^([01]?[0-9]|2[0-3]):([0-5]?[0-9])(?::([0-5]?[0-9]))?$");
/**
* @param time HH:mm
@ -23,8 +23,10 @@ public class TimeUtils {
if (StringUtils.isNotBlank(time)) {
final Matcher matcher = TIME_PATTERN.matcher(time);
if (matcher.find()) {
final String sec = matcher.group(3);
return Integer.parseInt(matcher.group(1)) * 60 * 60 +
Integer.parseInt(matcher.group(2)) * 60;
Integer.parseInt(matcher.group(2)) * 60 +
(StringUtils.isNotBlank(sec) ? Integer.parseInt(sec) : 0);
}
}
return null;

View File

@ -1,6 +1,5 @@
package com.pudonghot.yo.openapi.controller;
import com.pudonghot.yo.fsagent.api.IvrTransferService;
import lombok.extern.slf4j.Slf4j;
import com.pudonghot.yo.model.domain.Agent;
import org.apache.commons.lang3.StringUtils;
@ -13,6 +12,7 @@ import com.pudonghot.yo.model.exception.AssertUtils;
import com.pudonghot.yo.openapi.service.AgentService;
import com.pudonghot.yo.openapi.auth.SessionAbility;
import com.pudonghot.yo.service.CommonChannelService;
import com.pudonghot.yo.fsagent.api.IvrTransferService;
import com.pudonghot.yo.openapi.request.ReqAgentDialout;
import com.pudonghot.yo.fsagent.api.request.ReqAgentDial;
import com.pudonghot.yo.service.CommonAgentStatusService;

View File

@ -0,0 +1,48 @@
package com.pudonghot.yo.openapi.controller;
import javax.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import com.pudonghot.yo.model.domain.Agent;
import org.springframework.stereotype.Controller;
import com.pudonghot.yo.openapi.request.ReqCampaign;
import com.pudonghot.yo.openapi.auth.SessionAbility;
import com.pudonghot.yo.openapi.service.AgentService;
import com.pudonghot.yo.service.CommonAgentStatusService;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.beans.factory.annotation.Autowired;
/**
* @author Donghuang
* @date Jul 02, 2020 11:53:21
*/
@Slf4j
@Controller
public class CampaignController implements SessionAbility {
@Autowired
private AgentService agentService;
@Autowired
private CommonAgentStatusService agentStatusService;
@RequestMapping("/resource/task/{account}")
public void addToQueue(
@PathVariable("account")
final String account,
@Valid final ReqCampaign form) {
final Agent agent = agentService.findValid(
form.getTenantId(), account);
agentStatusService.findValidAgentStatus(agent);
// TODO Add agent to queue
}
@RequestMapping("/resource/task/start")
public void start(@Valid final ReqCampaign form) {
// TODO start campaign
}
@RequestMapping("/resource/task/stop")
public void stop(@Valid final ReqCampaign form) {
// TODO stop campaign
}
}

View File

@ -1,28 +0,0 @@
package com.pudonghot.yo.openapi.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import com.pudonghot.yo.fsagent.api.DialService;
import com.pudonghot.yo.openapi.dto.request.AgentDial;
import com.pudonghot.yo.fsagent.api.request.ReqAgentDial;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import com.pudonghot.yo.openapi.dto.response.AgentDialResp;
/**
* @author Donghuang <br>
* Jan 03, 2020 18:20:59
*/
@Slf4j
@Controller
@RequestMapping("/v1")
public class DialController {
@Autowired
private DialService dialService;
@RequestMapping("/dial")
public AgentDialResp dial(final AgentDial req) {
return new AgentDialResp(dialService.agentDial(
req.copy(new ReqAgentDial())).getUuid());
}
}

View File

@ -0,0 +1,27 @@
package com.pudonghot.yo.openapi.request;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.validation.constraints.NotBlank;
/**
* @author Donghuang
* @date Jul 09, 2020 11:09:01
*/
@Getter
@Setter
@ToString
public class ReqCampaign extends BaseForm {
@NotBlank
private String campaignKey;
private String campaignName;
public void setTaskid(final String taskId) {
this.campaignKey = taskId;
}
public void setTaskname(final String taskName) {
this.campaignName = taskName;
}
}

View File

@ -31,6 +31,6 @@ yo.openapi.default-tenant=GOBLIN
# Dubbo
## Dubbo Registry
dubbo.registry.address=zookeeper://172.16.67.223:2181
dubbo.registry.address=zookeeper://172.18.4.35:2181
dubbo.registry.file=${user.home}/dubbo-cache/${spring.application.name}/dubbo.cache
yo.fsagent.dubbo.service.version=1.0.0

View File

@ -1,7 +1,7 @@
package com.pudonghot.yo.state.service.impl;
import java.util.Date;
import java.util.Map;
import java.util.Date;
import java.util.HashMap;
import lombok.extern.slf4j.Slf4j;
import com.wacai.tigon.mybatis.Search;
@ -60,6 +60,8 @@ public class AgentStatusScheduleServiceImpl implements AgentStatusScheduleServic
update.put(AgentStatus.REGISTERED, false);
agentStatusMapper.update(update,
new Search(AgentStatus.CHECK_REG_KEY, checkRegKey));
log.info("ACW cleanup.");
agentStatusMapper.acwCleanup();
}
private Map<String, Object> checkRegKeyUpdate(final String checkRegKey) {