diff --git a/cms/local-repo/com/pudonghot/yo/yo-cms-web/0.0.1-SNAPSHOT/yo-cms-web-0.0.1-SNAPSHOT.jar b/cms/local-repo/com/pudonghot/yo/yo-cms-web/0.0.1-SNAPSHOT/yo-cms-web-0.0.1-SNAPSHOT.jar index 12fdc462..afb44900 100644 Binary files a/cms/local-repo/com/pudonghot/yo/yo-cms-web/0.0.1-SNAPSHOT/yo-cms-web-0.0.1-SNAPSHOT.jar and b/cms/local-repo/com/pudonghot/yo/yo-cms-web/0.0.1-SNAPSHOT/yo-cms-web-0.0.1-SNAPSHOT.jar differ diff --git a/cms/src/main/java/com/pudonghot/yo/cms/controller/AgentStatusController.java b/cms/src/main/java/com/pudonghot/yo/cms/controller/AgentStatusController.java new file mode 100644 index 00000000..56fec62e --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/controller/AgentStatusController.java @@ -0,0 +1,35 @@ +package com.pudonghot.yo.cms.controller; + +import com.wacai.tigon.form.FormList; +import com.pudonghot.yo.model.domain.Agent; +import com.wacai.tigon.web.annotation.ListApi; +import com.wacai.tigon.web.annotation.FilterCol; +import com.pudonghot.yo.model.domain.AgentStatus; +import org.springframework.stereotype.Controller; +import com.pudonghot.yo.cms.annotation.TenantResource; +import com.wacai.tigon.web.controller.BaseQueryController; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + * @author Donghuang + * @date Jul 09, 2020 16:44:23 + */ +@Controller +@ListApi(searchCols = { + Agent.NAME, + Agent.ACCOUNT, + Agent.AGENT, + Agent.NOTE +}, +filterCols = { + @FilterCol(param = AgentStatus.STATUS, type = AgentStatus.Status.class), + @FilterCol(param = AgentStatus.STATE, type = AgentStatus.State.class), + @FilterCol(param = AgentStatus.REGISTERED, type = boolean.class), +}) +@TenantResource +@RequestMapping("/agent-status") +public class AgentStatusController + extends BaseQueryController { +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormSequence.java b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormSequence.java index 977d4598..b32bb5cc 100644 --- a/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormSequence.java +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormSequence.java @@ -2,10 +2,7 @@ package com.pudonghot.yo.cms.form.create; import lombok.Getter; import lombok.Setter; -import javax.validation.constraints.Min; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Pattern; -import javax.validation.constraints.NotBlank; +import javax.validation.constraints.*; import com.wacai.tigon.format.annotation.Trim; import com.pudonghot.yo.cms.form.BaseCreateForm; @@ -20,10 +17,14 @@ public class CreateFormSequence extends BaseCreateForm { @NotBlank @Pattern(regexp = "^\\w+$", message = "序列名必须是字母数字下划线构成") private String name; + @Min(2) + @Max(16) + private int length; @Min(1) @NotNull private Long initVal; @Min(1) + @Max(128) @NotNull private Long step; } diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/AgentStatusService.java b/cms/src/main/java/com/pudonghot/yo/cms/service/AgentStatusService.java new file mode 100644 index 00000000..f3e9e4f9 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/AgentStatusService.java @@ -0,0 +1,12 @@ +package com.pudonghot.yo.cms.service; + +import com.pudonghot.yo.model.domain.AgentStatus; +import com.wacai.tigon.service.BaseQueryService; + +/** + * @author Donghuang + * @date Jul 09, 2020 16:53:30 + */ +public interface AgentStatusService + extends BaseQueryService { +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/SequenceService.java b/cms/src/main/java/com/pudonghot/yo/cms/service/SequenceService.java index 41f30e59..688aa3a2 100644 --- a/cms/src/main/java/com/pudonghot/yo/cms/service/SequenceService.java +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/SequenceService.java @@ -1,9 +1,9 @@ package com.pudonghot.yo.cms.service; -import com.pudonghot.yo.cms.form.create.CreateFormSequence; -import com.pudonghot.yo.cms.form.update.UpdateFormSequence; import com.pudonghot.yo.model.domain.Sequence; import com.wacai.tigon.service.BaseCrudByFormService; +import com.pudonghot.yo.cms.form.create.CreateFormSequence; +import com.pudonghot.yo.cms.form.update.UpdateFormSequence; /** * @author Donghuang
@@ -22,4 +22,13 @@ public interface SequenceService * @return next seq val */ Long nextVal(Integer tenantId, String name); + + /** + * next seq val string, padding 0 to length + * + * @param tenantId tenant id + * @param name seq name + * @return next val + */ + String nextValStr(Integer tenantId, String name); } diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/impl/AgentGroupServiceImpl.java b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/AgentGroupServiceImpl.java index 8e0d0aa7..46193fc3 100644 --- a/cms/src/main/java/com/pudonghot/yo/cms/service/impl/AgentGroupServiceImpl.java +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/AgentGroupServiceImpl.java @@ -99,8 +99,7 @@ public class AgentGroupServiceImpl super.beforeInsert(model); if (StringUtils.isBlank(model.getIdentifier())) { model.setIdentifier(identifierPrefix + - StringUtils.leftPad(String.valueOf( - seqService.nextVal(model.getTenantId(), seqName)), 6, '0')); + seqService.nextValStr(model.getTenantId(), seqName)); } } diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/impl/AgentServiceImpl.java b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/AgentServiceImpl.java index a8b3a743..0d5d032d 100644 --- a/cms/src/main/java/com/pudonghot/yo/cms/service/impl/AgentServiceImpl.java +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/AgentServiceImpl.java @@ -131,7 +131,7 @@ public class AgentServiceImpl protected void beforeInsert(final Agent model) { super.beforeInsert(model); if (StringUtils.isBlank(model.getAgent())) { - model.setAgent(String.valueOf(seqService.nextVal(model.getTenantId(), seqName))); + model.setAgent(seqService.nextValStr(model.getTenantId(), seqName)); } if (StringUtils.isBlank(model.getPassword())) { diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/impl/AgentStatusServiceImpl.java b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/AgentStatusServiceImpl.java new file mode 100644 index 00000000..f0c12dca --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/AgentStatusServiceImpl.java @@ -0,0 +1,19 @@ +package com.pudonghot.yo.cms.service.impl; + +import org.springframework.stereotype.Service; +import com.pudonghot.yo.mapper.AgentStatusMapper; +import com.pudonghot.yo.model.domain.AgentStatus; +import com.pudonghot.yo.cms.service.AgentStatusService; +import com.wacai.tigon.service.support.BaseQueryServiceSupport; + +/** + * @author Donghuang
+ * Dec 24, 2019 16:49:51 + */ +@Service +public class AgentStatusServiceImpl + extends BaseQueryServiceSupport + implements AgentStatusService { +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/impl/QueueServiceImpl.java b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/QueueServiceImpl.java index a1bdb821..dd8b693e 100644 --- a/cms/src/main/java/com/pudonghot/yo/cms/service/impl/QueueServiceImpl.java +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/QueueServiceImpl.java @@ -50,9 +50,7 @@ public class QueueServiceImpl final Tenant tenant = tenantMapper.find(tenantId); Assert.state(tenant != null, () -> "No tenant [" + tenantId + "] found"); - model.setIdentifier(identifierPrefix + - StringUtils.leftPad(String.valueOf( - seqService.nextVal(tenantId, seqName)), 6, '0')); + model.setIdentifier(identifierPrefix + seqService.nextValStr(tenantId, seqName)); } } } diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/impl/SequenceServiceImpl.java b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/SequenceServiceImpl.java index 334b2840..590cc2e5 100644 --- a/cms/src/main/java/com/pudonghot/yo/cms/service/impl/SequenceServiceImpl.java +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/SequenceServiceImpl.java @@ -1,13 +1,14 @@ package com.pudonghot.yo.cms.service.impl; -import com.pudonghot.yo.cms.form.create.CreateFormSequence; import lombok.extern.slf4j.Slf4j; import com.wacai.tigon.mybatis.Search; +import org.apache.commons.lang3.StringUtils; import org.springframework.util.Assert; import org.springframework.stereotype.Service; import com.pudonghot.yo.mapper.SequenceMapper; import com.pudonghot.yo.model.domain.Sequence; import com.pudonghot.yo.cms.service.SequenceService; +import com.pudonghot.yo.cms.form.create.CreateFormSequence; import com.pudonghot.yo.cms.form.update.UpdateFormSequence; import org.springframework.transaction.annotation.Transactional; import com.wacai.tigon.service.support.BaseCrudByFormServiceSupport; @@ -42,17 +43,18 @@ public class SequenceServiceImpl * {@inheritDoc} */ @Override - @Transactional + @Transactional(rollbackFor = RuntimeException.class) public Long nextVal(final Integer tenantId, final String name) { - final Sequence seq = mapper.findForUpdate( - new Search(Sequence.NAME, name) - .eq(Sequence.TENANT_ID, tenantId) - .eq(Sequence.ACTIVE, true)); - Assert.state(seq != null, () -> "No valid sequence [" + name + "] found"); - final Long nextVal = seq.getCurrentVal() + seq.getStep(); - seq.setCurrentVal(nextVal); - mapper.update(seq); - return nextVal; + return nextVal(tenantId, name, false); + } + + /** + * {@inheritDoc} + */ + @Override + @Transactional(rollbackFor = RuntimeException.class) + public String nextValStr(final Integer tenantId, final String name) { + return nextVal(tenantId, name, true); } /** @@ -61,8 +63,28 @@ public class SequenceServiceImpl @Override protected void beforeInsert(final Sequence model) { super.beforeInsert(model); + Assert.state(String.valueOf(model.getInitVal()).length() <= model.getLength(), + "Sequence init value exceeds length"); if (model.getCurrentVal() == null) { model.setCurrentVal(model.getInitVal()); } } + + T nextVal(final Integer tenantId, final String name, final boolean str) { + final Sequence seq = mapper.findForUpdate( + new Search(Sequence.TENANT_ID, tenantId) + .eq(Sequence.NAME, name) + .eq(Sequence.ACTIVE, true)); + Assert.state(seq != null, () -> + "No valid sequence [" + name + "] found"); + final Long nextVal = seq.getCurrentVal() + seq.getStep(); + final String strVal = String.valueOf(nextVal); + Assert.state(strVal.length() <= seq.getLength(), + "Sequence next value exceeds length"); + seq.setCurrentVal(nextVal); + mapper.update(seq); + return str ? (T) StringUtils.leftPad(strVal, seq.getLength(), '0') + : (T) nextVal; + } + } diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/impl/TrunkServiceImpl.java b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/TrunkServiceImpl.java index 623e2459..d9e79e20 100644 --- a/cms/src/main/java/com/pudonghot/yo/cms/service/impl/TrunkServiceImpl.java +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/TrunkServiceImpl.java @@ -1,29 +1,27 @@ package com.pudonghot.yo.cms.service.impl; import java.util.*; - -import com.pudonghot.yo.cms.form.SessionForm; -import com.pudonghot.yo.cms.form.create.CreateFormTrunk; import com.pudonghot.yo.mapper.*; -import com.pudonghot.yo.model.domain.*; import lombok.extern.slf4j.Slf4j; import java.util.stream.Collectors; import java.util.function.BiConsumer; - import com.wacai.tigon.mybatis.Search; +import com.pudonghot.yo.model.domain.*; import com.wacai.tigon.model.ViewModel; import org.springframework.util.Assert; 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.cellphone.CellphoneService; import com.pudonghot.yo.cms.service.TrunkService; +import com.pudonghot.yo.cellphone.CellphoneService; import com.pudonghot.yo.cms.service.AreaCodeService; import com.pudonghot.yo.cms.service.SequenceService; -import org.springframework.beans.factory.annotation.Value; +import com.pudonghot.yo.cms.form.create.CreateFormTrunk; import com.pudonghot.yo.cms.form.update.UpdateFormTrunk; +import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Autowired; /** @@ -142,8 +140,7 @@ public class TrunkServiceImpl protected void beforeInsert(final Trunk model) { super.beforeInsert(model); if (StringUtils.isBlank(model.getPrefix())) { - model.setPrefix(String.valueOf( - seqService.nextVal(model.getTenantId(), seqName))); + model.setPrefix(seqService.nextValStr(model.getTenantId(), seqName)); } } diff --git a/cms/src/main/resources/application-test.properties b/cms/src/main/resources/application-test.properties index e604a9d0..d21d938e 100644 --- a/cms/src/main/resources/application-test.properties +++ b/cms/src/main/resources/application-test.properties @@ -24,7 +24,8 @@ spring.redis.port=6379 # CAS tigon.shiro.cas.server.addr=http://118.24.251.131:8080/cas -tigon.shiro.cas.client.addr=http://118.24.251.131:8080/cms/ +tigon.shiro.cas.client.addr=http://118.24.251.131:8080/cms +tigon.shiro.cas.login-success-url=/cms/ tigon.shiro.filter-chain=${site.context-path}/auth/login=anon \ /=anon \ /state-machine/**=anon \ diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/BaseDialplanController.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/BaseDialplanController.java index e21b336e..e6240c1f 100644 --- a/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/BaseDialplanController.java +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/BaseDialplanController.java @@ -1,8 +1,8 @@ package com.pudonghot.yo.fsagent.controller; -import com.pudonghot.yo.model.domain.Tenant; 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.web.bind.annotation.RequestMapping; @@ -43,4 +43,12 @@ public class BaseDialplanController extends BaseXmlController { thisDn + "-" + otherDn + recordingFileExt; } + + /** + * return empty dial plan + * @return empty dial plan + */ + protected FreeMarkerView empty() { + return empty(SECTION_DIALPLAN); + } } diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/ConfigController.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/ConfigController.java index f5a36b04..3509b689 100644 --- a/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/ConfigController.java +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/ConfigController.java @@ -5,7 +5,6 @@ import java.util.List; import javax.sql.DataSource; import lombok.extern.slf4j.Slf4j; import com.wacai.tigon.mybatis.Search; -import org.apache.commons.lang3.StringUtils; import com.pudonghot.yo.model.domain.Gateway; import com.alibaba.druid.pool.DruidDataSource; import com.pudonghot.yo.fsagent.util.OdbcUtils; @@ -55,8 +54,7 @@ public class ConfigController extends BaseXmlController { final FreeMarkerView view = view("config/sofia.conf.xml"); final List gateways = gatewayService.list( new Search(Gateway.ACTIVE, true)); - gateways.forEach(g -> g.setName( - "GW" + StringUtils.leftPad(String.valueOf(g.getId()), 6, '0'))); + gateways.forEach(g -> g.setName(gatewayService.genGatewayName(g))); attr(view, "gateways", gateways); return view; diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/DialplanInternalController.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/DialplanInternalController.java index cec4a2ee..502883a3 100644 --- a/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/DialplanInternalController.java +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/DialplanInternalController.java @@ -2,14 +2,20 @@ package com.pudonghot.yo.fsagent.controller; import java.util.Map; import lombok.extern.slf4j.Slf4j; +import com.pudonghot.yo.fsagent.service.*; +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.tuple.Pair; +import org.springframework.util.DigestUtils; +import com.pudonghot.yo.model.domain.AgentGroup; import org.springframework.stereotype.Controller; -import com.pudonghot.yo.fsagent.service.AgentService; -import com.pudonghot.yo.fsagent.service.LocalApiService; +import com.pudonghot.yo.fsagent.util.CallStrUtils; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; +import static com.pudonghot.yo.fsagent.util.CallStrUtils.DialTarget; import org.springframework.web.servlet.view.freemarker.FreeMarkerView; /** @@ -22,9 +28,17 @@ public class DialplanInternalController extends BaseDialplanController { @Autowired private AgentService agentService; @Autowired + private AgentGroupService agentGroupService; + @Autowired + private QueueService queueService; + @Autowired private LocalApiService localApiService; @Autowired private IdSequence idSeq; + @Autowired + private TrunkService trunkService; + @Autowired + private GatewayService gatewayService; @RequestMapping(params = { "variable_domain_name", @@ -41,31 +55,108 @@ public class DialplanInternalController extends BaseDialplanController { @RequestParam final Map params) { - log.info("XML dialplan of domain [{}].", domain); + log.info("XML dialplan of domain [{}] [{}] -> [{}].", domain, user, calledNumber); log.debug("XML dialplan params [{}].", params); + final Agent callerAgent = + agentService.findByDomainAndAgent(domain, user); + + 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(); + } + 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!!!"); + } + + final Pair trunkInfo = trunkService.parseDialTarget( + callerAgent.getTenantId(), calledNumber); + 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"); + final String connId = idSeq.get(); + attr(view, "connId", connId); + attr(view, "callerAgent", callerAgent); + attr(view, "gatewayName", + gatewayService.genGatewayName(trunk.getGatewayId())); + attr(view, "targetNumber", targetNumber); + attr(view, "recordingLoc", recordingFile(connId, + callerAgent.getAccount(), + DigestUtils.md5DigestAsHex(targetNumber.getBytes()))); + attr(view, "postRecUrl", localApiService.getPostRecUrl()); + return view; + } + final Agent calledAgent = agentService.findOfCalled(domain, calledNumber); if (calledAgent != null) { log.info("Local extension [{}] found.", calledAgent); - final FreeMarkerView view = view(domain, "local-extension.xml"); - final String connId = idSeq.get(); - attr(view, "connId", connId); - attr(view, "destinationUser", calledAgent.getAgent()); - final Agent callerAgent = - agentService.findByDomainAndAgent(domain, user); - attr(view, "callerAgent", callerAgent); - attr(view, "calledAgent", calledAgent); - attr(view, "recordingLoc", recordingFile(connId, - callerAgent.getAccount(), - calledAgent.getAccount())); - attr(view, "postRecUrl", localApiService.getPostRecUrl()); - return view; + return agentCallAgent(domain, callerAgent, calledAgent); } - // TODO Dial Queue + log.warn("No correct dialplan found for called number [{}].", calledNumber); + return empty(); + } - return view(domain, "dialplan.xml"); + private FreeMarkerView agentCallAgent(final String domain, Agent callerAgent, Agent calledAgent) { + final FreeMarkerView view = view(domain, "local-extension.xml"); + final String connId = idSeq.get(); + attr(view, "connId", connId); + attr(view, "destinationUser", calledAgent.getAgent()); + attr(view, "callerAgent", callerAgent); + attr(view, "calledAgent", calledAgent); + attr(view, "recordingLoc", recordingFile(connId, + callerAgent.getAccount(), + calledAgent.getAccount())); + attr(view, "postRecUrl", localApiService.getPostRecUrl()); + return view; } } diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/ChannelAnswer.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/ChannelAnswer.java index b6127bc3..c78aa503 100644 --- a/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/ChannelAnswer.java +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/ChannelAnswer.java @@ -8,7 +8,7 @@ import org.springframework.stereotype.Component; 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.ChannelNameUtils; +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; @@ -39,13 +39,8 @@ public class ChannelAnswer { public void onChannelAnswer(final Event event) { log.debug("On channel answer event [{}] [{}].", event, event.getHeaders()); - // sofia/internal/700001@d1.wacai.info - // sofia/external/013764268709 - final String channelName = event.getHeader("variable_channel_name"); - log.info("On channel [{}] answer event.", channelName); - - final ChannelNameUtils.ChannelInfo channelInfo = - ChannelNameUtils.parse(channelName); + final CallStrUtils.ChannelInfo channelInfo = + CallStrUtils.getChannelInfo(event); if (channelInfo.isAgent()) { final Agent agent = findAgent(channelInfo); @@ -81,7 +76,7 @@ public class ChannelAnswer { } } - private Agent findAgent(final ChannelNameUtils.ChannelInfo channelInfo) { + private Agent findAgent(final CallStrUtils.ChannelInfo channelInfo) { return agentService.findByDomainAndAgent( channelInfo.getDomain(), channelInfo.getNumber()); } diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/ChannelCreate.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/ChannelCreate.java index 86508eea..46777922 100644 --- a/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/ChannelCreate.java +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/ChannelCreate.java @@ -2,7 +2,7 @@ package com.pudonghot.yo.fsagent.listener; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; -import com.pudonghot.yo.fsagent.util.ChannelNameUtils; +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; @@ -29,7 +29,7 @@ public class ChannelCreate { public void onChannelCreate(final Event event) { log.debug("On channel create event [{}] [{}].", event, event.getHeaders()); - final ChannelNameUtils.ChannelInfo channelInfo = ChannelNameUtils.get(event); + final CallStrUtils.ChannelInfo channelInfo = CallStrUtils.getChannelInfo(event); if (channelInfo.isAgent()) { agentStatusService.inACall(channelInfo.getDomain(), diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/ChannelDestroy.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/ChannelDestroy.java index 7740effc..cf5f76e1 100644 --- a/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/ChannelDestroy.java +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/ChannelDestroy.java @@ -9,14 +9,14 @@ import org.springframework.stereotype.Component; 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.ChannelNameUtils; +import com.pudonghot.yo.fsagent.util.CallStrUtils; import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; import org.freeswitch.esl.client.transport.event.Event; import com.pudonghot.yo.service.CommonAgentStatusService; import com.pudonghot.yo.service.CommonAgentEventQueueService; import org.springframework.beans.factory.annotation.Autowired; -import com.pudonghot.yo.fsagent.util.ChannelNameUtils.ChannelInfo; +import com.pudonghot.yo.fsagent.util.CallStrUtils.ChannelInfo; import static com.pudonghot.yo.model.agentevent.EventType.AgentOther_PhoneRelease; import static com.pudonghot.yo.model.agentevent.EventType.AgentEvent_Customer_Release; @@ -47,7 +47,7 @@ public class ChannelDestroy { final String presenceId = event.getHeader("variable_presence_id"); final String callUuid = event.getCallUuid(); log.info("On channel [{}][{}] destroy event.", presenceId, callUuid); - final ChannelInfo channelInfo = ChannelNameUtils.get(event); + final ChannelInfo channelInfo = CallStrUtils.getChannelInfo(event); if (channelInfo.isAgent()) { agentStatusService.acw(channelInfo.getDomain(), diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/ChannelHangupComplete.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/ChannelHangupComplete.java index 2397fedb..5a7b8980 100644 --- a/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/ChannelHangupComplete.java +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/ChannelHangupComplete.java @@ -8,7 +8,7 @@ import org.springframework.stereotype.Component; import com.pudonghot.yo.fsagent.util.EslEventUtils; import com.pudonghot.yo.fsagent.service.AgentService; import com.pudonghot.yo.model.agentevent.AgentEvent; -import com.pudonghot.yo.fsagent.util.ChannelNameUtils; +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; @@ -41,8 +41,8 @@ public class ChannelHangupComplete { if (!event.isHangupNormalClearing()) { - final ChannelNameUtils.ChannelInfo channelInfo = - ChannelNameUtils.get(event); + final CallStrUtils.ChannelInfo channelInfo = + CallStrUtils.getChannelInfo(event); if (channelInfo.isAgent()) { final Agent agent = agentService.findByDomainAndAgent( diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/ChannelProgress.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/ChannelProgress.java index cd718628..7c018a4d 100644 --- a/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/ChannelProgress.java +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/ChannelProgress.java @@ -8,7 +8,7 @@ import org.springframework.stereotype.Component; 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.ChannelNameUtils; +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; @@ -42,8 +42,8 @@ public class ChannelProgress { log.info("On channel [{}] answer event.", channelName); if (EslEventUtils.isCalled(event)) { - final ChannelNameUtils.ChannelInfo channelInfo = - ChannelNameUtils.get(event); + final CallStrUtils.ChannelInfo channelInfo = + CallStrUtils.getChannelInfo(event); if (channelInfo.isAgent()) { final Agent agent = agentService.findByDomainAndAgent( channelInfo.getDomain(), diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/GatewayService.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/GatewayService.java index c4e1da14..0405f61c 100644 --- a/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/GatewayService.java +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/GatewayService.java @@ -8,4 +8,20 @@ import com.wacai.tigon.service.BaseQueryService; * Dec 04, 2019 19:04:12 */ public interface GatewayService extends BaseQueryService { + + /** + * generate gateway name + * + * @param gateway gateway + * @return gateway name + */ + String genGatewayName(Gateway gateway); + + /** + * generate gateway name + * + * @param gatewayId gatewayId + * @return gateway name + */ + String genGatewayName(Integer gatewayId); } diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/TrunkService.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/TrunkService.java index b9802fd4..88fecf3f 100644 --- a/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/TrunkService.java +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/TrunkService.java @@ -27,4 +27,13 @@ public interface TrunkService extends BaseQueryService { * @return called number */ String calledNumber(Trunk trunk, String number); + + /** + * parse dial target + * + * @param tenantId tenant id + * @param calledNumber called number + * @return trunk and called number + */ + Pair parseDialTarget(Integer tenantId, String calledNumber); } diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/impl/GatewayerviceImpl.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/impl/GatewayerviceImpl.java index aae3be78..84dd9ab8 100644 --- a/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/impl/GatewayerviceImpl.java +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/impl/GatewayerviceImpl.java @@ -1,10 +1,11 @@ package com.pudonghot.yo.fsagent.service.impl; -import com.pudonghot.yo.fsagent.service.GatewayService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import com.pudonghot.yo.mapper.GatewayMapper; import com.pudonghot.yo.model.domain.Gateway; -import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import com.pudonghot.yo.fsagent.service.GatewayService; import com.wacai.tigon.service.support.BaseQueryServiceSupport; /** @@ -18,4 +19,20 @@ public class GatewayerviceImpl Gateway, GatewayMapper> implements GatewayService { + + /** + * {@inheritDoc} + */ + @Override + public String genGatewayName(final Gateway gateway) { + return genGatewayName(gateway.getId()); + } + + /** + * {@inheritDoc} + */ + @Override + public String genGatewayName(Integer gatewayId) { + return "GW" + StringUtils.leftPad(String.valueOf(gatewayId), 6, '0'); + } } diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/impl/TrunkServiceImpl.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/impl/TrunkServiceImpl.java index 1419257e..f0f0a443 100644 --- a/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/impl/TrunkServiceImpl.java +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/impl/TrunkServiceImpl.java @@ -2,14 +2,19 @@ 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 lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.util.Assert; import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.StringUtils; @@ -32,12 +37,18 @@ public class TrunkServiceImpl TrunkMapper> implements TrunkService { + @Value("${yo.fsagent.trunk-prefix.seq-name:TRUNK}") + private String seqName; + @Autowired + private SequenceMapper sequenceMapper; @Autowired private TrunkStrategyMapper trunkStrategyMapper; @Autowired private GatewayMapper gatewayMapper; @Autowired private CellphoneService cellphoneService; + @Autowired + private GatewayService gatewayService; /** * {@inheritDoc} @@ -70,8 +81,8 @@ public class TrunkServiceImpl Assert.state(gateway.getActive(), () -> "Trunk [" + trunk.getPrefix() + "] gateway is not active"); - return Pair.of(trunk, "sofia/gateway/GW" + - StringUtils.leftPad(String.valueOf(gateway.getId()), 6, '0') + + return Pair.of(trunk, "sofia/gateway/" + + gatewayService.genGatewayName(gateway) + "/" + calledNumber(trunk, number)); } @@ -100,6 +111,30 @@ public class TrunkServiceImpl return addPrefix(trunk, calledNumber); } + /** + * {@inheritDoc} + */ + @Override + public Pair parseDialTarget(final Integer tenantId, final String calledNumber) { + final Sequence trunkSeq = sequenceMapper.find( + new Search(Sequence.TENANT_ID, tenantId) + .eq(Sequence.NAME, seqName) + .eq(Sequence.ACTIVE, true)); + final int prefixLength = trunkSeq.getLength(); + if (calledNumber.length() > prefixLength) { + final String prefix = calledNumber.substring(0, prefixLength); + final Trunk trunk = mapper.find( + 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 null; + } + private String addPrefix(final Trunk trunk, String number) { final String gatewayPrefix = trunk.getGatewayPrefix(); return StringUtils.isNotBlank(gatewayPrefix) ? gatewayPrefix + number : number; diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/util/ChannelNameUtils.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/util/CallStrUtils.java similarity index 56% rename from fsagent/src/main/java/com/pudonghot/yo/fsagent/util/ChannelNameUtils.java rename to fsagent/src/main/java/com/pudonghot/yo/fsagent/util/CallStrUtils.java index 1a8efedd..b89cdedd 100644 --- a/fsagent/src/main/java/com/pudonghot/yo/fsagent/util/ChannelNameUtils.java +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/util/CallStrUtils.java @@ -12,20 +12,23 @@ import org.freeswitch.esl.client.transport.event.Event; * @author Donghuang * @date Jul 08, 2020 11:42:21 */ -public class ChannelNameUtils { +public class CallStrUtils { public static final Pattern PATTERN_USER_AND_DOMAIN = Pattern.compile("^sofia/internal/([^@]+)@(.+)$"); public static final Pattern PATTERN_EXTERNAL_NUMBER = Pattern.compile("^sofia/external/(\\w+)$"); + public static final Pattern PATTERN_DIAL_TARGET = + Pattern.compile("^(\\d+)\\.(A|Q|(?:AG))$"); + /** * get channel info * * @param event event * @return channel info */ - public static ChannelInfo get(final Event event) { - return parse(event.getChannelName()); + public static ChannelInfo getChannelInfo(final Event event) { + return parseChannelName(event.getChannelName()); } /** @@ -34,7 +37,7 @@ public class ChannelNameUtils { * @param channelName channel name * @return channel info */ - public static ChannelInfo parse(final String channelName) { + public static ChannelInfo parseChannelName(final String channelName) { Matcher m = PATTERN_USER_AND_DOMAIN.matcher(channelName); if (m.find()) { return new ChannelInfo(m.group(1), m.group(2), true); @@ -49,6 +52,18 @@ public class ChannelNameUtils { "Invalid channel name: " + channelName); } + public static DialTarget parseDialTarget(final String target) { + Matcher m = PATTERN_DIAL_TARGET.matcher(target); + if (m.find()) { + final String type = m.group(2); + return new DialTarget(Integer.parseInt(m.group(1)), + "A".equals(type) ? DialTarget.Type.AGENT : + "AG".equals(type) ? DialTarget.Type.AGENT_GROUP : + DialTarget.Type.QUEUE); + } + return null; + } + @Getter @Setter @ToString @@ -58,4 +73,19 @@ public class ChannelNameUtils { private final String domain; private final boolean agent; } + + @Getter + @Setter + @ToString + @RequiredArgsConstructor + public static class DialTarget { + private final Integer id; + private final Type type; + + public enum Type { + AGENT, + AGENT_GROUP, + QUEUE + } + } } diff --git a/fsagent/src/main/resources/templates/dialplan/trunk-outbound.xml b/fsagent/src/main/resources/templates/dialplan/trunk-outbound.xml new file mode 100644 index 00000000..778ac16e --- /dev/null +++ b/fsagent/src/main/resources/templates/dialplan/trunk-outbound.xml @@ -0,0 +1,39 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ diff --git a/fsagent/src/main/resources/templates/empty.xml b/fsagent/src/main/resources/templates/empty.xml index 9223eda3..8d0b0245 100644 --- a/fsagent/src/main/resources/templates/empty.xml +++ b/fsagent/src/main/resources/templates/empty.xml @@ -1,6 +1,6 @@ -
+
diff --git a/fsagent/src/test/java/com/pudonghot/yo/fsagent/ChannelNameUtilsTest.java b/fsagent/src/test/java/com/pudonghot/yo/fsagent/ChannelNameUtilsTest.java index 28fcefca..0de1fefb 100644 --- a/fsagent/src/test/java/com/pudonghot/yo/fsagent/ChannelNameUtilsTest.java +++ b/fsagent/src/test/java/com/pudonghot/yo/fsagent/ChannelNameUtilsTest.java @@ -2,7 +2,7 @@ package com.pudonghot.yo.fsagent; import org.junit.Test; import lombok.extern.slf4j.Slf4j; -import com.pudonghot.yo.fsagent.util.ChannelNameUtils; +import com.pudonghot.yo.fsagent.util.CallStrUtils; /** * @author Donghuang @@ -13,7 +13,7 @@ public class ChannelNameUtilsTest { @Test public void testParse() { - log.info("User and domain [{}]", ChannelNameUtils.parse("sofia/internal/700001@d1.wacai.info")); - log.info("External number [{}]", ChannelNameUtils.parse("sofia/external/013764268709")); + log.info("User and domain [{}]", CallStrUtils.parseChannelName("sofia/internal/700001@d1.wacai.info")); + log.info("External number [{}]", CallStrUtils.parseChannelName("sofia/external/013764268709")); } } diff --git a/lib/model/src/main/java/com/pudonghot/yo/model/domain/Sequence.java b/lib/model/src/main/java/com/pudonghot/yo/model/domain/Sequence.java index 4185a0ba..7c19bd74 100644 --- a/lib/model/src/main/java/com/pudonghot/yo/model/domain/Sequence.java +++ b/lib/model/src/main/java/com/pudonghot/yo/model/domain/Sequence.java @@ -22,6 +22,8 @@ public class Sequence extends BaseDomain { @NotUpdate private String name; @NotUpdate + private int length; + @NotUpdate private Long initVal; private Long step; private Long currentVal; diff --git a/openapi/src/main/java/com/pudonghot/yo/openapi/controller/AgentStatusController.java b/openapi/src/main/java/com/pudonghot/yo/openapi/controller/AgentStatusController.java index 5ec23b7b..2c019085 100644 --- a/openapi/src/main/java/com/pudonghot/yo/openapi/controller/AgentStatusController.java +++ b/openapi/src/main/java/com/pudonghot/yo/openapi/controller/AgentStatusController.java @@ -28,8 +28,7 @@ public class AgentStatusController implements SessionAbility { @Autowired private AgentEventService agentEventService; - @RequestMapping({"/", "/forcelogin" - }) + @RequestMapping({"/", "/forcelogin"}) public RespAgentReady signIn( @PathVariable("account") final String account) { diff --git a/web/cms/app/nav-items.js b/web/cms/app/nav-items.js index b4c7b2c4..b8071676 100644 --- a/web/cms/app/nav-items.js +++ b/web/cms/app/nav-items.js @@ -60,6 +60,12 @@ export default (function() { route: 'agent.list', icon: 'fa-user', text: '坐席管理' + }, + { + perm: 'PERM_VIEW_AGENT_STATUS_LIST', + route: 'agent-status.list', + icon: 'fa-user', + text: '坐席状态管理' } ] }, { @@ -70,13 +76,7 @@ export default (function() { route: 'campaign.list', icon: 'fa-fax', text: '外呼活动管理' - }, - { - perm: 'PERM_VIEW_CAMPAIGN_LIST', - route: 'calling-list.list', - icon: 'fa fa-list-alt', - text: '拨打列表' - }, + } ] }, { text: '公共', diff --git a/web/cms/app/router.js b/web/cms/app/router.js index 41c029d9..4757d1d9 100644 --- a/web/cms/app/router.js +++ b/web/cms/app/router.js @@ -97,25 +97,19 @@ Router.map(function() { this.route('edit', {path: '/:id/edit'}); }); - this.route('campaign-stat', function() { - this.route('list'); - }); - this.route('trunk-attr', function() { this.route('list'); this.route('create'); this.route('edit', {path: '/:id/edit'}); }); - this.route('calling-list', function() { - this.route('list'); - this.route('create'); - this.route('edit', {path: '/:id/edit'}); - }); - this.route('call-detail-record', function() { this.route('list'); }); + + this.route('agent-status', function() { + this.route('list'); + }); }); export default Router; diff --git a/web/cms/app/routes/agent-status/list.js b/web/cms/app/routes/agent-status/list.js new file mode 100644 index 00000000..503f6c37 --- /dev/null +++ b/web/cms/app/routes/agent-status/list.js @@ -0,0 +1,6 @@ +import BaseListRoute from './../base-list'; + +export default BaseListRoute.extend({ + perm: 'PERM_VIEW_AGENT_STATUS_LIST', + breadcrumbs: [{text: '坐席状态列表'}], +}); diff --git a/web/cms/app/routes/calling-list/create.js b/web/cms/app/routes/calling-list/create.js deleted file mode 100644 index c9497e02..00000000 --- a/web/cms/app/routes/calling-list/create.js +++ /dev/null @@ -1,34 +0,0 @@ -import BaseRoute from '../base'; -import RSVP from 'rsvp'; - -export default BaseRoute.extend({ - perm: 'PERM_VIEW_CAMPAIGN_CREATE', - breadcrumbs: [{route: 'calling-list.list', text: '拨打列表'}, {text: '创建拨打名单'}], - model() { - const store = this.get('store'); - return RSVP.hash({ - active: true, - privacy: true, - campaignList: store.ajaxGet('campaign/list'), - callingListStatus: store.ajaxGet('calling-list/status'), - timeList:[{value:'25200', text:'7:00'}, - {value:'28800', text:'8:00'}, - {value:'32400', text:'9:00'}, - {value:'36000', text:'10:00'}, - {value:'39600', text:'11:00'}, - {value:'43200', text:'12:00'}, - {value:'46800', text:'13:00'}, - {value:'50400', text:'14:00'}, - {value:'54000', text:'15:00'}, - {value:'57600', text:'16:00'}, - {value:'61200', text:'17:00'}, - {value:'64800', text:'18:00'}, - {value:'68400', text:'19:00'}, - {value:'72000', text:'20:00'}, - {value:'75600', text:'21:00'}, - {value:'79200', text:'22:00'}, - {value:'82800', text:'23:00'} - ] - }); - } -}); \ No newline at end of file diff --git a/web/cms/app/routes/calling-list/edit.js b/web/cms/app/routes/calling-list/edit.js deleted file mode 100644 index 5aadbd87..00000000 --- a/web/cms/app/routes/calling-list/edit.js +++ /dev/null @@ -1,28 +0,0 @@ -import BaseEditRoute from '../base-edit'; - -export default BaseEditRoute.extend({ - perm: 'PERM_VIEW_CAMPAIGN_EDIT', - afterModel(model) { - this.set('breadcrumbs', - [{route: 'calling-list.list', text: '拨打列表'}, - {text: '编辑拨打名单'}]); - model.timeList=[{value:'25200', text:'7:00'}, - {value:'28800', text:'8:00'}, - {value:'32400', text:'9:00'}, - {value:'36000', text:'10:00'}, - {value:'39600', text:'11:00'}, - {value:'43200', text:'12:00'}, - {value:'46800', text:'13:00'}, - {value:'50400', text:'14:00'}, - {value:'54000', text:'15:00'}, - {value:'57600', text:'16:00'}, - {value:'61200', text:'17:00'}, - {value:'64800', text:'18:00'}, - {value:'68400', text:'19:00'}, - {value:'72000', text:'20:00'}, - {value:'75600', text:'21:00'}, - {value:'79200', text:'22:00'}, - {value:'82800', text:'23:00'} - ] - } -}); \ No newline at end of file diff --git a/web/cms/app/routes/calling-list/list.js b/web/cms/app/routes/calling-list/list.js deleted file mode 100644 index 6ab730eb..00000000 --- a/web/cms/app/routes/calling-list/list.js +++ /dev/null @@ -1,6 +0,0 @@ -import BaseListRoute from './../base-list'; - -export default BaseListRoute.extend({ - perm: 'PERM_VIEW_CAMPAIGN_LIST', - breadcrumbs: [{text: '拨打列表'}] -}); \ No newline at end of file diff --git a/web/cms/app/routes/sequence/create.js b/web/cms/app/routes/sequence/create.js index a3b67a96..1a889d07 100644 --- a/web/cms/app/routes/sequence/create.js +++ b/web/cms/app/routes/sequence/create.js @@ -7,6 +7,7 @@ export default BaseRoute.extend({ return { active: true, initVal: 1, + length: 4, step: 1 }; } diff --git a/web/cms/app/services/nlp-component-group/service.js b/web/cms/app/services/nlp-component-group/service.js deleted file mode 100644 index 9b6a39d9..00000000 --- a/web/cms/app/services/nlp-component-group/service.js +++ /dev/null @@ -1,14 +0,0 @@ -import Service from '../service'; - -export default Service.extend({ - modelName: 'NlpComponentGroup', - constraints: { - name: { - presence: true, - length: { - minimum: 1, - maximum: 36 - } - } - } -}); diff --git a/web/cms/app/services/nlp-component/service.js b/web/cms/app/services/nlp-component/service.js deleted file mode 100644 index a2322b6d..00000000 --- a/web/cms/app/services/nlp-component/service.js +++ /dev/null @@ -1,32 +0,0 @@ -import Service from '../service'; - -export default Service.extend({ - modelName: 'NlpComponent', - createConstraints: { - groupId: { - presence: true - }, - type: { - presence: true - }, - name: { - presence: true, - length: { - minimum: 1, - maximum: 36 - } - } - }, - constraints: { - groupId: { - presence: true - }, - name: { - presence: true, - length: { - minimum: 1, - maximum: 36 - } - } - } -}); diff --git a/web/cms/app/services/nlp-node-sample/service.js b/web/cms/app/services/nlp-node-sample/service.js deleted file mode 100644 index 73cc09fa..00000000 --- a/web/cms/app/services/nlp-node-sample/service.js +++ /dev/null @@ -1,15 +0,0 @@ -import Service from '../service'; - -export default Service.extend({ - modelName: 'NlpNodeSample', - constraints: { - sampleText: { - presence: true, - } - }, - batchCreateConstraints: { - sampleTexts: { - presence: true - } - }, -}); diff --git a/web/cms/app/services/nlp-node/service.js b/web/cms/app/services/nlp-node/service.js deleted file mode 100644 index 886b2b91..00000000 --- a/web/cms/app/services/nlp-node/service.js +++ /dev/null @@ -1,25 +0,0 @@ -import Service from '../service'; - -export default Service.extend({ - modelName: 'NlpNode', - constraints: { - componentId: { - presence: true - }, - name: { - presence: true, - length: { - minimum: 1, - maximum: 36 - } - }, - nodeIndex: { - presence: true, - numericality: { - onlyInteger: true, - greaterThanOrEqualTo: 0, - lessThanOrEqualTo: 128 - } - } - } -}); diff --git a/web/cms/app/services/nlp-term-group/service.js b/web/cms/app/services/nlp-term-group/service.js deleted file mode 100644 index d6413f7b..00000000 --- a/web/cms/app/services/nlp-term-group/service.js +++ /dev/null @@ -1,14 +0,0 @@ -import Service from '../service'; - -export default Service.extend({ - modelName: 'NlpTermGroup', - constraints: { - name: { - presence: true, - length: { - minimum: 1, - maximum: 36 - } - } - } -}); diff --git a/web/cms/app/services/nlp-term/service.js b/web/cms/app/services/nlp-term/service.js deleted file mode 100644 index da85b06a..00000000 --- a/web/cms/app/services/nlp-term/service.js +++ /dev/null @@ -1,24 +0,0 @@ -import Service from '../service'; - -export default Service.extend({ - modelName: 'NlpTerm', - constraints: { - groupId: { - presence: true - }, - term: { - presence: true, - length: { - minimum: 1, - maximum: 36 - } - }, - pinyin: { - presence: true, - length: { - minimum: 1, - maximum: 128 - } - } - } -}); diff --git a/web/cms/app/services/sequence/service.js b/web/cms/app/services/sequence/service.js index d7c05174..85dd5b71 100644 --- a/web/cms/app/services/sequence/service.js +++ b/web/cms/app/services/sequence/service.js @@ -10,6 +10,14 @@ export default Service.extend({ maximum: 36 } }, + length: { + presence: true, + numericality: { + onlyInteger: true, + greaterThan: 1, + lessThanOrEqualTo: 16 + } + }, initVal: { presence: true, numericality: { @@ -26,14 +34,7 @@ export default Service.extend({ } } }, - createConstraints: { - name: { - presence: true, - length: { - minimum: 1, - maximum: 36 - } - }, + updateConstraints: { step: { presence: true, numericality: { diff --git a/web/cms/app/services/talk-component/service.js b/web/cms/app/services/talk-component/service.js deleted file mode 100644 index 59924aae..00000000 --- a/web/cms/app/services/talk-component/service.js +++ /dev/null @@ -1,53 +0,0 @@ -import BaseService from '../service'; - -export default BaseService.extend({ - modelName: 'TalkComponent', - constraints: { - note: { - presence: true - }, - content: { - presence: true - }, - }, - voiceConstraints: { - note: { - presence: true - }, - voice: { - presence: true - } - }, - ttsConstraints: { - text: { - presence: true - }, - ttsVoiceActor: { - presence: true - }, - note: { - presence: true - }, - volume: { - presence: true, - numericality: { - notLessThan: -500, - notGreaterThan: 500 - } - }, - speechRate: { - presence: true, - numericality: { - notLessThan: -500, - notGreaterThan: 500 - } - }, - pitchRate: { - presence: true, - numericality: { - notLessThan: -500, - notGreaterThan: 500 - } - } - } -}); diff --git a/web/cms/app/services/talk-group/service.js b/web/cms/app/services/talk-group/service.js deleted file mode 100644 index fae6a2f3..00000000 --- a/web/cms/app/services/talk-group/service.js +++ /dev/null @@ -1,14 +0,0 @@ -import Service from '../service'; - -export default Service.extend({ - modelName: 'TalkGroup', - constraints: { - name: { - presence: true, - length: { - minimum: 1, - maximum: 36 - } - } - } -}); diff --git a/web/cms/app/services/talk/service.js b/web/cms/app/services/talk/service.js deleted file mode 100644 index 63aea515..00000000 --- a/web/cms/app/services/talk/service.js +++ /dev/null @@ -1,57 +0,0 @@ -import BaseService from '../service'; - -export default BaseService.extend({ - modelName: 'Talk', - normalConstraints: { - talk: { - presence: true - } - }, - audioConstraints: { - overwriteStrategy: { - presence: true - }, - files: { - presence: true - } - }, - ttsConstraints: { - talk: { - presence: true - }, - ttsVoiceActor: { - presence: true - }, - volume: { - presence: true, - numericality: { - notLessThan: -500, - notGreaterThan: 500 - } - }, - speechRate: { - presence: true, - numericality: { - notLessThan: -500, - notGreaterThan: 500 - } - }, - pitchRate: { - presence: true, - numericality: { - notLessThan: -500, - notGreaterThan: 500 - } - } - }, - updateConstraints: { - talk: { - presence: true - } - }, - importConstraints: { - archive: { - presence: true - } - } -}); diff --git a/web/cms/app/services/voice-actor/service.js b/web/cms/app/services/voice-actor/service.js deleted file mode 100644 index 769514f6..00000000 --- a/web/cms/app/services/voice-actor/service.js +++ /dev/null @@ -1,21 +0,0 @@ -import Service from '../service'; - -export default Service.extend({ - modelName: 'VoiceActor', - constraints: { - name: { - presence: true, - length: { - minimum: 1, - maximum: 36 - } - }, - code: { - presence: true, - length: { - minimum: 1, - maximum: 36 - } - } - } -}); diff --git a/web/cms/app/templates/agent-status/list.hbs b/web/cms/app/templates/agent-status/list.hbs new file mode 100644 index 00000000..03459fa1 --- /dev/null +++ b/web/cms/app/templates/agent-status/list.hbs @@ -0,0 +1,88 @@ +
+ {{grid-header search-box=false}} + +
+ +
+ + + + + + + + + + + + + + {{#each model.data as |it|}} + + + + + + + + + + {{/each}} + +
+ 账户 + + 分机 + + {{th-filter name='registerd' + text='注册状态' + options=(array + (hash value=true text='在线') + (hash value=false text='离线') + ) + }} + + {{th-filter name='status' + text='坐席状态' + options=(array + (hash value='READY' text='就绪') + (hash value='NOT_READY' text='未就绪') + (hash value='OFFLINE' text='离线') + ) + }} + + {{th-filter name='state' + text='话机状态' + options=(array + (hash value='IDLE' text='空闲') + (hash value='IN_A_CALL' text='通话中') + (hash value='ACW' text='话后事务') + ) + }} + + 通话ID + + 事件Key +
+ {{it.account}} + + {{it.agent}} + + {{status-cell model=it + field='registered' + enabled-text='在线' + disabled-text='离线'}} + + {{it.status}} + + {{it.state}} + + {{it.callUuid}} + + {{it.eventKey}} +
+
+ {{pagination-bar}} +
+
+{{outlet}} diff --git a/web/cms/app/templates/calling-list/create.hbs b/web/cms/app/templates/calling-list/create.hbs deleted file mode 100644 index 7139e652..00000000 --- a/web/cms/app/templates/calling-list/create.hbs +++ /dev/null @@ -1,41 +0,0 @@ -{{#form-content}} -
- {{form-input-select - name='campaignId' - label='外呼活动' - options=model.campaignList.data - value-field='id' - text-field='name' - enabled-field='active' - }} - {{form-input name='phone' label='号码'}} - {{form-input-select - name='status' - label='状态' - options=model.callingListStatus - value-field='value' - text-field='label' - }} - {{form-input-select - name='dailyFrom' - label='开始时间' - options=model.timeList - value-field='value' - text-field='text' - }} - {{form-input-select - name='dailyTo' - label='结束时间' - options=model.timeList - value-field='value' - text-field='text' - }} - {{form-input name='taskKey' label='任务KEY'}} - {{form-input name='recordKey' label='记录KEY'}} - {{form-input type='textarea' name='recordData' label='记录数据' placeholder='请在此输入业务数据,例如:{"username": "东皇", "gender": "MALE"}'}} - {{form-input name='attachedData' label='随路数据'}} - {{form-input name='note' label='备注'}} -
- {{form-footer-buttons type='create'}} -{{/form-content}} -{{outlet}} diff --git a/web/cms/app/templates/calling-list/edit.hbs b/web/cms/app/templates/calling-list/edit.hbs deleted file mode 100644 index 2894e593..00000000 --- a/web/cms/app/templates/calling-list/edit.hbs +++ /dev/null @@ -1,60 +0,0 @@ -{{#form-content}} -
- {{form-input type='hidden' name='id'}} - {{form-input-select - name='campaignId' - label='外呼活动' - options=model.campaignList - value-field='id' - text-field='name' - enabled-field='active' - }} - {{form-input name='phone' label='号码'}} - {{form-input-select - name='status' - label='状态' - options=model.callingListStatus - value-field='value' - text-field='label' - }} - {{form-input-select - name='dailyFrom' - label='开始时间' - options=model.timeList - value-field='value' - text-field='text' - }} - {{form-input-select - name='dailyTo' - label='结束时间' - options=model.timeList - value-field='value' - text-field='text' - }} - {{form-input name='callUuid' label='通话ID'}} - {{form-input-datetimepicker - labelClass='col-sm-3 col-md-3' - inputClass='col-sm-5 col-md-5' - name='callStartTime' - label='通话开始时间'}} - {{form-input-datetimepicker - labelClass='col-sm-3 col-md-3' - inputClass='col-sm-5 col-md-5' - name='callEstablishedTime' - label='通话接通时间'}} - {{form-input-datetimepicker - labelClass='col-sm-3 col-md-3' - inputClass='col-sm-5 col-md-5' - name='callEndTime' - label='通话结束时间'}} - {{form-input name='callResult' label='通话结果'}} - {{form-input name='taskKey' label='任务KEY'}} - {{form-input name='recordKey' label='记录KEY'}} - {{form-input type='textarea' name='recordData' label='记录数据' placeholder='请在此输入业务数据,例如:{"username": "东皇", "gender": "MALE"}'}} - {{form-input name='attachedData' label='随路数据'}} - {{form-input-enabled}} - {{form-input name='note' label='备注'}} -
- {{form-footer-buttons type='update'}} -{{/form-content}} -{{outlet}} diff --git a/web/cms/app/templates/calling-list/list.hbs b/web/cms/app/templates/calling-list/list.hbs deleted file mode 100644 index 9247ed81..00000000 --- a/web/cms/app/templates/calling-list/list.hbs +++ /dev/null @@ -1,137 +0,0 @@ -
- {{#grid-header}} - {{#has-perm 'PERM_VIEW_CAMPAIGN_CREATE'}} -
  • - {{#link-to 'calling-list.create'}} - - 新建名单 - {{/link-to}} -
  • - {{/has-perm}} - {{/grid-header}} - -
    - -
    - - - - - - - - - - - - - - - - - - - - - {{#each model.data as |it|}} - - - - - - - - - - - - - - - - - {{/each}} - -
    - {{th-filter name='campaignId' - text='外呼活动' - options=model.campaignList - value-field='id' - text-field='name' - }} - - 号码 - - {{th-filter name='status' - text='状态' - options=model.callingListStatus - value-field='value' - text-field='label' - }} - - 开始时间 - - 结束时间 - - 通话ID - - 拨打开始时间 - - 拨打接通时间 - - 拨打结束时间 - - 拨打结果 - - 任务KEY - - 记录KEY - - 创建时间 - - - 管理 -
    - {{it.campaign.name}} - - {{it.phone}} - - {{it.status}} - - {{second-time timeInSecond=it.dailyFrom}} - - {{second-time timeInSecond=it.dailyTo}} - - {{it.callUuid}} - - {{date-cell value=it.callStartTime format='YYYY-MM-DD H:mm:ss'}} - - {{date-cell value=it.callEstablishedTime format='YYYY-MM-DD H:mm:ss'}} - - {{date-cell value=it.callEndTime format='YYYY-MM-DD H:mm:ss'}} - - {{it.callResult}} - - {{it.taskKey}} - - {{it.recordKey}} - - {{date-cell value=it.createdTime format='YYYY-MM-DD H:mm:ss'}} - -
    - {{#has-perm 'PERM_VIEW_CAMPAIGN_EDIT'}} - {{status-toggle-button model=it}} - {{edit-btn route-name='calling-list.edit' model-id=it.id perm='PERM_VIEW_CAMPAIGN_EDIT'}} - {{/has-perm}} - {{#has-perm 'PERM_VIEW_CAMPAIGN_DELETE'}} - {{#unless it.active}} - {{delete-button model=it}} - {{/unless}} - {{/has-perm}} -
    -
    -
    - {{pagination-bar}} -
    -
    -{{outlet}} diff --git a/web/cms/app/templates/campaign/list.hbs b/web/cms/app/templates/campaign/list.hbs index 21ef6dd7..be143f14 100644 --- a/web/cms/app/templates/campaign/list.hbs +++ b/web/cms/app/templates/campaign/list.hbs @@ -140,13 +140,6 @@ {{delete-button model=it}} {{/unless}} {{/has-perm}} - {{#link-to 'calling-list.list' - (query-params filters=(concat '{"campaignId":[' it.id ']}')) - class='btn btn-xs btn-info' - data-rel='tooltip' - title='拨打列表'}} - - {{/link-to}} diff --git a/web/cms/app/templates/sequence/create.hbs b/web/cms/app/templates/sequence/create.hbs index e21ea092..76d27537 100644 --- a/web/cms/app/templates/sequence/create.hbs +++ b/web/cms/app/templates/sequence/create.hbs @@ -1,6 +1,7 @@ {{#form-content}}
    {{form-input name='name' label='名称'}} + {{form-input name='length' label='长度'}} {{form-input name='initVal' label='初始值'}} {{form-input name='step' label='递增值'}} diff --git a/web/cms/app/templates/sequence/edit.hbs b/web/cms/app/templates/sequence/edit.hbs index 917f5788..16d7b7cf 100644 --- a/web/cms/app/templates/sequence/edit.hbs +++ b/web/cms/app/templates/sequence/edit.hbs @@ -1,6 +1,7 @@ {{#form-content}} {{form-input type='hidden' name='id'}} - {{form-input name='name' label='名称'}} + {{form-input name='name' label='名称' readonly=true}} + {{form-input name='length' label='长度' readonly=true}} {{form-input name='initVal' label='初始值' readonly=true}} {{form-input name='step' label='递增值'}} diff --git a/web/cms/app/templates/sequence/list.hbs b/web/cms/app/templates/sequence/list.hbs index 9269c6ca..0c9cf16b 100644 --- a/web/cms/app/templates/sequence/list.hbs +++ b/web/cms/app/templates/sequence/list.hbs @@ -19,6 +19,9 @@ 名称 + + 长度 + 当前值 @@ -53,6 +56,9 @@ {{it.name}} + + {{it.length}} + {{it.currentVal}} diff --git a/web/cms/tests/unit/routes/agent-status/list-test.js b/web/cms/tests/unit/routes/agent-status/list-test.js new file mode 100644 index 00000000..1605e303 --- /dev/null +++ b/web/cms/tests/unit/routes/agent-status/list-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | agent-status/list', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:agent-status/list'); + assert.ok(route); + }); +});