diff --git a/README.md b/README.md new file mode 100644 index 00000000..c81ce3ae --- /dev/null +++ b/README.md @@ -0,0 +1,309 @@ +# Corona + +### IDEA 环境需求 + +由于项目依赖`org.projectlombok:lombok:1.18.2`,而`Lombok`在`1.18.4`版本做了[重设计](https://projectlombok.org/features/experimental/FieldNameConstants),不兼容之前版本,因此`IDEA`的`Lombok`插件必须是版本`lombok-plugin-0.23`,这里比较坑,如果升级到版本`1.18.4`以后,插件还是得升级,而且很多API都要改。 + +插件下载地址:[https://pan.caimi-inc.com/d/b038610394/](https://pan.caimi-inc.com/d/b038610394/) + +下载对应版本插件,比如`IDEA 2018.2.x`下载`lombok-plugin-0.23-2018.2.zip`,卸载`IDEA`已安装`Lombok`插件,选择磁盘安装,如下图: +![安装Lombok插件](doc/images/install-lombok-plugin.png) + +### 启动SpringBoot项目 + +建议先执行mvn编译命令,比如编译模块`CMS` + +```bash +mvn clean compile -am -pl cms -T 2C +``` + +### Tigon + +#### Controller + +如果加注了`@ResponseBody`或者`@RestController`,不做变动,依循`Spring MVC`方式`JSON`序列化返回数据,否则返回`Wacai`接口规范数据格式,如下例: + +#### @ResponseBody + +```java +@ResponseBody +@RequestMapping("/resp-body") +public String respBody() { + return "Response Body"; +} +``` + +响应输出 + +``` +Response Body +``` + +#### Wacai JSON API + +```java +@RequestMapping("/resp-body") +public String respBody() { + return "Response Body"; +} +``` + +响应输出 + +``` +{ + "code": 0, + "success": true, + "data": "Response Body" +} +``` + +简单来说就是,不加`@ResponseBody`注解,默认会以`Wacai`API规范响应数据结果。 + +参见以下样例: + +I. + +```java +@RequestMapping("/count") +public int count() { + return 1024; +} +``` + +响应输出 + +``` +{ + "code": 0, + "success": true, + "data": 1024 +} +``` + +II. + +```java +@RequestMapping("/user-info") +public Map userInfo() { + final Map user = new HashMap<>(4); + user.put("name", "Uncle Donghuang"); + user.put("gender", "MALE"); + user.put("mobile", "17161787481"); + user.put("active", true); + return user; +} +``` + +响应输出 + +``` +{ + "code": 0, + "success": true, + "data": { + "name": "Uncle Donghuang", + "gender": "MALE", + "mobile": "17161787481", + "active": true, + } +} +``` + +III. + +```java +@RequestMapping("/error-caused") +public Map errorCaused() { + throw new RuntimeException("Oops, some error caused"); +} +``` + +响应输出 + +``` +{ + "code": 5000, + "success": false, + "message": "Oops, some error caused" +} +``` + +#### MyBatis + +`MyBatis`做了启动期增强,实例`Mapper`继承了`BaseMapper`之后,会继承获得相关的增删改查等方法,如下例: + +##### Model + +```java +import lombok.Getter; +import lombok.Setter; +import java.util.Date; +import com.wacai.tigon.model.M0; +import com.wacai.tigon.mybatis.Table; +import com.wacai.tigon.mybatis.NotUpdate; +import lombok.experimental.FieldNameConstants; + +@Getter +@Setter +@Table("yo_customer") +@FieldNameConstants(prefix = "") +public class Customer extends M0 { + private String name; + private String account; + private String password; + private String note; + @NotUpdate + protected Date createdTime; + protected Date updatedTime; + @NotUpdate + protected String createdBy; + protected String updatedBy; + protected boolean active; +} +``` + +##### Mapper + +```java +import com.wacai.tigon.mybatis.BaseMapper; + +public interface CustomerMapper extends BaseMapper { +} +``` + +##### Mapper.xml +```xml + + + + +``` + +##### Usage + +```java +@Autowired +private CustomerMapper customerMapper; +``` + +I. 插入 + +```java +final Customer customer = new Customer(); +customer.setName("Uncle Donghuang"); +customer.setAccount("donghuang"); +customer.setPassword("qR$#FzM!z9W*"); +customer.setCreatedBy("SYS"); +customer.setCreatedTime(new Date()); +customer.setActive(true); +customerMapper.insert(customer); +``` + +II. 查询 + +Find by ID + +```java +final Integer id = 1154; +final Customer customer = customerMapper.find(id); +``` + +Find by `Search` + +```java +final Customer customer = customerMapper.find( + new Search(Customer.ACCOUNT, "donghuang") + .eq(Customer.ACTIVE, true)); +``` + +List by `Search` + +```java +final List customers = customerMapper.list( + new Search(Customer.ACTIVE, true) + .between(Customer.ID, 1, 1154) + .asc(Customer.ACCOUNT) + .limit(42)); +``` + +`Search` API + +- `and` And another `Search` +- `asc` Order ASC +- `between` Between two values +- `contains` Value contains string +- `desc` Order DSC +- `endsWith` Value ends with string +- `eq` Eqauls +- `gt` Greater than +- `gte` Eqauls or greater than +- `in` In values +- `isNull` Value is null +- `like` Value like +- `limit` Return rows limit +- `lt` Less than +- `lte` Eqauls or less than +- `ne` Not equals +- `notIn` Not in values +- `notNull` Value is not null +- `offset` Return rows offset +- `or` Or another `Search` +- `orderBy` Order by +- `startsWith` Value starts with string + +III. 更新 + +Update model + +```java +Customer customer = customerMapper.find( + new Search(Customer.ACCOUNT, "donghuang") + .eq(Customer.ACTIVE, true)); + +customer.setPassword("g!5KpWdXEB!^"); +customer.setUpdatedBy("SYS"); +customer.setUpdatedTime(new Date()); +customerMapper.update(customer); +``` + +Update with map + +```java +final Map update = new HashMap<>(4); +update.put(Customer.PASSWORD, "g!5KpWdXEB!^"); +update.put(Customer.UPDATED_BY, "SYS"); +update.put(Customer.UPDATED_TIME, new Date()); + +customerMapper.update(update, 1154); +// OR +// customerMapper.update(update, new Search(Customer.ID, 1154)); +``` + +Set null + +```java +// Update note to null of customer 1154 +customerMapper.setNull(Customer.NOTE, 1154); +// Update note to null of customer 1154 +customerMapper.setNull(Customer.NOTE, new Search(Customer.ID, 1154)); +// Update note to null of all +customerMapper.setNull(Customer.NOTE, new Search()); + +``` + +IV. 删除 + +Delete by ID + +```java +customerMapper.delete(1154); +``` + +Delete by `Search` + +```java +customerMapper.delete(new Search(Customer.ID, 1154)); +``` + diff --git a/cms/README.md b/cms/README.md new file mode 100644 index 00000000..661ff0d1 --- /dev/null +++ b/cms/README.md @@ -0,0 +1 @@ +# Yo BR CMS 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 new file mode 100644 index 00000000..2a36f067 Binary files /dev/null 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/pom.xml b/cms/pom.xml new file mode 100644 index 00000000..3791ae10 --- /dev/null +++ b/cms/pom.xml @@ -0,0 +1,127 @@ + + + 4.0.0 + yo-cms + 0.0.1-RELEASE + Yo CMS + Yo CMS + jar + + + com.pudonghot.yo + yo + 0.0.1-SNAPSHOT + ../ + + + + com.pudonghot.yo.cms.YoCMS + + + + + com.pudonghot.yo + yo-cms-web + 0.0.1-SNAPSHOT + system + ${project.basedir}/local-repo/com/pudonghot/yo/yo-cms-web/0.0.1-SNAPSHOT/yo-cms-web-0.0.1-SNAPSHOT.jar + + + com.pudonghot.yo + yo-openapi-dto + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + io.github.openfeign + feign-httpclient + + + io.github.openfeign + feign-jackson + + + com.pudonghot.yo + yo-mapper + + + com.pudonghot.yo + yo-redis-raw + + + com.pudonghot.yo + yo-util + + + com.pudonghot.yo + yo-cellphone-location + + + com.pudonghot.yo + yo-http-client-apache + + + com.wacai.tigon + tigon-shiro-cas + + + com.pudonghot.yo + yo-shiro-cache + + + com.wacai.tigon + tigon-common + + + com.wacai.tigon + tigon-service-support + + + com.pudonghot.yo + yo-web-common + + + com.wacai.tigon + tigon-web-controller + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.projectlombok + lombok + provided + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + false + true + + + + pl.project13.maven + git-commit-id-plugin + + + + diff --git a/cms/src/main/java/com/pudonghot/yo/cms/YoCMS.java b/cms/src/main/java/com/pudonghot/yo/cms/YoCMS.java new file mode 100644 index 00000000..091ebf87 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/YoCMS.java @@ -0,0 +1,51 @@ +package com.pudonghot.yo.cms; + +import lombok.extern.slf4j.Slf4j; +import java.util.concurrent.TimeUnit; +import org.springframework.util.StringUtils; +import org.springframework.http.CacheControl; +import org.springframework.boot.SpringApplication; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; + +/** + * @author Donghuang
+ * Oct 26, 2019 15:56:08 + */ +@Slf4j +@EnableFeignClients +@SpringBootApplication +public class YoCMS implements WebMvcConfigurer { + + @Value("${site.context-path:}") + private String siteContextPath; + + /** + * main + * @param args args + */ + public static void main(final String[] args) { + SpringApplication.run(YoCMS.class, args); + } + + /** + * {@inheritDoc} + */ + @Override + public void addResourceHandlers(final ResourceHandlerRegistry registry) { + // Assets + final String assetsPattern = "/assets/**"; + final String resourceLocation = "classpath:/yo-cms/assets/"; + log.info("Add resource handler [{}] -> [{}].", assetsPattern, resourceLocation); + + registry.addResourceHandler( + StringUtils.hasText(siteContextPath) ? + siteContextPath + assetsPattern : + assetsPattern) + .addResourceLocations(resourceLocation) + .setCacheControl(CacheControl.maxAge(42, TimeUnit.DAYS)); + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/annotation/Taggable.java b/cms/src/main/java/com/pudonghot/yo/cms/annotation/Taggable.java new file mode 100644 index 00000000..6befcd5b --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/annotation/Taggable.java @@ -0,0 +1,15 @@ +package com.pudonghot.yo.cms.annotation; + +import java.lang.annotation.Target; +import java.lang.annotation.Retention; +import java.lang.annotation.ElementType; +import java.lang.annotation.RetentionPolicy; + +/** + * @author Donghuang
+ * Nov 17, 2019 16:51:56 + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Taggable { +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/annotation/TenantResource.java b/cms/src/main/java/com/pudonghot/yo/cms/annotation/TenantResource.java new file mode 100644 index 00000000..66335b3d --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/annotation/TenantResource.java @@ -0,0 +1,22 @@ +package com.pudonghot.yo.cms.annotation; + +import java.lang.annotation.Target; +import java.lang.annotation.Retention; +import java.lang.annotation.ElementType; +import java.lang.annotation.RetentionPolicy; + +/** + * Mark resource is part of tenant + * @author Donghuang
+ * Nov 04, 2019 23:06:46 + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface TenantResource { + + /** + * search attr + * @return true if set search attr + */ + boolean searchAttr() default false; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/auth/AuthRequestFilter.java b/cms/src/main/java/com/pudonghot/yo/cms/auth/AuthRequestFilter.java new file mode 100644 index 00000000..526fb897 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/auth/AuthRequestFilter.java @@ -0,0 +1,27 @@ +package com.pudonghot.yo.cms.auth; + +import javax.servlet.*; +import java.io.IOException; +import lombok.extern.slf4j.Slf4j; +import javax.servlet.http.HttpServletRequest; +import org.springframework.stereotype.Component; +import org.springframework.core.annotation.Order; + +/** + * @author Donghuang
+ * Nov 17, 2019 16:30:41 + */ +@Order +@Slf4j +@Component +public class AuthRequestFilter implements Filter { + + /** + * {@inheritDoc} + */ + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + log.debug("Wrap HTTP servlet request as AUTH request."); + filterChain.doFilter(new AuthRequestWrapper((HttpServletRequest) servletRequest), servletResponse); + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/auth/AuthRequestWrapper.java b/cms/src/main/java/com/pudonghot/yo/cms/auth/AuthRequestWrapper.java new file mode 100644 index 00000000..303102ba --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/auth/AuthRequestWrapper.java @@ -0,0 +1,68 @@ +package com.pudonghot.yo.cms.auth; + +import java.util.*; +import java.util.function.Function; +import javax.servlet.http.HttpServletRequest; + +import com.pudonghot.yo.cms.form.BasicForm; + +import javax.servlet.http.HttpServletRequestWrapper; +import com.pudonghot.yo.model.domain.BaseDomain; + +/** + * @author Donghuang
+ * Nov 17, 2019 16:05:39 + */ +public class AuthRequestWrapper extends HttpServletRequestWrapper implements SessionAbility { + private static Map> AUTH_ELS = new HashMap<>(); + static { + AUTH_ELS.put(BasicForm.TENANT_ID, SessionAbility::getTenantId); + AUTH_ELS.put(BasicForm.TENANT_CODE, SessionAbility::getTenantCode); + AUTH_ELS.put(BasicForm.AUTH_USER, SessionAbility::getUserAccount); + AUTH_ELS.put(BaseDomain.CREATED_BY, SessionAbility::getUserAccount); + AUTH_ELS.put(BaseDomain.UPDATED_BY, SessionAbility::getUserAccount); + } + + /** + * {@inheritDoc} + */ + public AuthRequestWrapper(final HttpServletRequest request) { + super(request); + } + + /** + * {@inheritDoc} + */ + @Override + public Enumeration getParameterNames() { + final Enumeration paramNames = super.getParameterNames(); + + if (isAuthenticated()) { + final List params = Collections.list(paramNames); + params.addAll(AUTH_ELS.keySet()); + return Collections.enumeration(params); + } + return paramNames; + } + + /** + * {@inheritDoc} + */ + @Override + public String[] getParameterValues(final String name) { + final Function getter = AUTH_ELS.get(name); + return getter != null ? new String[] { getParam(getter) } : super.getParameterValues(name); + } + + /** + * get params + * @param getter getter + * @return param string or null + */ + private String getParam(final Function getter) { + final Object param = getter.apply(this); + return param != null ? + (param instanceof String ? + (String) param : String.valueOf(param)) : null; + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/auth/SessionAbility.java b/cms/src/main/java/com/pudonghot/yo/cms/auth/SessionAbility.java new file mode 100644 index 00000000..6ca87c52 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/auth/SessionAbility.java @@ -0,0 +1,108 @@ +package com.pudonghot.yo.cms.auth; + +import java.util.Set; +import com.wacai.tigon.mybatis.Search; +import org.apache.shiro.SecurityUtils; +import org.springframework.util.Assert; +import io.buji.pac4j.subject.Pac4jPrincipal; +import org.apache.commons.lang3.StringUtils; +import com.pudonghot.yo.model.ValueTextModel; +import com.pudonghot.yo.model.domain.Tenant; +import com.pudonghot.yo.cms.service.TenantService; +import org.springframework.validation.annotation.Validated; + +/** + * @author Donghuang
+ * Sep 11, 2019 17:03:44 + */ +@Validated +public interface SessionAbility { + + /** + * get tenant id + * @return tenant code + */ + default Integer getTenantId() { + return getTenant(getUserAccount()).getId(); + } + + /** + * get tenant code + * @return tenant code + */ + default String getTenantCode() { + return getTenant(getUserAccount()).getCode(); + } + + /** + * get current user tenant + * @return current user tenant + */ + default Tenant getTenant() { + return getTenant(getUserAccount()); + } + + /** + * get account tenant + * @param account account + * @return tenant of account + */ + default Tenant getTenant(final String account) { + final TenantService tenantService = + SessionServiceHolder.getTenantService(); + final ValueTextModel tenantVt = + tenantService.current(account); + Assert.state(tenantVt != null, "没有选择租户"); + final Tenant tenant = tenantService.find( + new Search(Tenant.CODE, tenantVt.getValue()) + .eq(Tenant.ACTIVE, true)); + Assert.state(tenant != null, "无效租户"); + return tenant; + } + + /** + * get auth user account + * @return user account + */ + default String getUserAccount() { + final Pac4jPrincipal principal = + (Pac4jPrincipal) SecurityUtils.getSubject().getPrincipal(); + // extract email account + return principal.getProfile().getId().replaceAll("@.+$", ""); + } + + /** + * return current auth user permissions + */ + default Set getPermissions() { + final Pac4jPrincipal principal = + (Pac4jPrincipal) SecurityUtils.getSubject().getPrincipal(); + return principal.getProfile().getPermissions(); + } + + /** + * return current auth user roles + * @return current auth user roles + */ + default Set getRoles() { + final Pac4jPrincipal principal = + (Pac4jPrincipal) SecurityUtils.getSubject().getPrincipal(); + return principal.getProfile().getRoles(); + } + + /** + * login user name + * @return login user name + */ + default String getUsername() { + return StringUtils.capitalize(getUserAccount()); + } + + /** + * return true if user is authenticated + * @return true if user is authenticated + */ + default boolean isAuthenticated() { + return SecurityUtils.getSubject().isAuthenticated(); + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/auth/SessionServiceHolder.java b/cms/src/main/java/com/pudonghot/yo/cms/auth/SessionServiceHolder.java new file mode 100644 index 00000000..863d7158 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/auth/SessionServiceHolder.java @@ -0,0 +1,33 @@ +package com.pudonghot.yo.cms.auth; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import com.pudonghot.yo.cms.service.TenantService; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * @author Donghuang
+ * Sep 11, 2019 16:57:24 + */ +@Getter +@Component +@RequiredArgsConstructor(onConstructor_ = {@Autowired}) +public class SessionServiceHolder implements InitializingBean { + private static SessionServiceHolder holder; + + private final TenantService tenantService; + + /** + * {@inheritDoc} + */ + @Override + public void afterPropertiesSet() { + holder = this; + } + + public static TenantService getTenantService() { + return holder.tenantService; + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/controller/AgentController.java b/cms/src/main/java/com/pudonghot/yo/cms/controller/AgentController.java new file mode 100644 index 00000000..f7a064c8 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/controller/AgentController.java @@ -0,0 +1,79 @@ +package com.pudonghot.yo.cms.controller; + +import com.pudonghot.yo.cms.auth.SessionAbility; +import com.pudonghot.yo.cms.form.create.CreateFormAgent; +import com.pudonghot.yo.cms.form.update.UpdateFormAgent; +import com.pudonghot.yo.cms.service.AgentGroupService; +import com.wacai.tigon.form.FormList; +import com.wacai.tigon.mybatis.Search; +import com.wacai.tigon.model.ViewModel; +import com.pudonghot.yo.model.domain.Queue; +import com.pudonghot.yo.model.domain.Agent; +import com.wacai.tigon.web.controller.ArgQuery; +import com.wacai.tigon.web.annotation.ListApi; +import org.springframework.stereotype.Controller; +import com.pudonghot.yo.model.domain.AgentGroup; +import com.wacai.tigon.web.annotation.FilterCol; +import com.pudonghot.yo.cms.annotation.Taggable; +import com.pudonghot.yo.cms.service.QueueService; +import com.wacai.tigon.web.controller.BaseCrudController; +import com.pudonghot.yo.cms.annotation.TenantResource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + * @author Donghuang
+ * Nov 02, 2019 13:46:16 + */ +@Controller +@ListApi(searchCols = { + Agent.NAME, + Agent.ACCOUNT, + Agent.AGENT, + Agent.NOTE +}, +filterCols = { + @FilterCol(param = Agent.ACTIVE, type = boolean.class), + @FilterCol(param = Agent.GROUP_ID, type = Integer.class), + @FilterCol(param = Agent.TYPE, type = Agent.Type.class), + @FilterCol(param = Agent.WEBRTC, type = boolean.class), + @FilterCol(param = Agent.TAGS, type = Integer.class, searchAttr = true), + @FilterCol(param = Agent.QUEUES, type = Integer.class, searchAttr = true) +}) +@Taggable +@TenantResource +@RequestMapping("/agent") +public class AgentController + extends BaseCrudController implements SessionAbility { + @Autowired + private AgentGroupService agentGroupService; + @Autowired + private QueueService queueService; + + /** + * {@inheritDoc} + */ + @Override + protected void before(final ArgQuery arg) { + super.before(arg); + arg.getSearch().attr("withQueues", true); + } + + /** + * {@inheritDoc} + */ + @Override + protected void after(final ArgQuery arg) { + super.after(arg); + final ViewModel model = arg.getResult(); + final Integer tenantId = getTenantId(); + model.setAttr("groupsList", agentGroupService.list( + new Search(AgentGroup.TENANT_ID, tenantId))); + model.setAttr("queuesList", queueService.list( + new Search(Queue.TENANT_ID, tenantId))); + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/controller/AgentGroupController.java b/cms/src/main/java/com/pudonghot/yo/cms/controller/AgentGroupController.java new file mode 100644 index 00000000..4dc24c64 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/controller/AgentGroupController.java @@ -0,0 +1,74 @@ +package com.pudonghot.yo.cms.controller; + +import com.pudonghot.yo.cms.auth.SessionAbility; +import com.pudonghot.yo.cms.form.create.CreateFormAgentGroup; +import com.wacai.tigon.form.FormList; +import com.wacai.tigon.mybatis.Search; +import com.wacai.tigon.web.controller.ArgQuery; +import com.wacai.tigon.web.annotation.ListApi; +import org.springframework.stereotype.Controller; +import com.wacai.tigon.web.annotation.FilterCol; +import com.pudonghot.yo.model.domain.AgentGroup; +import com.pudonghot.yo.cms.annotation.Taggable; +import com.pudonghot.yo.model.domain.TrunkStrategy; +import com.wacai.tigon.web.controller.BaseCrudController; +import com.pudonghot.yo.cms.annotation.TenantResource; +import com.pudonghot.yo.cms.service.TrunkStrategyService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import com.pudonghot.yo.cms.form.update.UpdateFormAgentGroup; + +/** + * @author Donghuang
+ * Nov 02, 2019 13:46:16 + */ +@Controller +@ListApi(searchCols = { + AgentGroup.NAME, + AgentGroup.NOTE +}, +filterCols = { + @FilterCol(param = AgentGroup.ACTIVE, type = boolean.class), + @FilterCol(param = AgentGroup.PRIVACY_LEVEL, type = String.class), + @FilterCol(param = AgentGroup.TRUNK_STRATEGIES, type = Integer.class, searchAttr = true), + @FilterCol(param = AgentGroup.TAGS, type = Integer.class, searchAttr = true) +}) +@Taggable +@TenantResource +@RequestMapping("/agent-group") +public class AgentGroupController + extends BaseCrudController implements SessionAbility { + @Autowired + private TrunkStrategyService trunkStrategyService; + + /** + * {@inheritDoc} + */ + @Override + protected void before(final ArgQuery arg) { + super.before(arg); + arg.getSearch().attr("withTrunkStrategies", true); + } + + /** + * {@inheritDoc} + */ + @Override + protected void after(final ArgQuery arg) { + super.after(arg); + arg.getResult().attr("trunkStrategiesList", + trunkStrategyService.list( + new Search(TrunkStrategy.TENANT_ID, getTenantId()))); + arg.getResult().attr("privacyLevelsList", AgentGroup.PrivacyLevel.values()); + } + + @RequestMapping("/privacyLevelsList") + public AgentGroup.PrivacyLevel[] privacyLevelsList(){ + return AgentGroup.PrivacyLevel.values(); + } + +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/controller/AreaCodeController.java b/cms/src/main/java/com/pudonghot/yo/cms/controller/AreaCodeController.java new file mode 100644 index 00000000..43121321 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/controller/AreaCodeController.java @@ -0,0 +1,42 @@ +package com.pudonghot.yo.cms.controller; + +import com.pudonghot.yo.cms.auth.SessionAbility; +import com.pudonghot.yo.cms.form.create.CreateFormAreaCode; +import com.pudonghot.yo.cms.form.update.UpdateFormAreaCode; +import lombok.extern.slf4j.Slf4j; +import com.wacai.tigon.form.FormList; +import com.wacai.tigon.web.annotation.ListApi; +import org.springframework.stereotype.Controller; +import com.pudonghot.yo.model.domain.AreaCode; +import com.wacai.tigon.web.annotation.FilterCol; +import com.pudonghot.yo.cms.annotation.Taggable; +import com.wacai.tigon.web.controller.BaseCrudController; +import com.pudonghot.yo.cms.annotation.TenantResource; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + * Area code is public, has no tenant, @TenantResource on search attr + * @author Donghuang
+ * Jan 09, 2018 19:32:00 + */ +@Slf4j +@Controller +@ListApi(searchCols = { + AreaCode.CODE, + AreaCode.CITY, + AreaCode.NOTE +}, +filterCols = { + @FilterCol(param = AreaCode.TAGS, type = Integer.class, searchAttr = true) +}) +@Taggable +@TenantResource(searchAttr = true) +@RequestMapping("/area-code") +public class AreaCodeController + extends BaseCrudController + implements SessionAbility { +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/controller/AuthController.java b/cms/src/main/java/com/pudonghot/yo/cms/controller/AuthController.java new file mode 100644 index 00000000..31625d51 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/controller/AuthController.java @@ -0,0 +1,61 @@ +package com.pudonghot.yo.cms.controller; + +import java.util.Map; +import java.util.HashMap; + +import com.pudonghot.yo.cms.auth.SessionAbility; +import com.wacai.tigon.mybatis.Search; +import org.springframework.util.Assert; +import javax.validation.constraints.NotBlank; +import org.springframework.stereotype.Controller; +import com.pudonghot.yo.model.domain.AuthUser; +import com.pudonghot.yo.cms.service.TenantService; +import com.pudonghot.yo.cms.service.AuthUserService; +import com.pudonghot.yo.cms.service.AuthRoleService; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import com.pudonghot.yo.cms.service.AuthPermissionService; + +/** + * @author Donghuang
+ * Mar 14, 2017 14:59:22 + */ +@Controller +public class AuthController implements SessionAbility { + @Autowired + private TenantService tenantService; + @Autowired + private AuthUserService authUserService; + @Autowired + private AuthRoleService authRoleService; + @Autowired + private AuthPermissionService authPermissionService; + + @RequestMapping("/auth/info") + public Map info() { + final Map map = new HashMap<>(4); + final String account = getUserAccount(); + final AuthUser authUser = authUserService.find( + new Search(AuthUser.ACCOUNT, account)); + Assert.state(authUser != null, + () -> "No user [" + account + "] found"); + Assert.state(authUser.getActive(), + () -> "User [" + account + "] is not active"); + + map.put("account", account); + map.put("name", getUsername()); + map.put("roles", authRoleService.listOfUser(account)); + map.put("permissions", authPermissionService.listOfUser(account)); + map.put("tenants", authUser.isAdmin() ? + tenantService.listOfAdmin() : + tenantService.listOfUser(account)); + map.put("tenant", tenantService.current(account)); + return map; + } + + @RequestMapping("/auth/switch-tenant") + public void switchTenant(@NotBlank @RequestParam("tenant") final String tenant) { + tenantService.switchTo(getUserAccount(), tenant); + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/controller/AuthPermissionController.java b/cms/src/main/java/com/pudonghot/yo/cms/controller/AuthPermissionController.java new file mode 100644 index 00000000..04f9cd03 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/controller/AuthPermissionController.java @@ -0,0 +1,65 @@ +package com.pudonghot.yo.cms.controller; + +import com.pudonghot.yo.cms.annotation.Taggable; +import com.pudonghot.yo.cms.auth.SessionAbility; +import com.pudonghot.yo.cms.form.create.CreateFormAuthPermission; +import com.wacai.tigon.form.FormList; +import com.wacai.tigon.model.ViewModel; +import com.wacai.tigon.web.controller.ArgQuery; +import com.wacai.tigon.web.annotation.ListApi; +import org.springframework.stereotype.Controller; +import com.wacai.tigon.web.annotation.FilterCol; +import com.pudonghot.yo.cms.service.AuthRoleService; +import com.pudonghot.yo.model.domain.AuthPermission; +import com.wacai.tigon.web.controller.BaseCrudController; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.beans.factory.annotation.Autowired; +import com.pudonghot.yo.cms.form.update.UpdateFormAuthPermission; + +/** + * @author Donghuang
+ * Dec 24, 2019 17:24:04 + */ +@Controller +@ListApi(searchCols = { + AuthPermission.PERMISSION, + AuthPermission.NOTE +}, +filterCols = { + @FilterCol(param = AuthPermission.ACTIVE, type = boolean.class), + @FilterCol(param = AuthPermission.TAGS, type = Integer.class, searchAttr = true), + @FilterCol(param = AuthPermission.ROLES, type = Integer.class, searchAttr = true) +}) +@Taggable +@RequestMapping("/auth-permission") +public class AuthPermissionController + extends BaseCrudController + implements SessionAbility { + + @Autowired + private AuthRoleService authRoleService; + + /** + * {@inheritDoc} + */ + @Override + protected void before(final ArgQuery arg) { + super.before(arg); + arg.getSearch().attr("withRoles", true); + } + + /** + * {@inheritDoc} + */ + @Override + protected void after(final ArgQuery arg) { + super.after(arg); + + final ViewModel model = arg.getResult(); + model.setAttr("rolesList", authRoleService.list(null)); + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/controller/AuthRoleController.java b/cms/src/main/java/com/pudonghot/yo/cms/controller/AuthRoleController.java new file mode 100644 index 00000000..2293a5c7 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/controller/AuthRoleController.java @@ -0,0 +1,37 @@ +package com.pudonghot.yo.cms.controller; + +import com.pudonghot.yo.cms.auth.SessionAbility; +import com.pudonghot.yo.cms.form.create.CreateFormAuthRole; +import com.wacai.tigon.form.FormList; +import com.wacai.tigon.web.annotation.ListApi; +import org.springframework.stereotype.Controller; +import com.pudonghot.yo.model.domain.AuthRole; +import com.wacai.tigon.web.annotation.FilterCol; +import com.pudonghot.yo.cms.annotation.Taggable; +import com.wacai.tigon.web.controller.BaseCrudController; +import org.springframework.web.bind.annotation.RequestMapping; +import com.pudonghot.yo.cms.form.update.UpdateFormAuthRole; + +/** + * @author Donghuang
+ * Dec 24, 2019 17:13:49 + */ +@Controller +@ListApi(searchCols = { + AuthRole.ROLE, + AuthRole.NOTE +}, +filterCols = { + @FilterCol(param = AuthRole.ACTIVE, type = boolean.class), + @FilterCol(param = AuthRole.TAGS, type = Integer.class, searchAttr = true) +}) +@Taggable +@RequestMapping("/auth-role") +public class AuthRoleController + extends BaseCrudController + implements SessionAbility { +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/controller/AuthUserController.java b/cms/src/main/java/com/pudonghot/yo/cms/controller/AuthUserController.java new file mode 100644 index 00000000..e08d93e1 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/controller/AuthUserController.java @@ -0,0 +1,73 @@ +package com.pudonghot.yo.cms.controller; + +import com.pudonghot.yo.cms.auth.SessionAbility; +import com.pudonghot.yo.cms.form.create.CreateFormAuthUser; +import com.wacai.tigon.form.FormList; +import com.wacai.tigon.model.ViewModel; +import com.wacai.tigon.web.controller.ArgQuery; +import com.wacai.tigon.web.annotation.ListApi; +import org.springframework.stereotype.Controller; +import com.pudonghot.yo.model.domain.AuthUser; +import com.wacai.tigon.web.annotation.FilterCol; +import com.pudonghot.yo.cms.annotation.Taggable; +import com.pudonghot.yo.cms.service.TenantService; +import com.pudonghot.yo.cms.service.AuthRoleService; +import com.wacai.tigon.web.controller.BaseCrudController; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import com.pudonghot.yo.cms.form.update.UpdateFormAuthUser; + +/** + * @author Donghuang
+ * Dec 24, 2019 17:13:49 + */ +@Controller +@ListApi(searchCols = { + AuthUser.NAME, + AuthUser.ACCOUNT, + AuthUser.NOTE +}, +filterCols = { + @FilterCol(param = AuthUser.ACTIVE, type = boolean.class), + @FilterCol(param = AuthUser.ADMIN, type = boolean.class), + @FilterCol(param = AuthUser.TAGS, type = Integer.class, searchAttr = true), + @FilterCol(param = AuthUser.ROLES, type = Integer.class, searchAttr = true), + @FilterCol(param = AuthUser.TENANTS, type = Integer.class, searchAttr = true) +}) +@Taggable +@RequestMapping("/auth-user") +public class AuthUserController + extends BaseCrudController + implements SessionAbility { + + @Autowired + private AuthRoleService authRoleService; + @Autowired + private TenantService tenantService; + + /** + * {@inheritDoc} + */ + @Override + protected void before(final ArgQuery arg) { + super.before(arg); + arg.getSearch().attr("withRoles", true) + .attr("withTenants", true); + } + + /** + * {@inheritDoc} + */ + @Override + protected void after(final ArgQuery arg) { + super.after(arg); + + final ViewModel model = arg.getResult(); + model.setAttr("rolesList", authRoleService.list(null)); + model.setAttr("tenantsList", tenantService.list(null)); + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/controller/CallDetailRecordController.java b/cms/src/main/java/com/pudonghot/yo/cms/controller/CallDetailRecordController.java new file mode 100644 index 00000000..0e4b271b --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/controller/CallDetailRecordController.java @@ -0,0 +1,69 @@ +package com.pudonghot.yo.cms.controller; + +import com.pudonghot.yo.cms.auth.SessionAbility; +import com.pudonghot.yo.cms.form.CallDetailRecordListForm; +import com.pudonghot.yo.mapper.CallRecordingMapper; +import com.pudonghot.yo.model.domain.CallDetailRecord; +import com.pudonghot.yo.model.domain.CallRecording; +import com.wacai.tigon.model.ViewModel; +import com.wacai.tigon.mybatis.Search; +import com.wacai.tigon.web.controller.ArgQuery; +import com.wacai.tigon.web.controller.BaseQueryController; +import com.wacai.tigon.web.annotation.FilterCol; +import com.wacai.tigon.web.annotation.ListApi; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; + +import java.util.List; + +/** + * @author bingpo + * @date 2020/3/30 下午3:27 + */ +@Controller +@ListApi(searchCols = { + CallDetailRecord.CONN_ID +}, +filterCols = { + @FilterCol(param = CallDetailRecord.CONN_ID, type = String.class) +}) +@RequestMapping("/call-detail-record") +public class CallDetailRecordController + extends BaseQueryController + implements SessionAbility { + @Autowired + private CallRecordingMapper callRecordingMapper; + + @Override + protected void before(ArgQuery arg) { + if (arg.getType() == ArgQuery.Type.LIST) { + final CallDetailRecordListForm form = (CallDetailRecordListForm) arg.getArg(); + final Search search = arg.getSearch(); + search.gt(CallDetailRecord.START_STAMP, form.getStartDate()); + search.lt(CallDetailRecord.START_STAMP, form.getEndDate()); + final String connId = form.getConnId(); + if (StringUtils.isNotBlank(connId)) { + search.eq(CallDetailRecord.CONN_ID, connId); + } + + } + } + + @Override + protected void after(ArgQuery arg) { + super.after(arg); + if (arg.getType() == ArgQuery.Type.LIST) { + final ViewModel>> vmList = + (ViewModel>>) arg.getResult(); + vmList.getData().forEach(vm -> { + final CallDetailRecord record = vm.getData(); + if (record.getAnswerStamp() != null) { + vm.setAttr("recording", callRecordingMapper.find( + new Search(CallRecording.CONN_ID, vm.getData().getConnId()))); + } + }); + } + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/controller/CallingListController.java b/cms/src/main/java/com/pudonghot/yo/cms/controller/CallingListController.java new file mode 100644 index 00000000..a57a861c --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/controller/CallingListController.java @@ -0,0 +1,91 @@ +package com.pudonghot.yo.cms.controller; + +import com.pudonghot.yo.cms.auth.SessionAbility; +import com.pudonghot.yo.cms.form.create.CreateFormCallingList; +import com.pudonghot.yo.cms.annotation.TenantResource; +import com.pudonghot.yo.cms.form.update.UpdateFormCallingList; +import com.pudonghot.yo.mapper.CampaignMapper; +import com.pudonghot.yo.model.domain.CallingList; +import com.pudonghot.yo.model.domain.Campaign; +import com.wacai.tigon.form.FormList; +import com.wacai.tigon.model.ViewModel; +import com.wacai.tigon.mybatis.Search; +import com.wacai.tigon.web.controller.ArgQuery; +import com.wacai.tigon.web.controller.BaseCrudController; +import com.wacai.tigon.web.annotation.FilterCol; +import com.wacai.tigon.web.annotation.ListApi; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author bingpo + * @date 2020/3/16 下午4:00 + */ +@Controller +@ListApi(searchCols = { + CallingList.PHONE, + CallingList.RECORD_KEY, + CallingList.TASK_KEY +}, +filterCols = { + @FilterCol(param = CallingList.ACTIVE, type = boolean.class), + @FilterCol(param = CallingList.CAMPAIGN_ID, type = Integer.class), + @FilterCol(param = CallingList.STATUS, type = String.class), +}) +@TenantResource +@RequestMapping("/calling-list") +public class CallingListController + extends BaseCrudController implements SessionAbility { + @Autowired + private CampaignMapper campaignMapper; + + /** + * {@inheritDoc} + */ + @Override + protected void before(final ArgQuery arg) { + super.before(arg); + } + + /** + * {@inheritDoc} + */ + @Override + protected void after(final ArgQuery arg) { + super.after(arg); + if (arg.getType() == ArgQuery.Type.LIST) { + final ViewModel>> vmList = + (ViewModel>>) arg.getResult(); + vmList.getData().forEach(vm -> + vm.setAttr("campaign", campaignMapper.find(new Search(vm.getData().getCampaignId()))) + ); + } + final ViewModel vm = arg.getResult(); + vm.setAttr("campaignList", campaignMapper.list(new Search(Campaign.ACTIVE, true))); + vm.setAttr("callingListStatus", status()); + } + + @RequestMapping("/status") + public List> status() { + CallingList.Status[] statusArr = CallingList.Status.values(); + List> statusList = new ArrayList<>(statusArr.length); + for (CallingList.Status status : CallingList.Status.values()) { + statusList.add( + new HashMap(4) {{ + put("label", status.name()); + put("value", status.name()); + }}); + } + return statusList; + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/controller/CampaignController.java b/cms/src/main/java/com/pudonghot/yo/cms/controller/CampaignController.java new file mode 100644 index 00000000..4094d81c --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/controller/CampaignController.java @@ -0,0 +1,116 @@ +package com.pudonghot.yo.cms.controller; + +import com.pudonghot.yo.cms.auth.SessionAbility; +import com.pudonghot.yo.cms.form.create.CreateFormCampaign; +import com.pudonghot.yo.cms.service.CampaignService; +import com.pudonghot.yo.mapper.RobotCallConfigMapper; +import com.pudonghot.yo.model.domain.Campaign; +import com.pudonghot.yo.model.domain.RobotCallConfig; +import com.pudonghot.yo.model.domain.TrunkStrategy; +import com.wacai.tigon.form.FormList; +import com.wacai.tigon.model.ViewModel; +import com.wacai.tigon.mybatis.Search; +import com.wacai.tigon.web.controller.ArgQuery; +import com.wacai.tigon.web.annotation.ListApi; +import org.apache.shiro.util.Assert; +import org.springframework.stereotype.Controller; +import com.wacai.tigon.web.annotation.FilterCol; +import com.pudonghot.yo.cms.annotation.Taggable; +import com.wacai.tigon.web.controller.BaseCrudController; +import com.pudonghot.yo.cms.annotation.TenantResource; +import com.pudonghot.yo.cms.service.TrunkStrategyService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import com.pudonghot.yo.cms.form.update.UpdateFormCampaign; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.List; + +/** + * @author Donghuang
+ * Nov 02, 2019 13:46:16 + */ +@Controller +@ListApi(searchCols = { + Campaign.NAME, + Campaign.NOTE +}, +filterCols = { + @FilterCol(param = Campaign.ACTIVE, type = boolean.class), + @FilterCol(param = Campaign.STATUS, type = Campaign.Status.class), + @FilterCol(param = Campaign.TYPE, type = Campaign.Type.class), + @FilterCol(param = Campaign.TRUNK_STRATEGIES, type = Integer.class, searchAttr = true), + @FilterCol(param = Campaign.TAGS, type = Integer.class, searchAttr = true) +}) +@Taggable +@TenantResource +@RequestMapping("/campaign") +public class CampaignController + extends BaseCrudController implements SessionAbility { + @Autowired + private TrunkStrategyService trunkStrategyService; + @Autowired + private RobotCallConfigMapper robotCallConfigMapper; + + @RequestMapping("/start") + public void start(@RequestParam("id") Integer id) { + activity(id, Campaign.Status.RUNNING); + } + + @RequestMapping("/stop") + public void stop(@RequestParam("id") Integer id) { + activity(id, Campaign.Status.STOPPED); + } + + private void activity(final Integer id, final Campaign.Status status) { + final CampaignService campaignService = ((CampaignService) crudService); + final Campaign campaign = campaignService.find(new Search(id).eq(Campaign.ACTIVE, true)); + Assert.state(campaign != null, "无效的外呼活动"); + campaign.setStatus(status); + campaignService.update(campaign); + } + + /** + * {@inheritDoc} + */ + @Override + protected void before(final ArgQuery arg) { + super.before(arg); + arg.getSearch().attr("withTrunkStrategies", true); + } + + /** + * {@inheritDoc} + */ + @Override + protected void after(final ArgQuery arg) { + super.after(arg); + arg.getResult().attr("trunkStrategiesList", + trunkStrategyService.list( + new Search(TrunkStrategy.TENANT_ID, getTenantId()))); + // arg.getResult().attr("stateMachineList", stateMachineList()); + if (arg.getType() == ArgQuery.Type.LIST) { + final ViewModel>> vmList = + (ViewModel>>) arg.getResult(); + vmList.getData().forEach(vm -> + vm.setAttr("running", vm.getData().getStatus() == Campaign.Status.RUNNING) + ); + } else if (arg.getType() == ArgQuery.Type.FIND) { + final ViewModel vm = (ViewModel) arg.getResult(); + final RobotCallConfig robotCallConfig = robotCallConfigMapper.find( + new Search(RobotCallConfig.CAMPAIGN_ID, vm.getData().getId())); + if (robotCallConfig != null) { + vm.setAttr("robotResultTopic", robotCallConfig.getResultTopic()); + vm.setAttr("robotStateMachineId", robotCallConfig.getStateMachineId()); + vm.setAttr("robotFetchData", robotCallConfig.getFetchData()); + vm.setAttr("robotFetchLocal", !robotCallConfig.getFetchLocal()); + vm.setAttr("robotFetchUrl", robotCallConfig.getFetchUrl()); + vm.setAttr("robotFetchHeader", robotCallConfig.getFetchHeader()); + } + } + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/controller/GatewayController.java b/cms/src/main/java/com/pudonghot/yo/cms/controller/GatewayController.java new file mode 100644 index 00000000..accbbff6 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/controller/GatewayController.java @@ -0,0 +1,32 @@ +package com.pudonghot.yo.cms.controller; + +import com.pudonghot.yo.cms.form.create.CreateFormGateway; +import com.wacai.tigon.form.FormList; +import com.pudonghot.yo.model.domain.Gateway; +import com.wacai.tigon.web.annotation.ListApi; +import org.springframework.stereotype.Controller; +import com.wacai.tigon.web.annotation.FilterCol; +import com.wacai.tigon.web.controller.BaseCrudController; +import org.springframework.web.bind.annotation.RequestMapping; +import com.pudonghot.yo.cms.form.update.UpdateFormGateway; + +/** + * @author Donghuang
+ * Nov 01, 2019 22:53:11 + */ +@Controller +@ListApi(searchCols = { + Gateway.NAME, + Gateway.NOTE +}, +filterCols = { + @FilterCol(param = Gateway.ACTIVE, type = boolean.class) +}) +@RequestMapping("/gateway") +public class GatewayController + extends BaseCrudController { +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/controller/QueueController.java b/cms/src/main/java/com/pudonghot/yo/cms/controller/QueueController.java new file mode 100644 index 00000000..a4356f5e --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/controller/QueueController.java @@ -0,0 +1,34 @@ +package com.pudonghot.yo.cms.controller; + +import com.pudonghot.yo.cms.form.create.CreateFormQueue; +import com.wacai.tigon.form.FormList; +import com.pudonghot.yo.model.domain.Queue; +import com.wacai.tigon.web.annotation.ListApi; +import org.springframework.stereotype.Controller; +import com.wacai.tigon.web.annotation.FilterCol; +import com.wacai.tigon.web.controller.BaseCrudController; +import com.pudonghot.yo.cms.annotation.TenantResource; +import com.pudonghot.yo.cms.form.update.UpdateFormQueue; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + * @author Donghuang
+ * Nov 01, 2019 22:53:11 + */ +@Controller +@ListApi(searchCols = { + Queue.NAME, + Queue.NOTE +}, +filterCols = { + @FilterCol(param = Queue.ACTIVE, type = boolean.class) +}) +@TenantResource +@RequestMapping("/queue") +public class QueueController + extends BaseCrudController { +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/controller/RobotAgentController.java b/cms/src/main/java/com/pudonghot/yo/cms/controller/RobotAgentController.java new file mode 100644 index 00000000..d6d3fc48 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/controller/RobotAgentController.java @@ -0,0 +1,114 @@ +package com.pudonghot.yo.cms.controller; + +import com.pudonghot.yo.cms.auth.SessionAbility; +import com.pudonghot.yo.cms.form.create.CreateFormRobotAgent; +import com.pudonghot.yo.cms.annotation.Taggable; +import com.pudonghot.yo.cms.annotation.TenantResource; +import com.pudonghot.yo.cms.form.update.UpdateFormRobotAgent; +import com.pudonghot.yo.cms.service.AgentService; +import com.pudonghot.yo.cms.service.RobotAgentService; +import com.pudonghot.yo.model.domain.Agent; +import com.pudonghot.yo.model.domain.RobotAgent; +import com.pudonghot.yo.model.ValueTextModel; +import com.wacai.tigon.form.FormList; +import com.wacai.tigon.model.ViewModel; +import com.wacai.tigon.mybatis.Search; +import com.wacai.tigon.web.controller.ArgQuery; +import com.wacai.tigon.web.controller.BaseCrudController; +import com.wacai.tigon.web.annotation.FilterCol; +import com.wacai.tigon.web.annotation.ListApi; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; + +import javax.validation.Valid; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + + +/** + * @author qiushui
+ * qiushui@wacai.com
+ */ +@Controller +@ListApi(searchCols = { + RobotAgent.ACCOUNT, + RobotAgent.AGENT, + RobotAgent.BIND_ADDR +}, + filterCols = { + @FilterCol(param = RobotAgent.ACTIVE, type = boolean.class), + @FilterCol(param = RobotAgent.STATUS, type = RobotAgent.Status.class), + @FilterCol(param = RobotAgent.BIND_ADDR, type = String.class) + }) +@Taggable +@TenantResource +@RequestMapping("/robot-agent") +public class RobotAgentController + extends BaseCrudController implements SessionAbility { + + @Autowired + private AgentService agentService; + + + @RequestMapping("/batch-create") + public void batchCreate(@Valid CreateFormRobotAgent form) { + ((RobotAgentService) queryService).batchCreate(form); + } + + /** + * {@inheritDoc} + */ + @Override + protected void after(final ArgQuery arg) { + super.after(arg); + final ViewModel model = arg.getResult(); + model.setAttr("statusList", statusList()); + model.setAttr("bindAddrList", bindAddrList()); + } + + public List bindAddrList() { + List list = new ArrayList<>(); + List robotAgentList = queryService.list(new Search()); + Set bindAddressSet = robotAgentList.stream().map(RobotAgent::getBindAddr).filter(item -> !StringUtils.isEmpty(item)).collect(Collectors.toSet()); + for (String bindAddress : bindAddressSet) { + list.add(new ValueTextModel(bindAddress, bindAddress)); + } + return list; + } + + public List statusList() { + List list = new ArrayList<>(); + for (RobotAgent.Status status : RobotAgent.Status.values()) { + list.add(new ValueTextModel(status.name(), status.name())); + } + return list; + } + + @RequestMapping("/list-agent") + public List listAgent() { + List list = new ArrayList<>(); + Integer tenantId = getTenantId(); + List agentList = agentService.list(new Search().eq(Agent.ACTIVE, true) + .eq(Agent.TENANT_ID, tenantId).eq(Agent.TYPE, Agent.Type.ROBOT)); + + Map robotAgentMap = queryService.list(new Search()). + stream().collect(Collectors.toMap(RobotAgent::getAgentId, Function.identity() + )); + agentList = agentList.stream(). + filter(item -> !robotAgentMap.keySet().contains(item.getId())). + collect(Collectors.toList()); + + for (Agent agent : agentList) { + list.add(new ValueTextModel(agent.getId(), agent.getAccount()+":"+agent.getAgent())); + } + return list; + } + +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/controller/SequenceController.java b/cms/src/main/java/com/pudonghot/yo/cms/controller/SequenceController.java new file mode 100644 index 00000000..8d436de4 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/controller/SequenceController.java @@ -0,0 +1,34 @@ +package com.pudonghot.yo.cms.controller; + +import com.pudonghot.yo.cms.form.create.CreateFormSequence; +import com.pudonghot.yo.cms.annotation.TenantResource; +import com.wacai.tigon.form.FormList; +import com.wacai.tigon.web.annotation.ListApi; +import org.springframework.stereotype.Controller; +import com.pudonghot.yo.model.domain.Sequence; +import com.wacai.tigon.web.annotation.FilterCol; +import com.wacai.tigon.web.controller.BaseCrudController; +import org.springframework.web.bind.annotation.RequestMapping; +import com.pudonghot.yo.cms.form.update.UpdateFormSequence; + +/** + * @author Donghuang
+ * Nov 02, 2019 12:46:49 + */ +@Controller +@ListApi(searchCols = { + Sequence.NAME, + Sequence.NOTE +}, +filterCols = { + @FilterCol(param = Sequence.ACTIVE, type = boolean.class) +}) +@TenantResource +@RequestMapping("/sequence") +public class SequenceController + extends BaseCrudController { +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/controller/SiteController.java b/cms/src/main/java/com/pudonghot/yo/cms/controller/SiteController.java new file mode 100644 index 00000000..fbf82239 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/controller/SiteController.java @@ -0,0 +1,22 @@ +package com.pudonghot.yo.cms.controller; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.io.Resource; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.core.io.ClassPathResource; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + * @author Donghuang
+ * Oct 26, 2019 15:57:14 + */ +@Slf4j +@Controller +public class SiteController { + + @RequestMapping("/") + public ResponseEntity index() { + return ResponseEntity.ok(new ClassPathResource("/yo-cms/index.html")); + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/controller/TagController.java b/cms/src/main/java/com/pudonghot/yo/cms/controller/TagController.java new file mode 100644 index 00000000..40da9291 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/controller/TagController.java @@ -0,0 +1,51 @@ +package com.pudonghot.yo.cms.controller; + +import java.util.Arrays; + +import com.pudonghot.yo.cms.auth.SessionAbility; +import com.pudonghot.yo.cms.form.create.CreateFormTag; +import com.wacai.tigon.form.FormList; +import com.pudonghot.yo.model.domain.Tag; +import com.wacai.tigon.web.controller.ArgQuery; +import com.wacai.tigon.web.annotation.ListApi; +import org.springframework.stereotype.Controller; +import com.wacai.tigon.web.annotation.FilterCol; +import com.wacai.tigon.web.controller.BaseCrudController; +import com.pudonghot.yo.cms.form.update.UpdateFormTag; +import com.pudonghot.yo.cms.annotation.TenantResource; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + * @author Donghuang
+ * Nov 14, 2019 12:05:22 + */ +@Controller +@ListApi(searchCols = { + Tag.TAG, + Tag.NOTE +}, +filterCols = { + @FilterCol(param = Tag.SCOPE, type = Tag.Scope.class), + @FilterCol(param = Tag.ACTIVE, type = boolean.class) +}) +@TenantResource +@RequestMapping("/tag") +public class TagController + extends BaseCrudController + implements SessionAbility { + + /** + * {@inheritDoc} + */ + @Override + protected void before(final ArgQuery arg) { + super.before(arg); + arg.getSearch().in(Tag.OWNER, + Arrays.asList(Tag.OWNER_COMMON, + getUserAccount())); + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/controller/TelecomVendorController.java b/cms/src/main/java/com/pudonghot/yo/cms/controller/TelecomVendorController.java new file mode 100644 index 00000000..b4effa1e --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/controller/TelecomVendorController.java @@ -0,0 +1,32 @@ +package com.pudonghot.yo.cms.controller; + +import com.pudonghot.yo.cms.form.create.CreateFormTelecomVendor; +import com.wacai.tigon.form.FormList; +import com.wacai.tigon.web.annotation.ListApi; +import org.springframework.stereotype.Controller; +import com.wacai.tigon.web.annotation.FilterCol; +import com.pudonghot.yo.model.domain.TelecomVendor; +import com.wacai.tigon.web.controller.BaseCrudController; +import org.springframework.web.bind.annotation.RequestMapping; +import com.pudonghot.yo.cms.form.update.UpdateFormTelecomVendor; + +/** + * @author Donghuang
+ * Nov 01, 2019 22:53:11 + */ +@Controller +@ListApi(searchCols = { + TelecomVendor.NAME, + TelecomVendor.NOTE +}, +filterCols = { + @FilterCol(param = TelecomVendor.ACTIVE, type = boolean.class) +}) +@RequestMapping("/telecom-vendor") +public class TelecomVendorController + extends BaseCrudController { +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/controller/TenantController.java b/cms/src/main/java/com/pudonghot/yo/cms/controller/TenantController.java new file mode 100644 index 00000000..73bd2078 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/controller/TenantController.java @@ -0,0 +1,28 @@ +package com.pudonghot.yo.cms.controller; + +import com.pudonghot.yo.cms.form.create.CreateFormTenant; +import com.wacai.tigon.form.FormList; +import com.pudonghot.yo.model.domain.Tenant; +import com.wacai.tigon.web.annotation.ListApi; +import org.springframework.stereotype.Controller; +import com.wacai.tigon.web.annotation.FilterCol; +import com.wacai.tigon.web.controller.BaseCrudController; +import com.pudonghot.yo.cms.form.update.UpdateFormTenant; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + * @author Donghuang
+ * Oct 26, 2019 16:02:19 + */ +@Controller +@ListApi(searchCols = { + Tenant.NAME, + Tenant.CODE, + Tenant.NOTE +}, +filterCols = { + @FilterCol(param = Tenant.ACTIVE, type = boolean.class) +}) +@RequestMapping("/tenant") +public class TenantController extends BaseCrudController { +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/controller/TenantQueryHook.java b/cms/src/main/java/com/pudonghot/yo/cms/controller/TenantQueryHook.java new file mode 100644 index 00000000..d9a6feac --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/controller/TenantQueryHook.java @@ -0,0 +1,82 @@ +package com.pudonghot.yo.cms.controller; + +import com.pudonghot.yo.cms.auth.SessionAbility; +import lombok.extern.slf4j.Slf4j; +import com.wacai.tigon.mybatis.Search; +import com.wacai.tigon.model.ViewModel; +import com.wacai.tigon.web.controller.ArgQuery; +import com.pudonghot.yo.model.domain.Agent; +import org.springframework.stereotype.Component; +import com.pudonghot.yo.model.domain.ObjectTag; +import com.pudonghot.yo.cms.service.TagService; +import com.pudonghot.yo.cms.annotation.Taggable; +import org.springframework.core.annotation.AnnotationUtils; +import com.pudonghot.yo.cms.annotation.TenantResource; +import com.wacai.tigon.web.controller.BaseQueryControllerHook; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * @author Donghuang
+ * Nov 04, 2019 23:05:13 + */ +@Slf4j +@Component +public class TenantQueryHook implements BaseQueryControllerHook, SessionAbility { + @Autowired + private TagService tagService; + + /** + * {@inheritDoc} + */ + @Override + public void init(final ArgQuery arg) { + + } + + /** + * {@inheritDoc} + */ + @Override + public void before(final ArgQuery arg) { + final TenantResource tr = AnnotationUtils.findAnnotation( + arg.getController(), TenantResource.class); + if (tr != null) { + final Integer tenantId = getTenantId(); + if (tr.searchAttr()) { + log.debug("Query before: @TenantResource found, add search attr [{}].", tenantId); + arg.getSearch().setAttr( + Agent.TENANT_ID, tenantId); + } + else { + log.debug("Query before: @TenantResource found, add search criterion [{}].", tenantId); + arg.getSearch().eq(Agent.TENANT_ID, tenantId); + } + } + + // Taggable + final Taggable taggable = AnnotationUtils.findAnnotation( + arg.getController(), Taggable.class); + if (taggable != null) { + log.debug("Query before: @Taggable found, add search attr 'withTags'"); + arg.getSearch().attr("withTags", true); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void after(final ArgQuery arg) { + final Taggable taggable = AnnotationUtils.findAnnotation( + arg.getController(), Taggable.class); + if (taggable != null) { + final Integer tenantId = getTenantId(); + log.debug("Query after: @Taggable found, add tenant [{}] tags.", tenantId); + final ViewModel result = arg.getResult(); + if (result != null) { + result.setAttr("tagsList", tagService.list( + new Search(ObjectTag.TENANT_ID, tenantId))); + } + } + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/controller/TrunkAttrController.java b/cms/src/main/java/com/pudonghot/yo/cms/controller/TrunkAttrController.java new file mode 100644 index 00000000..2135db57 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/controller/TrunkAttrController.java @@ -0,0 +1,36 @@ +package com.pudonghot.yo.cms.controller; + +import com.pudonghot.yo.cms.auth.SessionAbility; +import com.pudonghot.yo.cms.form.create.CreateFormTrunkAttr; +import com.wacai.tigon.form.FormList; +import com.wacai.tigon.web.annotation.ListApi; +import org.springframework.stereotype.Controller; +import com.wacai.tigon.web.annotation.FilterCol; +import com.pudonghot.yo.model.domain.TrunkAttr; +import com.wacai.tigon.web.controller.BaseCrudController; +import com.pudonghot.yo.cms.annotation.TenantResource; +import org.springframework.web.bind.annotation.RequestMapping; +import com.pudonghot.yo.cms.form.update.UpdateFormTrunkAttr; + +/** + * @author Donghuang
+ * Jan 17, 2020 15:30:57 + */ +@Controller +@ListApi(searchCols = { + TrunkAttr.ATTR, + TrunkAttr.NOTE +}, +filterCols = { + @FilterCol(param = TrunkAttr.ACTIVE, type = boolean.class) +}) +@TenantResource +@RequestMapping("/trunk-attr") +public class TrunkAttrController + extends BaseCrudController + implements SessionAbility { +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/controller/TrunkController.java b/cms/src/main/java/com/pudonghot/yo/cms/controller/TrunkController.java new file mode 100644 index 00000000..cee66f0d --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/controller/TrunkController.java @@ -0,0 +1,119 @@ +package com.pudonghot.yo.cms.controller; + +import com.pudonghot.yo.cms.auth.SessionAbility; +import com.pudonghot.yo.cms.form.create.CreateFormTrunk; +import com.pudonghot.yo.cms.service.*; +import com.wacai.tigon.form.FormList; +import com.wacai.tigon.model.ViewModel; +import com.wacai.tigon.mybatis.Search; +import com.pudonghot.yo.model.domain.Agent; +import com.pudonghot.yo.model.domain.Queue; +import com.pudonghot.yo.model.domain.Trunk; +import com.wacai.tigon.web.controller.ArgQuery; +import com.wacai.tigon.web.annotation.ListApi; +import org.springframework.stereotype.Controller; +import com.wacai.tigon.web.annotation.FilterCol; +import com.pudonghot.yo.cms.annotation.Taggable; +import com.pudonghot.yo.model.domain.TrunkStrategy; +import com.wacai.tigon.web.controller.BaseCrudController; +import org.springframework.web.bind.annotation.GetMapping; +import com.pudonghot.yo.cms.annotation.TenantResource; +import com.pudonghot.yo.cms.form.update.UpdateFormTrunk; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + * @author Donghuang
+ * Nov 02, 2019 12:46:49 + */ +@Controller +@ListApi(searchCols = { + Trunk.PREFIX, + Trunk.CALLER_NUMBER, + Trunk.CPN, + Trunk.NOTE +}, +filterCols = { + @FilterCol(param = Trunk.ACTIVE, type = boolean.class), + @FilterCol(param = Trunk.AREA_CODE, type = String.class), + @FilterCol(param = Trunk.OC_ADD_ZERO, type = boolean.class), + @FilterCol(param = Trunk.GATEWAY_ID, type = Integer.class), + @FilterCol(param = Trunk.TELECOM_VENDOR_ID, type = Integer.class), + @FilterCol(param = Trunk.INBOUND_TARGET_TYPE, type = Trunk.InboundTargetType.class), + @FilterCol(param = Trunk.TAGS, type = Integer.class, searchAttr = true), + @FilterCol(param = Trunk.STRATEGIES, type = Integer.class, searchAttr = true), + @FilterCol(param = Trunk.ATTRS, type = Integer.class, searchAttr = true) +}) +@Taggable +@TenantResource +@RequestMapping("/trunk") +public class TrunkController + extends BaseCrudController + implements SessionAbility { + + @Autowired + private AreaCodeService areaCodeService; + @Autowired + private TelecomVendorService telecomVendorService; + @Autowired + private GatewayService gatewayService; + @Autowired + private TrunkStrategyService trunkStrategyService; + @Autowired + private TrunkAttrService trunkAttrService; + @Autowired + private AgentService agentService; + @Autowired + private QueueService queueService; + + /** + * inbound target types + * + * @return inbound target types + */ + @GetMapping("/inbound-target-types") + public Trunk.InboundTargetType[] inboundTargetTypes() { + return Trunk.InboundTargetType.values(); + } + + /** + * {@inheritDoc} + */ + @Override + protected void before(final ArgQuery arg) { + super.before(arg); + arg.getSearch().attr("withStrategies", true); + arg.getSearch().attr("withAttrs", true); + } + + /** + * {@inheritDoc} + */ + @Override + protected void after(final ArgQuery arg) { + super.after(arg); + + final ViewModel model = arg.getResult(); + model.setAttr("gatewaysList", gatewayService.list(null)); + model.setAttr("telecomVendorsList", telecomVendorService.list(null)); + final Integer tenantId = getTenantId(); + model.setAttr("trunkStrategiesList", trunkStrategyService.list( + new Search(TrunkStrategy.TENANT_ID, tenantId))); + model.setAttr("trunkAttrsList", trunkAttrService.list( + new Search(TrunkStrategy.TENANT_ID, tenantId))); + model.setAttr("inboundTargetTypesList", + Trunk.InboundTargetType.values()); + model.attr("agentsList", agentService.list( + new Search(Agent.TENANT_ID, tenantId))); + model.attr("queuesList", queueService.list( + new Search(Queue.TENANT_ID, tenantId))); + + if (arg.getType() == ArgQuery.Type.LIST) { + model.setAttr("areaCodes", areaCodeService.list(null)); + } + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/controller/TrunkProhibitedAreaCodeController.java b/cms/src/main/java/com/pudonghot/yo/cms/controller/TrunkProhibitedAreaCodeController.java new file mode 100644 index 00000000..d6a050e9 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/controller/TrunkProhibitedAreaCodeController.java @@ -0,0 +1,33 @@ +package com.pudonghot.yo.cms.controller; + +import com.pudonghot.yo.cms.form.BaseUpdateForm; +import com.pudonghot.yo.cms.form.create.CreateFormTrunkProhibitedAreaCode; +import com.wacai.tigon.form.FormList; +import com.wacai.tigon.web.annotation.ListApi; +import org.springframework.stereotype.Controller; +import com.wacai.tigon.web.annotation.FilterCol; +import com.wacai.tigon.web.controller.BaseCrudController; +import com.pudonghot.yo.cms.annotation.TenantResource; +import org.springframework.web.bind.annotation.RequestMapping; +import com.pudonghot.yo.model.domain.TrunkProhibitedAreaCode; + +/** + * @author Donghuang
+ * Nov 30, 2019 19:01:50 + */ +@Controller +@ListApi(searchCols = { + TrunkProhibitedAreaCode.NOTE +}, +filterCols = { + @FilterCol(param = TrunkProhibitedAreaCode.ACTIVE, type = boolean.class) +}) +@TenantResource +@RequestMapping("/trunk-prohibited-area-code") +public class TrunkProhibitedAreaCodeController + extends BaseCrudController { +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/controller/TrunkStrategyController.java b/cms/src/main/java/com/pudonghot/yo/cms/controller/TrunkStrategyController.java new file mode 100644 index 00000000..4f51a6e0 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/controller/TrunkStrategyController.java @@ -0,0 +1,52 @@ +package com.pudonghot.yo.cms.controller; + +import com.pudonghot.yo.cms.form.create.CreateFormTrunkStrategy; +import com.pudonghot.yo.cms.form.update.UpdateFormTrunkStrategy; +import com.wacai.tigon.form.FormList; +import com.wacai.tigon.model.ViewModel; +import com.wacai.tigon.web.controller.ArgQuery; +import com.wacai.tigon.web.annotation.ListApi; +import org.springframework.stereotype.Controller; +import com.wacai.tigon.web.annotation.FilterCol; +import com.pudonghot.yo.model.domain.TrunkStrategy; +import com.wacai.tigon.web.controller.BaseCrudController; +import com.pudonghot.yo.cms.annotation.TenantResource; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + * @author Donghuang
+ * Nov 30, 2019 19:01:50 + */ +@Controller +@ListApi(searchCols = { + TrunkStrategy.NAME, + TrunkStrategy.NOTE +}, +filterCols = { + @FilterCol(param = TrunkStrategy.STRATEGY, type = TrunkStrategy.Strategy.class), + @FilterCol(param = TrunkStrategy.ACTIVE, type = boolean.class) +}) +@TenantResource +@RequestMapping("/trunk-strategy") +public class TrunkStrategyController + extends BaseCrudController { + + /** + * {@inheritDoc} + */ + @Override + protected void after(final ArgQuery arg) { + super.after(arg); + final ViewModel model = arg.getResult(); + model.setAttr("strategiesList", TrunkStrategy.Strategy.values()); + } + + @RequestMapping("/strategies") + public TrunkStrategy.Strategy[] strategies() { + return TrunkStrategy.Strategy.values(); + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/BaseBatchForm.java b/cms/src/main/java/com/pudonghot/yo/cms/form/BaseBatchForm.java new file mode 100644 index 00000000..a8bad5f6 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/BaseBatchForm.java @@ -0,0 +1,18 @@ +package com.pudonghot.yo.cms.form; + +import lombok.Getter; +import lombok.Setter; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.NotEmpty; + +/** + * @author Donghuang
+ * Nov 25, 2019 14:55:34 + */ +@Setter +@Getter +public class BaseBatchForm extends BasicForm implements SessionForm { + @NotNull + @NotEmpty + private Integer[] ids; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/BaseCreateForm.java b/cms/src/main/java/com/pudonghot/yo/cms/form/BaseCreateForm.java new file mode 100644 index 00000000..2cbcde09 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/BaseCreateForm.java @@ -0,0 +1,34 @@ +package com.pudonghot.yo.cms.form; + +import lombok.Getter; +import lombok.Setter; +import javax.validation.constraints.NotBlank; +import com.wacai.tigon.form.FormCreateApi; + +/** + * @author Donghuang
+ * Jun 21, 2017 18:01 + */ +@Getter +public class BaseCreateForm extends BasicForm implements FormCreateApi { + @NotBlank + private String createdBy; + @Setter + private boolean active = true; + @Setter + private String note; + + /** + * set login user + * @param loginUser login user + */ + @Override + public void setAuthUser(final String loginUser) { + setCreatedBy(loginUser); + } + + public void setCreatedBy(final String createdBy) { + super.setAuthUser(createdBy); + this.createdBy = createdBy; + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/BaseUpdateForm.java b/cms/src/main/java/com/pudonghot/yo/cms/form/BaseUpdateForm.java new file mode 100644 index 00000000..705b25af --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/BaseUpdateForm.java @@ -0,0 +1,42 @@ +package com.pudonghot.yo.cms.form; + +import lombok.Getter; +import lombok.Setter; +import com.wacai.tigon.form.FU0; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.NotBlank; + +/** + * @author Donghuang
+ * Jun 21, 2017 18:05 + */ +@Getter +public class BaseUpdateForm extends FU0 implements SessionForm { + @Setter + @NotNull + private Integer tenantId; + @Setter + @NotBlank + private String tenantCode; + @NotBlank + private String authUser; + @NotBlank + private String updatedBy; + @Setter + private boolean active; + @Setter + private String note; + + /** + * {@inheritDoc} + */ + @Override + public void setAuthUser(final String authUser) { + setUpdatedBy(authUser); + } + + public void setUpdatedBy(final String updatedBy) { + this.authUser = updatedBy; + this.updatedBy = updatedBy; + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/BasicForm.java b/cms/src/main/java/com/pudonghot/yo/cms/form/BasicForm.java new file mode 100644 index 00000000..9b60b72d --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/BasicForm.java @@ -0,0 +1,24 @@ +package com.pudonghot.yo.cms.form; + +import lombok.Getter; +import lombok.Setter; +import com.wacai.tigon.form.BaseForm; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.NotBlank; +import lombok.experimental.FieldNameConstants; + +/** + * @author Donghuang
+ * Jun 21, 2017 18:01 + */ +@Getter +@Setter +@FieldNameConstants(prefix = "") +public class BasicForm extends BaseForm implements SessionForm { + @NotNull + private Integer tenantId; + @NotBlank + private String tenantCode; + @NotBlank + private String authUser; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/BatchForm.java b/cms/src/main/java/com/pudonghot/yo/cms/form/BatchForm.java new file mode 100644 index 00000000..9f0b6e6c --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/BatchForm.java @@ -0,0 +1,17 @@ +package com.pudonghot.yo.cms.form; + +import lombok.Getter; +import lombok.Setter; +import java.util.List; +import javax.validation.constraints.NotEmpty; + +/** + * @author Donghuang
+ * Jun 21, 2017 18:01 + */ +@Setter +@Getter +public class BatchForm extends BasicForm { + @NotEmpty + private List ids; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/CallDetailRecordListForm.java b/cms/src/main/java/com/pudonghot/yo/cms/form/CallDetailRecordListForm.java new file mode 100644 index 00000000..9e70f19f --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/CallDetailRecordListForm.java @@ -0,0 +1,21 @@ +package com.pudonghot.yo.cms.form; + +import com.wacai.tigon.form.FormList; +import lombok.Getter; +import lombok.Setter; + +import java.util.Date; + +/** + * @author bingpo + * @date 2020/3/30 下午5:09 + */ +@Getter +@Setter +public class CallDetailRecordListForm extends FormList { + + private Date startDate; + private Date endDate; + private String connId; + +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/RobotConfigForm.java b/cms/src/main/java/com/pudonghot/yo/cms/form/RobotConfigForm.java new file mode 100644 index 00000000..b78e01ac --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/RobotConfigForm.java @@ -0,0 +1,20 @@ +package com.pudonghot.yo.cms.form; + +import lombok.Getter; +import lombok.Setter; + +/** + * @author bingpo + * @date 2020/3/30 下午2:19 + */ +@Getter +@Setter +public class RobotConfigForm { + private Boolean robotConfig; + private Boolean robotFetchData; + private String robotResultTopic; + private Integer robotStateMachineId; + private Boolean robotFetchLocal; + private String robotFetchUrl; + private String robotFetchHeader; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/SessionForm.java b/cms/src/main/java/com/pudonghot/yo/cms/form/SessionForm.java new file mode 100644 index 00000000..9a1f7773 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/SessionForm.java @@ -0,0 +1,45 @@ +package com.pudonghot.yo.cms.form; + +import com.wacai.tigon.form.BaseFormApi; + +/** + * @author Donghuang
+ * Nov 15, 2019 21:27:22 + */ +public interface SessionForm extends BaseFormApi { + /** + * get tenant id + * @return tenant id + */ + Integer getTenantId(); + + /** + * set tenant id + * @param tenantId tenant id + */ + void setTenantId(Integer tenantId); + + /** + * get tenant code + * @return tenant code + */ + String getTenantCode(); + + /** + * set tenant code + * @param tenantCode tenant code + */ + void setTenantCode(String tenantCode); + + /** + * get auth user + * @return auth user + */ + String getAuthUser(); + + /** + * set auth user + * @param authUser auth user + */ + void setAuthUser(String authUser); +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/TaggableForm.java b/cms/src/main/java/com/pudonghot/yo/cms/form/TaggableForm.java new file mode 100644 index 00000000..16af1d86 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/TaggableForm.java @@ -0,0 +1,20 @@ +package com.pudonghot.yo.cms.form; + +/** + * @author Donghuang
+ * Nov 15, 2019 22:44:17 + */ +public interface TaggableForm { + + /** + * get tags + * @return tags + */ + Integer[] getTags(); + + /** + * set tags + * @param tags tags + */ + void setTags(Integer[] tags); +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/create/BatchCreateFromNlpNodeSample.java b/cms/src/main/java/com/pudonghot/yo/cms/form/create/BatchCreateFromNlpNodeSample.java new file mode 100644 index 00000000..3b088328 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/create/BatchCreateFromNlpNodeSample.java @@ -0,0 +1,25 @@ +package com.pudonghot.yo.cms.form.create; + +import com.pudonghot.yo.cms.form.BaseCreateForm; +import com.wacai.tigon.format.annotation.Trim; +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * @author: qiushui + * @date: 2020-03-16 16:26 + */ +@Setter +@Getter +public class BatchCreateFromNlpNodeSample extends BaseCreateForm{ + @NotNull + private Integer nodeId; + @NotNull + private Integer componentId; + @Trim + @NotBlank(message = "样本不能为空") + private String sampleTexts; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormAgent.java b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormAgent.java new file mode 100644 index 00000000..9c94692b --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormAgent.java @@ -0,0 +1,36 @@ +package com.pudonghot.yo.cms.form.create; + +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.NotBlank; +import com.wacai.tigon.format.annotation.Trim; +import com.pudonghot.yo.model.domain.Agent; +import org.hibernate.validator.constraints.Range; +import com.pudonghot.yo.cms.form.TaggableForm; +import com.pudonghot.yo.cms.form.BaseCreateForm; + +/** + * @author Donghuang
+ * Nov 02, 2019 12:51:22 + */ +@Getter +@Setter +public class CreateFormAgent extends BaseCreateForm implements TaggableForm { + @NotNull + private Integer groupId; + @NotNull + private Agent.Type type; + private boolean webrtc; + @Trim + @NotBlank + private String name; + @Trim + @NotBlank + private String account; + @Range(min = 0, max = 120) + private int wrapUpTime; + private Integer[] queues; + private Integer[] tags; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormAgentGroup.java b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormAgentGroup.java new file mode 100644 index 00000000..3af6809e --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormAgentGroup.java @@ -0,0 +1,24 @@ +package com.pudonghot.yo.cms.form.create; + +import com.pudonghot.yo.model.domain.AgentGroup; +import lombok.Getter; +import lombok.Setter; +import javax.validation.constraints.NotBlank; +import com.wacai.tigon.format.annotation.Trim; +import com.pudonghot.yo.cms.form.TaggableForm; +import com.pudonghot.yo.cms.form.BaseCreateForm; + +/** + * @author Donghuang
+ * Nov 16, 2019 11:27:56 + */ +@Getter +@Setter +public class CreateFormAgentGroup extends BaseCreateForm implements TaggableForm { + @Trim + @NotBlank + private String name; + private AgentGroup.PrivacyLevel privacyLevel; + private Integer[] trunkStrategies; + private Integer[] tags; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormAreaCode.java b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormAreaCode.java new file mode 100644 index 00000000..96322ff5 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormAreaCode.java @@ -0,0 +1,25 @@ +package com.pudonghot.yo.cms.form.create; + +import com.pudonghot.yo.cms.form.BaseCreateForm; +import com.pudonghot.yo.cms.form.TaggableForm; +import lombok.Getter; +import lombok.Setter; +import javax.validation.constraints.NotBlank; +import com.wacai.tigon.format.annotation.Trim; + +/** + * @author Donghuang
+ * Nov 15, 2019 19:12:48 + */ +@Getter +@Setter +public class CreateFormAreaCode extends BaseCreateForm implements TaggableForm { + + @Trim + @NotBlank + private String code; + @Trim + @NotBlank + private String city; + private Integer[] tags; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormAuthPermission.java b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormAuthPermission.java new file mode 100644 index 00000000..cc4c18d0 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormAuthPermission.java @@ -0,0 +1,24 @@ +package com.pudonghot.yo.cms.form.create; + +import lombok.Getter; +import lombok.Setter; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Pattern; +import com.wacai.tigon.format.annotation.Trim; +import com.pudonghot.yo.cms.form.TaggableForm; +import com.pudonghot.yo.cms.form.BaseCreateForm; + +/** + * @author Donghuang
+ * Dec 24, 2019 16:48:12 + */ +@Getter +@Setter +public class CreateFormAuthPermission extends BaseCreateForm implements TaggableForm { + @Trim + @NotBlank + @Pattern(regexp = "^\\w+$") + private String permission; + private Integer[] roles; + private Integer[] tags; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormAuthRole.java b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormAuthRole.java new file mode 100644 index 00000000..6bf01ba2 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormAuthRole.java @@ -0,0 +1,23 @@ +package com.pudonghot.yo.cms.form.create; + +import com.pudonghot.yo.cms.form.TaggableForm; +import lombok.Getter; +import lombok.Setter; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.NotBlank; +import com.wacai.tigon.format.annotation.Trim; +import com.pudonghot.yo.cms.form.BaseCreateForm; + +/** + * @author Donghuang
+ * Dec 24, 2019 16:43:31 + */ +@Getter +@Setter +public class CreateFormAuthRole extends BaseCreateForm implements TaggableForm { + @Trim + @NotBlank + @Pattern(regexp = "^\\w+$") + private String role; + private Integer[] tags; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormAuthUser.java b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormAuthUser.java new file mode 100644 index 00000000..82f8669b --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormAuthUser.java @@ -0,0 +1,27 @@ +package com.pudonghot.yo.cms.form.create; + +import lombok.Getter; +import lombok.Setter; +import javax.validation.constraints.NotBlank; +import com.wacai.tigon.format.annotation.Trim; +import com.pudonghot.yo.cms.form.TaggableForm; +import com.pudonghot.yo.cms.form.BaseCreateForm; + +/** + * @author Donghuang
+ * Dec 24, 2019 16:43:24 + */ +@Getter +@Setter +public class CreateFormAuthUser extends BaseCreateForm implements TaggableForm { + @Trim + @NotBlank + private String name; + @Trim + @NotBlank + private String account; + private boolean admin; + private Integer[] tenants; + private Integer[] roles; + private Integer[] tags; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormCallingList.java b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormCallingList.java new file mode 100644 index 00000000..e6975774 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormCallingList.java @@ -0,0 +1,31 @@ +package com.pudonghot.yo.cms.form.create; + +import com.pudonghot.yo.cms.form.BaseCreateForm; +import com.pudonghot.yo.model.domain.CallingList; +import com.wacai.tigon.format.annotation.Trim; +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * @author bingpo + * @date 2020/3/16 下午3:46 + */ +@Getter +@Setter +public class CreateFormCallingList extends BaseCreateForm { + @NotNull + private Integer campaignId; + @Trim + @NotBlank + private String phone; + private CallingList.Status status; + private Integer dailyFrom; + private Integer dailyTo; + private String taskKey; + private String recordKey; + private String recordData; + private String attachedData; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormCampaign.java b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormCampaign.java new file mode 100644 index 00000000..73d497d4 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormCampaign.java @@ -0,0 +1,36 @@ +package com.pudonghot.yo.cms.form.create; + +import lombok.Getter; +import lombok.Setter; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.NotBlank; +import com.wacai.tigon.format.annotation.Trim; +import com.pudonghot.yo.model.domain.Campaign; +import com.pudonghot.yo.cms.form.TaggableForm; +import com.pudonghot.yo.cms.form.BaseCreateForm; + +/** + * @author Donghuang
+ * Nov 16, 2019 11:27:56 + */ +@Getter +@Setter +public class CreateFormCampaign extends BaseCreateForm implements TaggableForm { + @Trim + @NotBlank + private String name; + @NotNull + private Campaign.Type type; + @Trim + @NotBlank + private String target; + private Integer[] trunkStrategies; + private Integer[] tags; + private Boolean robotConfig; + private Boolean robotFetchData; + private String robotResultTopic; + private Integer robotStateMachineId; + private Boolean robotFetchLocal; + private String robotFetchUrl; + private String robotFetchHeader; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormGateway.java b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormGateway.java new file mode 100644 index 00000000..d3426493 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormGateway.java @@ -0,0 +1,118 @@ +package com.pudonghot.yo.cms.form.create; + +import com.pudonghot.yo.cms.form.BaseCreateForm; +import com.wacai.tigon.format.annotation.Trim; +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotBlank; + +/** + * @author Donghuang
+ * Nov 01, 2019 16:11:13 + */ +@Getter +@Setter +public class CreateFormGateway extends BaseCreateForm { + /** + * Gateway name + */ + @Trim + @NotBlank + private String name; + + /** + * Auth realm: *optional* same as gateway name, if blank + */ + private String realm; + + /** + * Account username *required* + */ + private String username; + + /** + * Account password *required* + */ + private String password; + + /** + * Username to use in from: *optional* same as username, if blank + */ + private String fromUser; + + /** + * Domain to use in from: *optional* same as realm, if blank + */ + private String fromDomain; + + /** + * Extension for inbound calls: *optional* same as username, if blank + */ + private String extension; + + /** + * Proxy host: *optional* same as realm, if blank + */ + private String proxy; + + /** + * Send register to this proxy: *optional* same as proxy, if blank + */ + private String registerProxy; + + /** + * Expire in seconds: *optional* 3600, if blank + */ + private String expireSeconds; + + /** + * Do not register + */ + private String register; + + /** + * Which transport to use for register + */ + private String registerTransport; + + /** + * how many seconds before a retry when a failure or timeout occurs + */ + private String retrySeconds; + + /** + * Use the callerid of an inbound call in the from field on outbound calls via this gateway + */ + private String callerIdInFrom; + + /** + * Extra sip params to send in the contact + */ + private String contactParams; + + /** + * Put the extension in the contact + */ + private String extensionInContact; + + /** + * Send an options ping every x seconds, failure will unregister and/or mark it down + */ + private String ping; + + /** + * CID type + */ + private String cidType; + + /** + * RFC5626 : Abilitazione rfc5626 + */ + private String rfc5626; + + /** + * RFC5626 : extra sip params to send in the contact + */ + private String regId; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormQueue.java b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormQueue.java new file mode 100644 index 00000000..e7a5e310 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormQueue.java @@ -0,0 +1,17 @@ +package com.pudonghot.yo.cms.form.create; + +import lombok.Getter; +import lombok.Setter; +import javax.validation.constraints.NotBlank; +import com.pudonghot.yo.cms.form.BaseCreateForm; + +/** + * @author Donghuang
+ * Nov 16, 2019 12:40:42 + */ +@Getter +@Setter +public class CreateFormQueue extends BaseCreateForm { + @NotBlank + private String name; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormRobotAgent.java b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormRobotAgent.java new file mode 100644 index 00000000..fc6a3fdb --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormRobotAgent.java @@ -0,0 +1,18 @@ +package com.pudonghot.yo.cms.form.create; + +import com.pudonghot.yo.cms.form.BaseCreateForm; +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotNull; + +/** + * @author qiushui
+ */ +@Getter +@Setter +public class CreateFormRobotAgent extends BaseCreateForm { + @NotNull + private Integer[] agentIds; + +} 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 new file mode 100644 index 00000000..977d4598 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormSequence.java @@ -0,0 +1,29 @@ +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 com.wacai.tigon.format.annotation.Trim; +import com.pudonghot.yo.cms.form.BaseCreateForm; + +/** + * @author Donghuang
+ * Nov 02, 2019 13:44:03 + */ +@Getter +@Setter +public class CreateFormSequence extends BaseCreateForm { + @Trim + @NotBlank + @Pattern(regexp = "^\\w+$", message = "序列名必须是字母数字下划线构成") + private String name; + @Min(1) + @NotNull + private Long initVal; + @Min(1) + @NotNull + private Long step; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormTag.java b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormTag.java new file mode 100644 index 00000000..b5f69391 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormTag.java @@ -0,0 +1,26 @@ +package com.pudonghot.yo.cms.form.create; + +import lombok.Getter; +import lombok.Setter; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.NotBlank; +import com.pudonghot.yo.model.domain.Tag; +import com.wacai.tigon.format.annotation.Trim; +import com.pudonghot.yo.cms.form.BaseCreateForm; + +/** + * @author Donghuang
+ * Nov 01, 2019 16:11:13 + */ +@Getter +@Setter +public class CreateFormTag extends BaseCreateForm { + @Trim + @NotBlank + private String tag; + @Trim + @NotBlank + private String bgcolor; + @NotNull + private Tag.Scope scope; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormTelecomVendor.java b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormTelecomVendor.java new file mode 100644 index 00000000..55f0cdd1 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormTelecomVendor.java @@ -0,0 +1,19 @@ +package com.pudonghot.yo.cms.form.create; + +import com.pudonghot.yo.cms.form.BaseCreateForm; +import lombok.Getter; +import lombok.Setter; +import javax.validation.constraints.NotBlank; +import com.wacai.tigon.format.annotation.Trim; + +/** + * @author Donghuang
+ * Nov 01, 2019 16:11:13 + */ +@Getter +@Setter +public class CreateFormTelecomVendor extends BaseCreateForm { + @Trim + @NotBlank + private String name; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormTenant.java b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormTenant.java new file mode 100644 index 00000000..572eb626 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormTenant.java @@ -0,0 +1,25 @@ +package com.pudonghot.yo.cms.form.create; + +import lombok.Getter; +import lombok.Setter; +import javax.validation.constraints.NotBlank; +import com.wacai.tigon.format.annotation.Trim; +import com.pudonghot.yo.cms.form.BaseCreateForm; + +/** + * @author Donghuang
+ * Nov 01, 2019 16:11:13 + */ +@Getter +@Setter +public class CreateFormTenant extends BaseCreateForm { + @Trim + @NotBlank + private String name; + @Trim + @NotBlank + private String code; + @Trim + @NotBlank + private String realm; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormTrunk.java b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormTrunk.java new file mode 100644 index 00000000..f7873eb7 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormTrunk.java @@ -0,0 +1,79 @@ +package com.pudonghot.yo.cms.form.create; + +import com.pudonghot.yo.cms.form.TaggableForm; +import lombok.Getter; +import lombok.Setter; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.NotBlank; +import com.wacai.tigon.format.annotation.Trim; +import com.pudonghot.yo.model.domain.Trunk; +import com.pudonghot.yo.cms.form.BaseCreateForm; + +/** + * @author Donghuang
+ * Nov 02, 2019 12:29:33 + */ +@Getter +@Setter +public class CreateFormTrunk extends BaseCreateForm implements TaggableForm { + + /** + * 语音厂商ID + */ + @NotNull + private Integer telecomVendorId; + + /** + * 网关ID + */ + @NotNull + private Integer gatewayId; + + /** + * 网关(SBC)前缀 + */ + @Trim + private String gatewayPrefix; + + /** + * 主叫(显示)号码 + */ + @Trim + @NotBlank + private String callerNumbers; + + /** + * CPN截除区号 + */ + private boolean cpnWithOutAreaCode; + + /** + * 外埠号码加拨0 + */ + private boolean ocAddZero; + + /** + * 进线目标类型 + */ + private Trunk.InboundTargetType inboundTargetType; + + /** + * 进线目标 + */ + private Integer inboundTarget; + + /** + * 标签 + */ + private Integer[] tags; + + /** + * 策略 + */ + private Integer[] strategies; + + /** + * 属性 + */ + private Integer[] attrs; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormTrunkAttr.java b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormTrunkAttr.java new file mode 100644 index 00000000..13584775 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormTrunkAttr.java @@ -0,0 +1,21 @@ +package com.pudonghot.yo.cms.form.create; + +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotBlank; + +import com.wacai.tigon.format.annotation.Trim; +import com.pudonghot.yo.cms.form.BaseCreateForm; + +/** + * @author Donghuang
+ * Jan 17, 2020 15:24:23 + */ +@Getter +@Setter +public class CreateFormTrunkAttr extends BaseCreateForm { + @Trim + @NotBlank + private String attr; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormTrunkProhibitedAreaCode.java b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormTrunkProhibitedAreaCode.java new file mode 100644 index 00000000..e85811a6 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormTrunkProhibitedAreaCode.java @@ -0,0 +1,21 @@ +package com.pudonghot.yo.cms.form.create; + +import lombok.Getter; +import lombok.Setter; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.NotEmpty; +import com.pudonghot.yo.cms.form.BaseCreateForm; + +/** + * @author Donghuang
+ * Nov 02, 2019 12:29:33 + */ +@Getter +@Setter +public class CreateFormTrunkProhibitedAreaCode extends BaseCreateForm { + @NotNull + private Integer trunkId; + @NotNull + @NotEmpty + private String[] areaCodes; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormTrunkStrategy.java b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormTrunkStrategy.java new file mode 100644 index 00000000..15b5d649 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/create/CreateFormTrunkStrategy.java @@ -0,0 +1,19 @@ +package com.pudonghot.yo.cms.form.create; + +import lombok.Getter; +import lombok.Setter; +import javax.validation.constraints.NotBlank; +import com.pudonghot.yo.cms.form.BaseCreateForm; +import com.pudonghot.yo.model.domain.TrunkStrategy; + +/** + * @author Donghuang
+ * Nov 16, 2019 11:27:56 + */ +@Getter +@Setter +public class CreateFormTrunkStrategy extends BaseCreateForm { + @NotBlank + private String name; + private TrunkStrategy.Strategy strategy; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormAgent.java b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormAgent.java new file mode 100644 index 00000000..368b6dd8 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormAgent.java @@ -0,0 +1,33 @@ +package com.pudonghot.yo.cms.form.update; + +import lombok.Getter; +import lombok.Setter; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.NotBlank; +import com.wacai.tigon.format.annotation.Trim; +import com.pudonghot.yo.model.domain.Agent; +import org.hibernate.validator.constraints.Range; +import com.pudonghot.yo.cms.form.TaggableForm; +import com.pudonghot.yo.cms.form.BaseUpdateForm; + +/** + * @author Donghuang
+ * Nov 02, 2019 12:51:33 + */ +@Getter +@Setter +public class UpdateFormAgent extends BaseUpdateForm implements TaggableForm { + @NotNull + private Integer groupId; + @NotNull + private Agent.Type type; + private boolean webrtc; + @Trim + @NotBlank + private String name; + @Range(min = 0, max = 120) + private int wrapUpTime; + private boolean updatePassword; + private Integer[] queues; + private Integer[] tags; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormAgentGroup.java b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormAgentGroup.java new file mode 100644 index 00000000..ee2336ab --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormAgentGroup.java @@ -0,0 +1,22 @@ +package com.pudonghot.yo.cms.form.update; + +import com.pudonghot.yo.cms.form.BaseUpdateForm; +import com.pudonghot.yo.cms.form.TaggableForm; +import com.pudonghot.yo.model.domain.AgentGroup; +import lombok.Getter; +import lombok.Setter; +import javax.validation.constraints.NotBlank; + +/** + * @author Donghuang
+ * Nov 16, 2019 11:28:20 + */ +@Getter +@Setter +public class UpdateFormAgentGroup extends BaseUpdateForm implements TaggableForm { + @NotBlank + private String name; + private AgentGroup.PrivacyLevel privacyLevel; + private Integer[] trunkStrategies; + private Integer[] tags; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormAreaCode.java b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormAreaCode.java new file mode 100644 index 00000000..a31c09b6 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormAreaCode.java @@ -0,0 +1,21 @@ +package com.pudonghot.yo.cms.form.update; + +import com.pudonghot.yo.cms.form.BaseUpdateForm; +import com.pudonghot.yo.cms.form.TaggableForm; +import lombok.Getter; +import lombok.Setter; +import javax.validation.constraints.NotBlank; +import com.wacai.tigon.format.annotation.Trim; + +/** + * @author Donghuang
+ * Nov 15, 2019 19:13:24 + */ +@Getter +@Setter +public class UpdateFormAreaCode extends BaseUpdateForm implements TaggableForm { + @Trim + @NotBlank + private String city; + private Integer[] tags; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormAuthPermission.java b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormAuthPermission.java new file mode 100644 index 00000000..4edfddcd --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormAuthPermission.java @@ -0,0 +1,17 @@ +package com.pudonghot.yo.cms.form.update; + +import com.pudonghot.yo.cms.form.BaseUpdateForm; +import com.pudonghot.yo.cms.form.TaggableForm; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Donghuang
+ * Dec 24, 2019 16:48:29 + */ +@Getter +@Setter +public class UpdateFormAuthPermission extends BaseUpdateForm implements TaggableForm { + private Integer[] tags; + private Integer[] roles; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormAuthRole.java b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormAuthRole.java new file mode 100644 index 00000000..a590756e --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormAuthRole.java @@ -0,0 +1,16 @@ +package com.pudonghot.yo.cms.form.update; + +import com.pudonghot.yo.cms.form.BaseUpdateForm; +import com.pudonghot.yo.cms.form.TaggableForm; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Donghuang
+ * Dec 24, 2019 16:46:38 + */ +@Getter +@Setter +public class UpdateFormAuthRole extends BaseUpdateForm implements TaggableForm { + private Integer[] tags; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormAuthUser.java b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormAuthUser.java new file mode 100644 index 00000000..84751be3 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormAuthUser.java @@ -0,0 +1,24 @@ +package com.pudonghot.yo.cms.form.update; + +import com.pudonghot.yo.cms.form.BaseUpdateForm; +import com.pudonghot.yo.cms.form.TaggableForm; +import lombok.Getter; +import lombok.Setter; +import javax.validation.constraints.NotBlank; +import com.wacai.tigon.format.annotation.Trim; + +/** + * @author Donghuang
+ * Nov 02, 2019 12:51:33 + */ +@Getter +@Setter +public class UpdateFormAuthUser extends BaseUpdateForm implements TaggableForm { + @Trim + @NotBlank + private String name; + private boolean admin; + private Integer[] tenants; + private Integer[] roles; + private Integer[] tags; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormCallingList.java b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormCallingList.java new file mode 100644 index 00000000..bb7bec22 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormCallingList.java @@ -0,0 +1,37 @@ +package com.pudonghot.yo.cms.form.update; + +import com.pudonghot.yo.cms.form.BaseUpdateForm; +import com.pudonghot.yo.model.domain.CallingList; +import com.wacai.tigon.format.annotation.Trim; +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.util.Date; + +/** + * @author bingpo + * @date 2020/3/16 下午3:46 + */ +@Getter +@Setter +public class UpdateFormCallingList extends BaseUpdateForm { + @NotNull + private Integer campaignId; + @Trim + @NotBlank + private String phone; + private CallingList.Status status; + private Integer dailyFrom; + private Integer dailyTo; + private String callUuid; + private Date callStartTime; + private Date callEstablishedTime; + private Date callEndTime; + private String callResult; + private String taskKey; + private String recordKey; + private String recordData; + private String attachedData; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormCampaign.java b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormCampaign.java new file mode 100644 index 00000000..53b87c59 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormCampaign.java @@ -0,0 +1,33 @@ +package com.pudonghot.yo.cms.form.update; + +import com.pudonghot.yo.cms.form.BaseUpdateForm; +import com.pudonghot.yo.cms.form.TaggableForm; +import lombok.Getter; +import lombok.Setter; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.NotBlank; +import com.pudonghot.yo.model.domain.Campaign; + +/** + * @author Donghuang
+ * Nov 16, 2019 11:28:20 + */ +@Getter +@Setter +public class UpdateFormCampaign extends BaseUpdateForm implements TaggableForm { + @NotBlank + private String name; + @NotNull + private Campaign.Type type; + @NotBlank + private String target; + private Integer[] trunkStrategies; + private Integer[] tags; + private Boolean robotConfig; + private Boolean robotFetchData; + private String robotResultTopic; + private Integer robotStateMachineId; + private Boolean robotFetchLocal; + private String robotFetchUrl; + private String robotFetchHeader; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormGateway.java b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormGateway.java new file mode 100644 index 00000000..a2d877d1 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormGateway.java @@ -0,0 +1,117 @@ +package com.pudonghot.yo.cms.form.update; + +import com.pudonghot.yo.cms.form.BaseUpdateForm; +import lombok.Getter; +import lombok.Setter; +import javax.validation.constraints.NotBlank; +import com.wacai.tigon.format.annotation.Trim; + +/** + * @author Donghuang
+ * Nov 01, 2019 16:11:46 + */ +@Getter +@Setter +public class UpdateFormGateway extends BaseUpdateForm { + /** + * Gateway name + */ + @Trim + @NotBlank + private String name; + + /** + * Auth realm: *optional* same as gateway name, if blank + */ + private String realm; + + /** + * Account username *required* + */ + private String username; + + /** + * Account password *required* + */ + private String password; + + /** + * Username to use in from: *optional* same as username, if blank + */ + private String fromUser; + + /** + * Domain to use in from: *optional* same as realm, if blank + */ + private String fromDomain; + + /** + * Extension for inbound calls: *optional* same as username, if blank + */ + private String extension; + + /** + * Proxy host: *optional* same as realm, if blank + */ + private String proxy; + + /** + * Send register to this proxy: *optional* same as proxy, if blank + */ + private String registerProxy; + + /** + * Expire in seconds: *optional* 3600, if blank + */ + private String expireSeconds; + + /** + * Do not register + */ + private String register; + + /** + * Which transport to use for register + */ + private String registerTransport; + + /** + * how many seconds before a retry when a failure or timeout occurs + */ + private String retrySeconds; + + /** + * Use the callerid of an inbound call in the from field on outbound calls via this gateway + */ + private String callerIdInFrom; + + /** + * Extra sip params to send in the contact + */ + private String contactParams; + + /** + * Put the extension in the contact + */ + private String extensionInContact; + + /** + * Send an options ping every x seconds, failure will unregister and/or mark it down + */ + private String ping; + + /** + * CID type + */ + private String cidType; + + /** + * RFC5626 : Abilitazione rfc5626 + */ + private String rfc5626; + + /** + * RFC5626 : extra sip params to send in the contact + */ + private String regId; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormQueue.java b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormQueue.java new file mode 100644 index 00000000..b5c77b76 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormQueue.java @@ -0,0 +1,17 @@ +package com.pudonghot.yo.cms.form.update; + +import com.pudonghot.yo.cms.form.BaseUpdateForm; +import lombok.Getter; +import lombok.Setter; +import javax.validation.constraints.NotBlank; + +/** + * @author Donghuang
+ * Nov 16, 2019 12:41:14 + */ +@Getter +@Setter +public class UpdateFormQueue extends BaseUpdateForm { + @NotBlank + private String name; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormRobotAgent.java b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormRobotAgent.java new file mode 100644 index 00000000..6013117f --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormRobotAgent.java @@ -0,0 +1,14 @@ +package com.pudonghot.yo.cms.form.update; + +import com.pudonghot.yo.cms.form.BaseUpdateForm; +import lombok.Getter; +import lombok.Setter; + + +/** + * @author qiushui
+ */ +@Getter +@Setter +public class UpdateFormRobotAgent extends BaseUpdateForm { +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormSequence.java b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormSequence.java new file mode 100644 index 00000000..1d87e571 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormSequence.java @@ -0,0 +1,22 @@ +package com.pudonghot.yo.cms.form.update; + +import com.pudonghot.yo.cms.form.BaseUpdateForm; +import lombok.Getter; +import lombok.Setter; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.NotBlank; + +/** + * @author Donghuang
+ * Nov 02, 2019 13:43:56 + */ +@Getter +@Setter +public class UpdateFormSequence extends BaseUpdateForm { + @NotBlank + private String name; + @Min(1) + @NotNull + private Long step; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormTag.java b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormTag.java new file mode 100644 index 00000000..2c93361e --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormTag.java @@ -0,0 +1,26 @@ +package com.pudonghot.yo.cms.form.update; + +import com.pudonghot.yo.cms.form.BaseUpdateForm; +import lombok.Getter; +import lombok.Setter; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.NotBlank; +import com.pudonghot.yo.model.domain.Tag; +import com.wacai.tigon.format.annotation.Trim; + +/** + * @author Donghuang
+ * Nov 01, 2019 16:11:46 + */ +@Getter +@Setter +public class UpdateFormTag extends BaseUpdateForm { + @Trim + @NotBlank + private String tag; + @Trim + @NotBlank + private String bgcolor; + @NotNull + private Tag.Scope scope; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormTelecomVendor.java b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormTelecomVendor.java new file mode 100644 index 00000000..b7982edf --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormTelecomVendor.java @@ -0,0 +1,19 @@ +package com.pudonghot.yo.cms.form.update; + +import com.pudonghot.yo.cms.form.BaseUpdateForm; +import lombok.Getter; +import lombok.Setter; +import javax.validation.constraints.NotBlank; +import com.wacai.tigon.format.annotation.Trim; + +/** + * @author Donghuang
+ * Nov 01, 2019 16:11:46 + */ +@Getter +@Setter +public class UpdateFormTelecomVendor extends BaseUpdateForm { + @Trim + @NotBlank + private String name; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormTenant.java b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormTenant.java new file mode 100644 index 00000000..4de90bbf --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormTenant.java @@ -0,0 +1,20 @@ +package com.pudonghot.yo.cms.form.update; + +import com.pudonghot.yo.cms.form.BaseUpdateForm; +import lombok.Getter; +import lombok.Setter; +import javax.validation.constraints.NotBlank; +import com.wacai.tigon.format.annotation.Trim; + +/** + * @author Donghuang
+ * Nov 01, 2019 16:11:46 + */ +@Getter +@Setter +public class UpdateFormTenant extends BaseUpdateForm { + @Trim + @NotBlank + private String name; + private boolean renewAccessSecret; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormTrunk.java b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormTrunk.java new file mode 100644 index 00000000..903b47ab --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormTrunk.java @@ -0,0 +1,58 @@ +package com.pudonghot.yo.cms.form.update; + +import com.pudonghot.yo.cms.form.BaseUpdateForm; +import com.pudonghot.yo.cms.form.TaggableForm; +import lombok.Getter; +import lombok.Setter; +import javax.validation.constraints.NotNull; +import com.pudonghot.yo.model.domain.Trunk; + +/** + * @author Donghuang
+ * Nov 01, 2019 16:11:46 + */ +@Getter +@Setter +public class UpdateFormTrunk extends BaseUpdateForm implements TaggableForm { + + /** + * 网关ID + */ + @NotNull + private Integer gatewayId; + + /** + * 网关(SBC)前缀 + */ + private String gatewayPrefix; + + /** + * 外埠号码加拨0 + */ + private boolean ocAddZero; + + /** + * 进线目标类型 + */ + private Trunk.InboundTargetType inboundTargetType; + + /** + * 进线目标 + */ + private Integer inboundTarget; + + /** + * 标签 + */ + private Integer[] tags; + + /** + * 策略 + */ + private Integer[] strategies; + + /** + * 属性 + */ + private Integer[] attrs; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormTrunkAttr.java b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormTrunkAttr.java new file mode 100644 index 00000000..e9c91450 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormTrunkAttr.java @@ -0,0 +1,20 @@ +package com.pudonghot.yo.cms.form.update; + +import com.pudonghot.yo.cms.form.BaseUpdateForm; +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotBlank; +import com.wacai.tigon.format.annotation.Trim; + +/** + * @author Donghuang
+ * Jan 17, 2020 15:24:15 + */ +@Getter +@Setter +public class UpdateFormTrunkAttr extends BaseUpdateForm { + @Trim + @NotBlank + private String attr; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormTrunkStrategy.java b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormTrunkStrategy.java new file mode 100644 index 00000000..74b3baf9 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/form/update/UpdateFormTrunkStrategy.java @@ -0,0 +1,21 @@ +package com.pudonghot.yo.cms.form.update; + +import lombok.Getter; +import lombok.Setter; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.NotBlank; +import com.pudonghot.yo.cms.form.BaseUpdateForm; +import com.pudonghot.yo.model.domain.TrunkStrategy; + +/** + * @author Donghuang
+ * Nov 16, 2019 11:28:20 + */ +@Getter +@Setter +public class UpdateFormTrunkStrategy extends BaseUpdateForm { + @NotBlank + private String name; + @NotNull + private TrunkStrategy.Strategy strategy; +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/AgentGroupService.java b/cms/src/main/java/com/pudonghot/yo/cms/service/AgentGroupService.java new file mode 100644 index 00000000..fc8d11cc --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/AgentGroupService.java @@ -0,0 +1,15 @@ +package com.pudonghot.yo.cms.service; + +import com.pudonghot.yo.cms.form.create.CreateFormAgentGroup; +import com.pudonghot.yo.model.domain.AgentGroup; +import com.pudonghot.yo.cms.form.update.UpdateFormAgentGroup; + +/** + * @author Donghuang
+ * Nov 16, 2019 11:29:12 + */ +public interface AgentGroupService + extends TaggableService { +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/AgentService.java b/cms/src/main/java/com/pudonghot/yo/cms/service/AgentService.java new file mode 100644 index 00000000..d80a673a --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/AgentService.java @@ -0,0 +1,23 @@ +package com.pudonghot.yo.cms.service; + +import com.pudonghot.yo.cms.form.create.CreateFormAgent; +import com.pudonghot.yo.cms.form.update.UpdateFormAgent; +import com.pudonghot.yo.model.domain.Agent; + +/** + * @author Donghuang
+ * Nov 02, 2019 12:52:34 + */ +public interface AgentService + extends TaggableService { + + /** + * find agent of domain + * @param domain domain + * @param agent agent + * @return agent + */ + Agent findOfDomain(String domain, String agent); +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/AreaCodeService.java b/cms/src/main/java/com/pudonghot/yo/cms/service/AreaCodeService.java new file mode 100644 index 00000000..9dcb23b7 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/AreaCodeService.java @@ -0,0 +1,39 @@ +package com.pudonghot.yo.cms.service; + +import java.util.Set; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.NotBlank; + +import com.pudonghot.yo.cms.form.create.CreateFormAreaCode; +import com.pudonghot.yo.cms.form.update.UpdateFormAreaCode; +import com.pudonghot.yo.model.domain.AreaCode; + +/** + * @author Donghuang
+ * Jan 09, 2018 19:44:55 + */ +public interface AreaCodeService + extends TaggableService { + + /** + * check area code is valid + * @param areaCode area code + * @return true if area code is valid + */ + boolean isValid(@NotBlank @Pattern(regexp = "^\\d{3,4}") String areaCode); + + /** + * get area code city + * @param areaCode area code + * @return city + */ + String getCity(@NotBlank @Pattern(regexp = "^\\d{3,4}") String areaCode); + + /** + * get codes + * @return codes + */ + Set getCodes(); +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/AuthPermissionService.java b/cms/src/main/java/com/pudonghot/yo/cms/service/AuthPermissionService.java new file mode 100644 index 00000000..d9a93662 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/AuthPermissionService.java @@ -0,0 +1,24 @@ +package com.pudonghot.yo.cms.service; + +import com.pudonghot.yo.cms.form.create.CreateFormAuthPermission; +import com.pudonghot.yo.cms.form.update.UpdateFormAuthPermission; +import com.pudonghot.yo.model.domain.AuthPermission; + +import java.util.List; + +/** + * @author Donghuang
+ * Nov 02, 2019 12:52:34 + */ +public interface AuthPermissionService + extends TaggableService { + + /** + * list permissions of auth user + * @param account account + * @return permissions + */ + List listOfUser(String account); +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/AuthRoleService.java b/cms/src/main/java/com/pudonghot/yo/cms/service/AuthRoleService.java new file mode 100644 index 00000000..e0108f5b --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/AuthRoleService.java @@ -0,0 +1,24 @@ +package com.pudonghot.yo.cms.service; + +import java.util.List; + +import com.pudonghot.yo.cms.form.create.CreateFormAuthRole; +import com.pudonghot.yo.model.domain.AuthRole; +import com.pudonghot.yo.cms.form.update.UpdateFormAuthRole; + +/** + * @author Donghuang
+ * Dec 24, 2019 17:02:46 + */ +public interface AuthRoleService + extends TaggableService { + + /** + * list roles of user + * @param account user account + * @return roles + */ + List listOfUser(String account); +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/AuthUserService.java b/cms/src/main/java/com/pudonghot/yo/cms/service/AuthUserService.java new file mode 100644 index 00000000..9d480d43 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/AuthUserService.java @@ -0,0 +1,15 @@ +package com.pudonghot.yo.cms.service; + +import com.pudonghot.yo.cms.form.create.CreateFormAuthUser; +import com.pudonghot.yo.model.domain.AuthUser; +import com.pudonghot.yo.cms.form.update.UpdateFormAuthUser; + +/** + * @author Donghuang
+ * Dec 24, 2019 16:50:15 + */ +public interface AuthUserService + extends TaggableService { +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/CallDetailRecordService.java b/cms/src/main/java/com/pudonghot/yo/cms/service/CallDetailRecordService.java new file mode 100644 index 00000000..707a4882 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/CallDetailRecordService.java @@ -0,0 +1,12 @@ +package com.pudonghot.yo.cms.service; + +import com.pudonghot.yo.model.domain.CallDetailRecord; +import com.wacai.tigon.service.BaseQueryService; + +/** + * @author bingpo + * @date 2020/3/30 下午3:23 + */ +public interface CallDetailRecordService extends BaseQueryService { + +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/CallingListService.java b/cms/src/main/java/com/pudonghot/yo/cms/service/CallingListService.java new file mode 100644 index 00000000..497d7609 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/CallingListService.java @@ -0,0 +1,16 @@ +package com.pudonghot.yo.cms.service; + +import com.pudonghot.yo.cms.form.create.CreateFormCallingList; +import com.pudonghot.yo.cms.form.update.UpdateFormCallingList; +import com.pudonghot.yo.model.domain.CallingList; +import com.wacai.tigon.service.BaseCrudByFormService; + +/** + * @author bingpo + * @date 2020/3/16 下午3:46 + */ +public interface CallingListService + extends BaseCrudByFormService { +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/CampaignService.java b/cms/src/main/java/com/pudonghot/yo/cms/service/CampaignService.java new file mode 100644 index 00000000..e4a66fff --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/CampaignService.java @@ -0,0 +1,15 @@ +package com.pudonghot.yo.cms.service; + +import com.pudonghot.yo.cms.form.create.CreateFormCampaign; +import com.pudonghot.yo.model.domain.Campaign; +import com.pudonghot.yo.cms.form.update.UpdateFormCampaign; + +/** + * @author Donghuang
+ * Jan 02, 2020 16:44:02 + */ +public interface CampaignService + extends TaggableService { +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/GatewayService.java b/cms/src/main/java/com/pudonghot/yo/cms/service/GatewayService.java new file mode 100644 index 00000000..211e2a68 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/GatewayService.java @@ -0,0 +1,17 @@ +package com.pudonghot.yo.cms.service; + +import com.pudonghot.yo.cms.form.create.CreateFormGateway; +import com.pudonghot.yo.cms.form.update.UpdateFormGateway; +import com.pudonghot.yo.model.domain.Gateway; +import com.wacai.tigon.service.BaseCrudByFormService; + +/** + * @author Donghuang
+ * Nov 02, 2019 09:58:50 + */ +public interface GatewayService + extends BaseCrudByFormService { +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/QueueService.java b/cms/src/main/java/com/pudonghot/yo/cms/service/QueueService.java new file mode 100644 index 00000000..6ba297b2 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/QueueService.java @@ -0,0 +1,17 @@ +package com.pudonghot.yo.cms.service; + +import com.pudonghot.yo.cms.form.create.CreateFormQueue; +import com.pudonghot.yo.cms.form.update.UpdateFormQueue; +import com.pudonghot.yo.model.domain.Queue; +import com.wacai.tigon.service.BaseCrudByFormService; + +/** + * @author Donghuang
+ * Nov 16, 2019 12:46:17 + */ +public interface QueueService + extends BaseCrudByFormService { +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/RobotAgentService.java b/cms/src/main/java/com/pudonghot/yo/cms/service/RobotAgentService.java new file mode 100644 index 00000000..99a85e2c --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/RobotAgentService.java @@ -0,0 +1,18 @@ +package com.pudonghot.yo.cms.service; + +import com.pudonghot.yo.cms.form.create.CreateFormRobotAgent; +import com.pudonghot.yo.cms.form.update.UpdateFormRobotAgent; +import com.pudonghot.yo.model.domain.RobotAgent; +import com.wacai.tigon.service.BaseCrudByFormService; + +/** + * @author: qiushui + * @date: 2020-02-27 17:36 + */ +public interface RobotAgentService extends BaseCrudByFormService { + + void batchCreate(CreateFormRobotAgent form); +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/RobotCallResultService.java b/cms/src/main/java/com/pudonghot/yo/cms/service/RobotCallResultService.java new file mode 100644 index 00000000..622d03ab --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/RobotCallResultService.java @@ -0,0 +1,17 @@ +package com.pudonghot.yo.cms.service; + +import com.pudonghot.yo.cms.form.BaseCreateForm; +import com.pudonghot.yo.cms.form.BaseUpdateForm; +import com.pudonghot.yo.model.domain.RobotCallResult; +import com.wacai.tigon.service.BaseCrudByFormService; + +/** + * @author: qiushui + * @date: 2020-02-27 17:36 + */ +public interface RobotCallResultService extends BaseCrudByFormService { + +} 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 new file mode 100644 index 00000000..41f30e59 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/SequenceService.java @@ -0,0 +1,25 @@ +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; + +/** + * @author Donghuang
+ * Nov 02, 2019 12:28:36 + */ +public interface SequenceService + extends BaseCrudByFormService { + + /** + * next seq val + * @param tenantId tenant id + * @param name seq name + * @return next seq val + */ + Long nextVal(Integer tenantId, String name); +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/TagService.java b/cms/src/main/java/com/pudonghot/yo/cms/service/TagService.java new file mode 100644 index 00000000..bfa0fe52 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/TagService.java @@ -0,0 +1,17 @@ +package com.pudonghot.yo.cms.service; + +import com.pudonghot.yo.cms.form.create.CreateFormTag; +import com.pudonghot.yo.cms.form.update.UpdateFormTag; +import com.pudonghot.yo.model.domain.Tag; +import com.wacai.tigon.service.BaseCrudByFormService; + +/** + * @author Donghuang
+ * Nov 14, 2019 12:03:19 + */ +public interface TagService + extends BaseCrudByFormService { +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/TaggableService.java b/cms/src/main/java/com/pudonghot/yo/cms/service/TaggableService.java new file mode 100644 index 00000000..eee29bdb --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/TaggableService.java @@ -0,0 +1,18 @@ +package com.pudonghot.yo.cms.service; + +import com.pudonghot.yo.cms.form.SessionForm; +import com.pudonghot.yo.cms.form.TaggableForm; +import com.wacai.tigon.form.FormCreateApi; +import com.wacai.tigon.form.FormUpdateApi; +import com.wacai.tigon.service.BaseCrudByFormService; +import com.pudonghot.yo.model.domain.TaggableDomain; + +/** + * @author Donghuang
+ * Nov 16, 2019 10:06:43 + */ +public interface TaggableService & SessionForm & TaggableForm> + extends BaseCrudByFormService { +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/TelecomVendorService.java b/cms/src/main/java/com/pudonghot/yo/cms/service/TelecomVendorService.java new file mode 100644 index 00000000..1205ba8b --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/TelecomVendorService.java @@ -0,0 +1,17 @@ +package com.pudonghot.yo.cms.service; + +import com.pudonghot.yo.cms.form.create.CreateFormTelecomVendor; +import com.pudonghot.yo.cms.form.update.UpdateFormTelecomVendor; +import com.wacai.tigon.service.BaseCrudByFormService; +import com.pudonghot.yo.model.domain.TelecomVendor; + +/** + * @author Donghuang
+ * Nov 01, 2019 22:48:10 + */ +public interface TelecomVendorService + extends BaseCrudByFormService { +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/TenantService.java b/cms/src/main/java/com/pudonghot/yo/cms/service/TenantService.java new file mode 100644 index 00000000..1700c690 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/TenantService.java @@ -0,0 +1,50 @@ +package com.pudonghot.yo.cms.service; + +import java.util.List; +import javax.validation.constraints.NotBlank; + +import com.pudonghot.yo.cms.form.create.CreateFormTenant; +import com.pudonghot.yo.model.ValueTextModel; +import com.pudonghot.yo.model.domain.Tenant; +import com.wacai.tigon.service.BaseCrudByFormService; +import com.pudonghot.yo.cms.form.update.UpdateFormTenant; + +/** + * @author Donghuang
+ * Oct 26, 2019 15:59:44 + */ +public interface TenantService extends BaseCrudByFormService { + + /** + * switch to tenant + * @param tenant tenant + */ + void switchTo(@NotBlank String account, @NotBlank String tenant); + + /** + * return account current bu + * @param account account + * @return current bu or null + */ + ValueTextModel current(@NotBlank String account); + + /** + * clear account bu + * @param account account + * @return current bu or null + */ + ValueTextModel clear(@NotBlank String account); + + /** + * list tenants of account + * @param account account + * @return tenants + */ + List> listOfUser(@NotBlank String account); + + /** + * list tenants of admin + * @return tenants + */ + List> listOfAdmin(); +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/TrunkAttrService.java b/cms/src/main/java/com/pudonghot/yo/cms/service/TrunkAttrService.java new file mode 100644 index 00000000..eb99da63 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/TrunkAttrService.java @@ -0,0 +1,17 @@ +package com.pudonghot.yo.cms.service; + +import com.pudonghot.yo.cms.form.create.CreateFormTrunkAttr; +import com.pudonghot.yo.model.domain.TrunkAttr; +import com.wacai.tigon.service.BaseCrudByFormService; +import com.pudonghot.yo.cms.form.update.UpdateFormTrunkAttr; + +/** + * @author Donghuang
+ * Jan 17, 2020 15:24:50 + */ +public interface TrunkAttrService + extends BaseCrudByFormService { +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/TrunkProhibitedAreaCodeService.java b/cms/src/main/java/com/pudonghot/yo/cms/service/TrunkProhibitedAreaCodeService.java new file mode 100644 index 00000000..7d85b589 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/TrunkProhibitedAreaCodeService.java @@ -0,0 +1,17 @@ +package com.pudonghot.yo.cms.service; + +import com.pudonghot.yo.cms.form.BaseUpdateForm; +import com.pudonghot.yo.cms.form.create.CreateFormTrunkProhibitedAreaCode; +import com.wacai.tigon.service.BaseCrudByFormService; +import com.pudonghot.yo.model.domain.TrunkProhibitedAreaCode; + +/** + * @author Donghuang
+ * Nov 30, 2019 19:00:39 + */ +public interface TrunkProhibitedAreaCodeService + extends BaseCrudByFormService { +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/TrunkService.java b/cms/src/main/java/com/pudonghot/yo/cms/service/TrunkService.java new file mode 100644 index 00000000..5049e1ca --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/TrunkService.java @@ -0,0 +1,15 @@ +package com.pudonghot.yo.cms.service; + +import com.pudonghot.yo.cms.form.create.CreateFormTrunk; +import com.pudonghot.yo.model.domain.Trunk; +import com.pudonghot.yo.cms.form.update.UpdateFormTrunk; + +/** + * @author Donghuang
+ * Nov 02, 2019 12:28:36 + */ +public interface TrunkService + extends TaggableService { +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/TrunkStrategyService.java b/cms/src/main/java/com/pudonghot/yo/cms/service/TrunkStrategyService.java new file mode 100644 index 00000000..08301827 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/TrunkStrategyService.java @@ -0,0 +1,17 @@ +package com.pudonghot.yo.cms.service; + +import com.pudonghot.yo.cms.form.create.CreateFormTrunkStrategy; +import com.pudonghot.yo.cms.form.update.UpdateFormTrunkStrategy; +import com.wacai.tigon.service.BaseCrudByFormService; +import com.pudonghot.yo.model.domain.TrunkStrategy; + +/** + * @author Donghuang
+ * Nov 30, 2019 19:00:39 + */ +public interface TrunkStrategyService + extends BaseCrudByFormService { +} 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 new file mode 100644 index 00000000..8e0d0aa7 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/AgentGroupServiceImpl.java @@ -0,0 +1,121 @@ +package com.pudonghot.yo.cms.service.impl; + +import java.util.Set; + +import com.pudonghot.yo.cms.form.SessionForm; +import com.pudonghot.yo.cms.form.create.CreateFormAgentGroup; +import com.pudonghot.yo.cms.service.AgentGroupService; +import lombok.extern.slf4j.Slf4j; +import java.util.stream.Collectors; +import com.wacai.tigon.mybatis.Search; +import com.wacai.tigon.model.ViewModel; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import com.pudonghot.yo.mapper.AgentGroupMapper; +import com.pudonghot.yo.model.domain.AgentGroup; +import com.pudonghot.yo.cms.service.SequenceService; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.annotation.Autowired; +import com.pudonghot.yo.mapper.AgentGroupTrunkStrategyMapper; +import com.pudonghot.yo.model.domain.AgentGroupTrunkStrategy; +import com.pudonghot.yo.cms.form.update.UpdateFormAgentGroup; + +/** + * @author Donghuang
+ * Nov 02, 2019 12:52:47 + */ +@Slf4j +@Service +public class AgentGroupServiceImpl + extends TaggableServiceImpl + implements AgentGroupService { + + @Value("${yo.cms.agent-group.seq-name:AGENT_GROUP}") + private String seqName; + @Value("${yo.cms.agent-group.identifier-prefix:AG}") + private String identifierPrefix; + @Autowired + private SequenceService seqService; + @Autowired + private AgentGroupTrunkStrategyMapper agentGroupTrunkStrategyMapper; + + /** + * {@inheritDoc} + */ + @Override + public ViewModel create(final CreateFormAgentGroup form) { + final ViewModel vm = super.create(form); + + final AgentGroup agentGroup = vm.getData(); + final Integer[] strategies = form.getTrunkStrategies(); + if (strategies != null) { + for (final Integer strategy : strategies) { + insertTrunkStrategy(agentGroup, strategy, form); + } + } + return vm; + } + + /** + * {@inheritDoc} + */ + @Override + public ViewModel update(final UpdateFormAgentGroup form) { + final ViewModel vm = super.update(form); + final AgentGroup agentGroup = vm.getData(); + final Integer agentGroupId = agentGroup.getId(); + + final Set strategiesExisted = agentGroupTrunkStrategyMapper.list( + new Search(AgentGroupTrunkStrategy.AGENT_GROUP_ID, + agentGroupId)).stream() + .map(AgentGroupTrunkStrategy::getTrunkStrategyId) + .collect(Collectors.toSet()); + + final Pair, Set> strategiesDiff = + diff(strategiesExisted, form.getTrunkStrategies()); + + for (final Integer strategyId : strategiesDiff.getLeft()) { + insertTrunkStrategy(agentGroup, strategyId, form); + } + + if (!strategiesDiff.getRight().isEmpty()) { + agentGroupTrunkStrategyMapper.delete( + new Search(AgentGroupTrunkStrategy.AGENT_GROUP_ID, agentGroupId) + .in(AgentGroupTrunkStrategy.TRUNK_STRATEGY_ID, + strategiesDiff.getRight())); + } + return vm; + } + + /** + * {@inheritDoc} + */ + @Override + protected void beforeInsert(final AgentGroup model) { + super.beforeInsert(model); + if (StringUtils.isBlank(model.getIdentifier())) { + model.setIdentifier(identifierPrefix + + StringUtils.leftPad(String.valueOf( + seqService.nextVal(model.getTenantId(), seqName)), 6, '0')); + } + } + + protected void insertTrunkStrategy(final AgentGroup agentGroup, + final Integer strategyId, + final SessionForm form) { + final AgentGroupTrunkStrategy model = + new AgentGroupTrunkStrategy(); + model.setTenantId(form.getTenantId()); + model.setTenantCode(form.getTenantCode()); + model.setAgentGroupId(agentGroup.getId()); + model.setTrunkStrategyId(strategyId); + model.setCreatedBy(form.getAuthUser()); + + log.info("Insert agent group trunk strategy [{}].", model); + agentGroupTrunkStrategyMapper.insert(model); + } +} 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 new file mode 100644 index 00000000..a8b3a743 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/AgentServiceImpl.java @@ -0,0 +1,164 @@ +package com.pudonghot.yo.cms.service.impl; + +import java.util.Set; + +import com.pudonghot.yo.cms.form.SessionForm; +import com.pudonghot.yo.cms.form.create.CreateFormAgent; +import com.pudonghot.yo.cms.form.update.UpdateFormAgent; +import lombok.extern.slf4j.Slf4j; +import java.util.stream.Collectors; +import com.wacai.tigon.mybatis.Search; +import com.wacai.tigon.model.ViewModel; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.util.Assert; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import com.pudonghot.yo.model.domain.Agent; +import com.pudonghot.yo.mapper.AgentMapper; +import org.apache.commons.lang3.RandomStringUtils; +import com.pudonghot.yo.mapper.AgentGroupMapper; +import com.pudonghot.yo.mapper.QueueAgentMapper; +import com.pudonghot.yo.model.domain.AgentGroup; +import com.pudonghot.yo.model.domain.QueueAgent; +import com.pudonghot.yo.cms.service.AgentService; +import com.pudonghot.yo.cms.service.SequenceService; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * @author Donghuang
+ * Nov 02, 2019 12:52:47 + */ +@Slf4j +@Service +public class AgentServiceImpl + extends TaggableServiceImpl + implements AgentService { + + @Value("${yo.cms.agent.seq-name:AGENT}") + private String seqName; + @Value("${yo.cms.agent.password-length:32}") + private int passwordLength; + @Autowired + private SequenceService seqService; + @Autowired + private AgentGroupMapper agentGroupMapper; + @Autowired + private QueueAgentMapper queueAgentMapper; + + /** + * {@inheritDoc} + */ + @Override + public ViewModel create(final CreateFormAgent form) { + final ViewModel vm = super.create(form); + final Agent agent = vm.getData(); + final Integer[] queues = form.getQueues(); + if (queues != null) { + for (final Integer queue : queues) { + insertQueueAgent(agent, queue, form); + } + } + return vm; + } + + /** + * {@inheritDoc} + */ + @Override + public ViewModel update(final UpdateFormAgent form) { + final ViewModel vm = super.update(form); + final Agent agent = vm.getData(); + + final Integer agentId = agent.getId(); + + final Set queuesExisted = queueAgentMapper.list( + new Search(QueueAgent.AGENT_ID, agentId)).stream() + .map(QueueAgent::getQueueId) + .collect(Collectors.toSet()); + + final Pair, Set> queuesDiff = + diff(queuesExisted, form.getQueues()); + + for (final Integer queueId : queuesDiff.getLeft()) { + insertQueueAgent(agent, queueId, form); + } + + if (!queuesDiff.getRight().isEmpty()) { + queueAgentMapper.delete( + new Search(QueueAgent.AGENT_ID, agentId) + .in(QueueAgent.QUEUE_ID, queuesDiff.getRight())); + } + + return vm; + } + + /** + * {@inheritDoc} + */ + @Override + protected void validate(final CreateFormAgent form) { + super.validate(form); + final Integer groupId = form.getGroupId(); + final AgentGroup agentGroup = agentGroupMapper.find(groupId); + Assert.state(agentGroup != null, () -> "No agent group [" + groupId + "] found"); + Assert.state(agentGroup.getActive(), () -> "Agent group [" + groupId + "] is inactive"); + Assert.state(!mapper.exists( + new Search(Agent.TENANT_ID, form.getTenantId()) + .eq(Agent.ACCOUNT, form.getAccount())), + () -> "Agent [" + form.getAccount() + "] existed"); + } + + /** + * {@inheritDoc} + */ + @Override + protected void beforeUpdate(final UpdateFormAgent form, final Agent model) { + super.beforeUpdate(form, model); + if (form.isUpdatePassword()) { + log.info("Update agent [{}] password.", model.getAccount()); + model.setPassword(RandomStringUtils.randomAlphanumeric(passwordLength)); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void beforeInsert(final Agent model) { + super.beforeInsert(model); + if (StringUtils.isBlank(model.getAgent())) { + model.setAgent(String.valueOf(seqService.nextVal(model.getTenantId(), seqName))); + } + + if (StringUtils.isBlank(model.getPassword())) { + model.setPassword(RandomStringUtils.randomAlphanumeric(passwordLength)); + } + } + + protected void insertQueueAgent(final Agent agent, + final Integer queueId, + final SessionForm form) { + + final QueueAgent arg = new QueueAgent(); + arg.setTenantId(form.getTenantId()); + arg.setTenantCode(form.getTenantCode()); + arg.setQueueId(queueId); + arg.setAgentId(agent.getId()); + arg.setAccount(agent.getAccount()); + arg.setAgent(agent.getAgent()); + arg.setCreatedBy(form.getAuthUser()); + queueAgentMapper.insert(arg); + } + + /** + * {@inheritDoc} + */ + @Override + public Agent findOfDomain(final String domain, final String agent) { + return mapper.findByDomainAndAgent(domain, agent); + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/impl/AreaCodeServiceImpl.java b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/AreaCodeServiceImpl.java new file mode 100644 index 00000000..7f2c8d0c --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/AreaCodeServiceImpl.java @@ -0,0 +1,120 @@ +package com.pudonghot.yo.cms.service.impl; + +import java.util.*; +import java.io.IOException; +import java.io.InputStream; + +import com.pudonghot.yo.cms.form.create.CreateFormAreaCode; +import com.pudonghot.yo.cms.form.update.UpdateFormAreaCode; +import lombok.extern.slf4j.Slf4j; +import java.util.stream.Collectors; +import org.apache.commons.io.IOUtils; +import com.wacai.tigon.mybatis.Search; +import org.springframework.util.Assert; +import java.nio.charset.StandardCharsets; +import org.apache.commons.io.LineIterator; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import com.pudonghot.yo.mapper.AreaCodeMapper; +import com.pudonghot.yo.model.domain.AreaCode; +import com.pudonghot.yo.cms.service.AreaCodeService; +import org.springframework.beans.factory.InitializingBean; + +/** + * @author Donghuang
+ * May 27, 2017 15:23:02 + */ +@Slf4j +@Service +public class AreaCodeServiceImpl + extends TaggableServiceImpl + implements AreaCodeService, InitializingBean { + + /** + * {@inheritDoc} + */ + @Override + public boolean isValid(final String areaCode) { + return mapper.exists(new Search(AreaCode.CODE, areaCode) + .eq(AreaCode.ACTIVE, true)); + } + + /** + * {@inheritDoc} + */ + @Override + public String getCity(final String code) { + final AreaCode areaCode = mapper.find( + new Search(AreaCode.CODE, code) + .eq(AreaCode.ACTIVE, true)); + Assert.state(areaCode != null, + () -> "No area code [" + code + "] found"); + return areaCode.getCity(); + } + + /** + * {@inheritDoc} + */ + @Override + public Set getCodes() { + return mapper.list(new Search(AreaCode.ACTIVE, true)) + .stream().map(AreaCode::getCode) + .collect(Collectors.toSet()); + } + + /** + * {@inheritDoc} + */ + @Override + public void afterPropertiesSet() { + if (mapper.count(null) == 0) { + log.info("Init area codes."); + + try (final InputStream stream = AreaCodeServiceImpl.class + .getResourceAsStream("/areacodes.txt")) { + final LineIterator lineIt = IOUtils.lineIterator( + stream, StandardCharsets.UTF_8); + final Date now = new Date(); + while (lineIt.hasNext()) { + final String line = lineIt.next(); + if (StringUtils.isNotBlank(line)) { + log.info("Area code line [{}] found.", line); + String[] split = line.split("\\s+"); + if (split.length == 2) { + final String code = StringUtils.trim(split[0]); + if (StringUtils.isNotBlank(code) && code.matches("^\\d{3,4}")) { + final String city = StringUtils.trim(split[1]); + if (StringUtils.isNotBlank(city)) { + log.info("Area code [{}] city [{}] found.", code, city); + final AreaCode areaCode = new AreaCode(); + areaCode.setCode(code); + areaCode.setCity(city); + areaCode.setCreatedTime(now); + mapper.insert(areaCode); + } + } + } + } + } + } + catch (final IOException e) { + throw new IllegalStateException( + "Load area codes error caused", e); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void validate(final CreateFormAreaCode form) { + super.validate(form); + Assert.state(!mapper.exists( + new Search(AreaCode.CODE, form.getCode())), + () -> "Area code [" + form.getCode() + "] existed"); + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/impl/AuthPermissionServiceImpl.java b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/AuthPermissionServiceImpl.java new file mode 100644 index 00000000..3c35fd18 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/AuthPermissionServiceImpl.java @@ -0,0 +1,150 @@ +package com.pudonghot.yo.cms.service.impl; + +import java.util.List; +import java.util.Set; + +import com.pudonghot.yo.cms.form.SessionForm; +import com.pudonghot.yo.cms.form.create.CreateFormAuthPermission; +import lombok.extern.slf4j.Slf4j; +import java.util.stream.Collectors; +import com.wacai.tigon.mybatis.Search; +import org.springframework.util.Assert; +import com.wacai.tigon.model.ViewModel; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.stereotype.Service; +import com.pudonghot.yo.mapper.AuthRoleMapper; +import com.pudonghot.yo.model.domain.AuthRole; +import com.pudonghot.yo.mapper.AuthPermissionMapper; +import com.pudonghot.yo.model.domain.AuthPermission; +import com.pudonghot.yo.mapper.AuthPermissionRoleMapper; +import com.pudonghot.yo.model.domain.AuthPermissionRole; +import org.springframework.beans.factory.annotation.Autowired; +import com.pudonghot.yo.cms.service.AuthPermissionService; +import org.springframework.transaction.annotation.Transactional; +import com.pudonghot.yo.cms.form.update.UpdateFormAuthPermission; + +/** + * @author Donghuang
+ * Dec 24, 2019 16:49:51 + */ +@Slf4j +@Service +public class AuthPermissionServiceImpl + extends TaggableServiceImpl + implements AuthPermissionService { + + @Autowired + private AuthRoleMapper roleMapper; + @Autowired + private AuthPermissionRoleMapper permissionRoleMapper; + + /** + * {@inheritDoc} + */ + @Override + protected void validate(final CreateFormAuthPermission form) { + super.validate(form); + Assert.state(!mapper.exists( + new Search(AuthPermission.PERMISSION, form.getPermission())), + () -> "Auth permission [" + form.getPermission() + "] existed"); + } + + /** + * {@inheritDoc} + */ + @Override + public ViewModel create(final CreateFormAuthPermission form) { + final ViewModel vm = super.create(form); + final AuthPermission agent = vm.getData(); + final Integer[] roles = form.getRoles(); + if (roles != null) { + for (final Integer roleId : roles) { + insertPermissionRole(agent, roleId, form); + } + } + return vm; + } + + /** + * {@inheritDoc} + */ + @Override + public ViewModel update(final UpdateFormAuthPermission form) { + log.info("Update auth permission form [{}].", form); + final ViewModel vm = super.update(form); + final AuthPermission user = vm.getData(); + final Integer permissionId = user.getId(); + + final Set rolesExisted = permissionRoleMapper.list( + new Search(AuthPermissionRole.PERMISSION_ID, permissionId)).stream() + .map(AuthPermissionRole::getRoleId) + .collect(Collectors.toSet()); + + final Pair, Set> rolesDiff = + diff(rolesExisted, form.getRoles()); + + for (final Integer roleId : rolesDiff.getLeft()) { + insertPermissionRole(user, roleId, form); + } + + if (!rolesDiff.getRight().isEmpty()) { + permissionRoleMapper.delete( + new Search(AuthPermissionRole.PERMISSION_ID, permissionId) + .in(AuthPermissionRole.ROLE_ID, rolesDiff.getRight())); + } + + return vm; + } + + /** + * {@inheritDoc} + */ + @Override + @Transactional + public int delete(final Search search) { + permissionRoleMapper.delete( + new Search(AuthPermissionRole.PERMISSION, list(search) + .stream().map(AuthPermission::getId) + .collect(Collectors.toList()))); + return super.delete(search); + } + + /** + * {@inheritDoc} + */ + @Override + @Transactional + public int delete(final Integer id) { + permissionRoleMapper.delete(new Search(AuthPermissionRole.PERMISSION_ID, id)); + return super.delete(id); + } + + protected void insertPermissionRole( + final AuthPermission model, + final Integer roleId, + final SessionForm form) { + + final AuthRole role = roleMapper.find(roleId); + Assert.state(role != null, + () -> "No auth role [" + roleId + "] found"); + final AuthPermissionRole arg = new AuthPermissionRole(); + arg.setPermissionId(model.getId()); + arg.setPermission(model.getPermission()); + arg.setRoleId(roleId); + arg.setRole(role.getRole()); + arg.setCreatedBy(form.getAuthUser()); + + permissionRoleMapper.insert(arg); + } + + /** + * {@inheritDoc} + */ + @Override + public List listOfUser(final String account) { + return mapper.listOfUser(account); + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/impl/AuthRoleServiceImpl.java b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/AuthRoleServiceImpl.java new file mode 100644 index 00000000..649967c1 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/AuthRoleServiceImpl.java @@ -0,0 +1,53 @@ +package com.pudonghot.yo.cms.service.impl; + +import java.util.List; + +import com.pudonghot.yo.cms.form.create.CreateFormAuthRole; +import lombok.extern.slf4j.Slf4j; +import com.wacai.tigon.mybatis.Search; +import org.springframework.util.Assert; +import org.springframework.stereotype.Service; +import com.pudonghot.yo.mapper.AuthRoleMapper; +import com.pudonghot.yo.model.domain.AuthRole; +import com.pudonghot.yo.mapper.AuthUserRoleMapper; +import com.pudonghot.yo.model.domain.AuthUserRole; +import com.pudonghot.yo.cms.service.AuthRoleService; +import org.springframework.beans.factory.annotation.Autowired; +import com.pudonghot.yo.cms.form.update.UpdateFormAuthRole; + +/** + * @author Donghuang
+ * Dec 24, 2019 16:49:51 + */ +@Slf4j +@Service +public class AuthRoleServiceImpl + extends TaggableServiceImpl + implements AuthRoleService { + + @Autowired + private AuthUserRoleMapper authUserRoleMapper; + + /** + * {@inheritDoc} + */ + @Override + protected void validate(final CreateFormAuthRole form) { + super.validate(form); + Assert.state(!mapper.exists( + new Search(AuthRole.ROLE, form.getRole())), + () -> "Auth role [" + form.getRole() + "] existed"); + } + + /** + * {@inheritDoc} + */ + @Override + public List listOfUser(final String account) { + return authUserRoleMapper.listCol(AuthUserRole.ROLE, + new Search(AuthUserRole.ACCOUNT, account)); + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/impl/AuthUserServiceImpl.java b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/AuthUserServiceImpl.java new file mode 100644 index 00000000..e443e1ee --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/AuthUserServiceImpl.java @@ -0,0 +1,191 @@ +package com.pudonghot.yo.cms.service.impl; + +import java.util.List; +import java.util.Set; + +import com.pudonghot.yo.cms.form.SessionForm; +import com.pudonghot.yo.cms.form.create.CreateFormAuthUser; +import com.pudonghot.yo.mapper.*; +import com.pudonghot.yo.model.domain.*; +import lombok.extern.slf4j.Slf4j; +import java.util.stream.Collectors; + +import com.wacai.tigon.mybatis.Search; +import org.springframework.util.Assert; +import com.wacai.tigon.model.ViewModel; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.stereotype.Service; +import com.pudonghot.yo.cms.service.AuthUserService; +import org.springframework.beans.factory.annotation.Autowired; +import com.pudonghot.yo.cms.form.update.UpdateFormAuthUser; + +/** + * @author Donghuang
+ * Dec 24, 2019 16:49:51 + */ +@Slf4j +@Service +public class AuthUserServiceImpl + extends TaggableServiceImpl + implements AuthUserService { + + @Autowired + private AuthRoleMapper roleMapper; + @Autowired + private AuthUserRoleMapper userRoleMapper; + @Autowired + private TenantMapper tenantMapper; + @Autowired + private AuthUserTenantMapper userTenantMapper; + + /** + * {@inheritDoc} + */ + @Override + protected void validate(final CreateFormAuthUser form) { + super.validate(form); + Assert.state(!mapper.exists( + new Search(AuthUser.ACCOUNT, form.getAccount())), + () -> "Auth user [" + form.getAccount() + "] existed"); + } + + /** + * {@inheritDoc} + */ + @Override + public ViewModel create(final CreateFormAuthUser form) { + final ViewModel vm = super.create(form); + final AuthUser agent = vm.getData(); + final Integer[] roles = form.getRoles(); + if (roles != null) { + for (final Integer roleId : roles) { + insertUserRole(agent, roleId, form); + } + } + + final Integer[] tenants = form.getTenants(); + if (tenants != null) { + for (final Integer tenantId : tenants) { + insertUserTenant(agent, tenantId, form); + } + } + return vm; + } + + /** + * {@inheritDoc} + */ + @Override + public ViewModel update(UpdateFormAuthUser form) { + final ViewModel vm = super.update(form); + final AuthUser user = vm.getData(); + final Integer userId = user.getId(); + + // Role + final Set rolesExisted = userRoleMapper.list( + new Search(AuthUserRole.USER_ID, userId)).stream() + .map(AuthUserRole::getRoleId) + .collect(Collectors.toSet()); + + final Pair, Set> rolesDiff = + diff(rolesExisted, form.getRoles()); + + for (final Integer roleId : rolesDiff.getLeft()) { + insertUserRole(user, roleId, form); + } + + if (!rolesDiff.getRight().isEmpty()) { + userRoleMapper.delete( + new Search(AuthUserRole.USER_ID, userId) + .in(AuthUserRole.ROLE_ID, rolesDiff.getRight())); + } + + // Tenant + final Set tenantsExisted = userTenantMapper.list( + new Search(AuthUserTenant.USER_ID, userId)).stream() + .map(AuthUserTenant::getTenantId) + .collect(Collectors.toSet()); + + final Pair, Set> tenantsDiff = + diff(tenantsExisted, form.getTenants()); + + for (final Integer tenantId : tenantsDiff.getLeft()) { + insertUserTenant(user, tenantId, form); + } + + if (!tenantsDiff.getRight().isEmpty()) { + userTenantMapper.delete( + new Search(AuthUserTenant.USER_ID, userId) + .in(AuthUserTenant.TENANT_ID, tenantsDiff.getRight())); + } + + return vm; + } + + /** + * {@inheritDoc} + */ + @Override + public int delete(final Search search) { + final List users = list(search).stream() + .map(AuthUser::getId) + .collect(Collectors.toList()); + + if (!users.isEmpty()) { + log.info("Delete auth users {}.", users); + userRoleMapper.delete( + new Search(AuthUserRole.USER_ID, users)); + userTenantMapper.delete( + new Search(AuthUserTenant.USER_ID, users)); + return super.delete(search); + } + log.warn("No auth users found to delete, ignore."); + return 0; + } + + /** + * {@inheritDoc} + */ + @Override + public int delete(final Integer id) { + userRoleMapper.delete(new Search(AuthUserRole.USER_ID, id)); + userTenantMapper.delete(new Search(AuthUserTenant.USER_ID, id)); + return super.delete(id); + } + + protected void insertUserRole(final AuthUser model, + final Integer roleId, + final SessionForm form) { + + final AuthRole role = roleMapper.find(roleId); + Assert.state(role != null, + () -> "No auth role [" + roleId + "] found"); + final AuthUserRole arg = new AuthUserRole(); + arg.setUserId(model.getId()); + arg.setAccount(model.getAccount()); + arg.setRoleId(roleId); + arg.setRole(role.getRole()); + arg.setCreatedBy(form.getAuthUser()); + + userRoleMapper.insert(arg); + } + + protected void insertUserTenant(final AuthUser model, + final Integer tenantId, + final SessionForm form) { + final Tenant tenant = tenantMapper.find(tenantId); + Assert.state(tenant != null, + () -> "No tenant [" + tenantId + "] found"); + final AuthUserTenant arg = new AuthUserTenant(); + arg.setUserId(model.getId()); + arg.setAccount(model.getAccount()); + arg.setTenantId(tenantId); + arg.setTenantCode(tenant.getCode()); + arg.setCreatedBy(form.getAuthUser()); + + userTenantMapper.insert(arg); + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/impl/CallDetailRecordServiceImpl.java b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/CallDetailRecordServiceImpl.java new file mode 100644 index 00000000..9cdb0022 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/CallDetailRecordServiceImpl.java @@ -0,0 +1,20 @@ +package com.pudonghot.yo.cms.service.impl; + +import com.pudonghot.yo.cms.service.CallDetailRecordService; +import com.pudonghot.yo.mapper.CallDetailRecordMapper; +import com.pudonghot.yo.model.domain.CallDetailRecord; +import com.wacai.tigon.service.support.BaseQueryServiceSupport; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +/** + * @author bingpo + * @date 2020/3/30 下午3:26 + */ +@Slf4j +@Service +public class CallDetailRecordServiceImpl + extends BaseQueryServiceSupport + implements CallDetailRecordService{ + +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/impl/CallingListServiceImpl.java b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/CallingListServiceImpl.java new file mode 100644 index 00000000..b3577a3d --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/CallingListServiceImpl.java @@ -0,0 +1,41 @@ +package com.pudonghot.yo.cms.service.impl; + +import com.pudonghot.yo.cms.form.create.CreateFormCallingList; +import com.pudonghot.yo.cms.form.update.UpdateFormCallingList; +import com.pudonghot.yo.cms.service.CallingListService; +import com.pudonghot.yo.mapper.CallingListMapper; +import com.pudonghot.yo.mapper.CampaignMapper; +import com.pudonghot.yo.model.domain.CallingList; +import com.pudonghot.yo.model.domain.Campaign; +import com.wacai.tigon.service.support.BaseCrudByFormServiceSupport; +import lombok.extern.slf4j.Slf4j; +import org.apache.shiro.util.Assert; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * @author bingpo + * @date 2020/3/16 下午4:00 + */ +@Slf4j +@Service +public class CallingListServiceImpl + extends BaseCrudByFormServiceSupport + implements CallingListService { + @Autowired + private CampaignMapper campaignMapper; + + @Override + protected void beforeCreate(CreateFormCallingList form, CallingList model) { + super.beforeCreate(form, model); + final Integer campaignId = form.getCampaignId(); + final Campaign campaign = campaignMapper.find(campaignId); + Assert.state(campaign != null, "无效的campaign[ " + campaignId + " ]"); + model.setCampaignKey(campaign.getCampaignKey()); + } + +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/impl/CampaignServiceImpl.java b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/CampaignServiceImpl.java new file mode 100644 index 00000000..b19b41e1 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/CampaignServiceImpl.java @@ -0,0 +1,159 @@ +package com.pudonghot.yo.cms.service.impl; + +import java.util.Set; + +import com.pudonghot.yo.cms.form.RobotConfigForm; +import com.pudonghot.yo.cms.form.SessionForm; +import com.pudonghot.yo.cms.form.create.CreateFormCampaign; +import com.pudonghot.yo.mapper.RobotCallConfigMapper; +import com.pudonghot.yo.model.domain.RobotCallConfig; +import lombok.extern.slf4j.Slf4j; +import java.util.stream.Collectors; +import com.wacai.tigon.mybatis.Search; +import com.wacai.tigon.model.ViewModel; +import com.wacai.tigon.sequence.IdSequence; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; +import com.pudonghot.yo.mapper.CampaignMapper; +import com.pudonghot.yo.model.domain.Campaign; +import com.pudonghot.yo.cms.service.CampaignService; +import org.springframework.beans.factory.annotation.Autowired; +import com.pudonghot.yo.mapper.CampaignTrunkStrategyMapper; +import com.pudonghot.yo.model.domain.CampaignTrunkStrategy; +import com.pudonghot.yo.cms.form.update.UpdateFormCampaign; + +/** + * @author Donghuang
+ * Jan 02, 2020 16:46:00 + */ +@Slf4j +@Service +public class CampaignServiceImpl + extends TaggableServiceImpl + implements CampaignService { + + @Autowired + private IdSequence idSeq; + @Autowired + private RobotCallConfigMapper robotCallConfigMapper; + @Autowired + private CampaignTrunkStrategyMapper campaignTrunkStrategyMapper; + + /** + * {@inheritDoc} + */ + @Override + public ViewModel create(final CreateFormCampaign form) { + final ViewModel vm = super.create(form); + + final Campaign agentGroup = vm.getData(); + final Integer[] strategies = form.getTrunkStrategies(); + if (strategies != null) { + for (final Integer strategy : strategies) { + insertTrunkStrategy(agentGroup, strategy, form); + } + } + if (form.getRobotConfig()) { + final RobotConfigForm robotConfigForm = new RobotConfigForm(); + BeanUtils.copyProperties(form, robotConfigForm); + insertRobotConfig(agentGroup, robotConfigForm); + } + return vm; + } + + private void insertRobotConfig(final Campaign campaign, + final RobotConfigForm form) { + final RobotCallConfig robotCallConfig = new RobotCallConfig(); + robotCallConfig.setTenantId(campaign.getTenantId()); + robotCallConfig.setTenantCode(campaign.getTenantCode()); + robotCallConfig.setCampaignId(campaign.getId()); + robotCallConfig.setResultTopic(form.getRobotResultTopic()); + robotCallConfig.setStateMachineId(form.getRobotStateMachineId()); + robotCallConfig.setFetchData(form.getRobotFetchData()); + final Boolean fetchLocal = form.getRobotFetchLocal(); + robotCallConfig.setFetchLocal(fetchLocal == null ? false : !fetchLocal); + robotCallConfig.setFetchUrl(form.getRobotFetchUrl()); + robotCallConfig.setFetchHeader(form.getRobotFetchHeader()); + robotCallConfigMapper.insert(robotCallConfig); + } + + private void deleteRobotConfig(final Campaign campaign) { + robotCallConfigMapper.delete(new Search(RobotCallConfig.CAMPAIGN_ID, campaign.getId())); + } + + /** + * {@inheritDoc} + */ + @Override + public ViewModel update(final UpdateFormCampaign form) { + final ViewModel vm = super.update(form); + final Campaign agentGroup = vm.getData(); + final Integer agentGroupId = agentGroup.getId(); + + final Set strategiesExisted = campaignTrunkStrategyMapper.list( + new Search(CampaignTrunkStrategy.CAMPAIGN_ID, + agentGroupId)).stream() + .map(CampaignTrunkStrategy::getTrunkStrategyId) + .collect(Collectors.toSet()); + + final Pair, Set> strategiesDiff = + diff(strategiesExisted, form.getTrunkStrategies()); + + for (final Integer strategyId : strategiesDiff.getLeft()) { + insertTrunkStrategy(agentGroup, strategyId, form); + } + + if (!strategiesDiff.getRight().isEmpty()) { + campaignTrunkStrategyMapper.delete( + new Search(CampaignTrunkStrategy.CAMPAIGN_ID, agentGroupId) + .in(CampaignTrunkStrategy.TRUNK_STRATEGY_ID, + strategiesDiff.getRight())); + } + deleteRobotConfig(agentGroup); + if (form.getRobotConfig()) { + final RobotConfigForm robotConfigForm = new RobotConfigForm(); + BeanUtils.copyProperties(form, robotConfigForm); + insertRobotConfig(agentGroup, robotConfigForm); + } + return vm; + } + + /** + * {@inheritDoc} + */ + @Override + protected void beforeInsert(final Campaign model) { + super.beforeInsert(model); + model.setStatus(Campaign.Status.STOPPED); + if (StringUtils.isBlank(model.getCampaignKey())) { + model.setCampaignKey(idSeq.get()); + } + } + + protected void insertTrunkStrategy(final Campaign agentGroup, + final Integer strategyId, + final SessionForm form) { + final CampaignTrunkStrategy model = + new CampaignTrunkStrategy(); + model.setTenantId(form.getTenantId()); + model.setTenantCode(form.getTenantCode()); + model.setCampaignId(agentGroup.getId()); + model.setTrunkStrategyId(strategyId); + model.setCreatedBy(form.getAuthUser()); + + log.info("Insert agent group trunk strategy [{}].", model); + campaignTrunkStrategyMapper.insert(model); + } + + @Override + public int delete(Integer id) { + final Campaign campaign = find(id); + deleteRobotConfig(campaign); + return super.delete(id); + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/impl/GatewayServiceImpl.java b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/GatewayServiceImpl.java new file mode 100644 index 00000000..96fec583 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/GatewayServiceImpl.java @@ -0,0 +1,25 @@ +package com.pudonghot.yo.cms.service.impl; + +import com.pudonghot.yo.cms.form.create.CreateFormGateway; +import com.pudonghot.yo.cms.form.update.UpdateFormGateway; +import com.pudonghot.yo.cms.service.GatewayService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import com.pudonghot.yo.mapper.GatewayMapper; +import com.pudonghot.yo.model.domain.Gateway; +import com.wacai.tigon.service.support.BaseCrudByFormServiceSupport; + +/** + * @author Donghuang
+ * Nov 01, 2019 22:48:24 + */ +@Slf4j +@Service +public class GatewayServiceImpl + extends BaseCrudByFormServiceSupport + implements GatewayService { +} 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 new file mode 100644 index 00000000..a1bdb821 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/QueueServiceImpl.java @@ -0,0 +1,58 @@ +package com.pudonghot.yo.cms.service.impl; + +import com.pudonghot.yo.cms.form.create.CreateFormQueue; +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.Assert; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import com.pudonghot.yo.mapper.QueueMapper; +import com.pudonghot.yo.model.domain.Queue; +import com.pudonghot.yo.mapper.TenantMapper; +import com.pudonghot.yo.model.domain.Tenant; +import com.pudonghot.yo.cms.service.QueueService; +import com.pudonghot.yo.cms.service.SequenceService; +import org.springframework.beans.factory.annotation.Value; +import com.pudonghot.yo.cms.form.update.UpdateFormQueue; +import org.springframework.beans.factory.annotation.Autowired; +import com.wacai.tigon.service.support.BaseCrudByFormServiceSupport; + +/** + * @author Donghuang
+ * Nov 16, 2019 12:46:42 + */ +@Slf4j +@Service +public class QueueServiceImpl + extends BaseCrudByFormServiceSupport + implements QueueService { + + @Value("${yo.cms.queue.seq-name:QUEUE}") + private String seqName; + @Value("${yo.cms.queue.identifier-prefix:Q}") + private String identifierPrefix; + @Autowired + private SequenceService seqService; + @Autowired + private TenantMapper tenantMapper; + + /** + * {@inheritDoc} + */ + @Override + protected void beforeInsert(final Queue model) { + super.beforeInsert(model); + if (StringUtils.isBlank(model.getIdentifier())) { + final Integer tenantId = model.getTenantId(); + 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')); + } + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/impl/RobotAgentServiceImpl.java b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/RobotAgentServiceImpl.java new file mode 100644 index 00000000..dbca7b04 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/RobotAgentServiceImpl.java @@ -0,0 +1,51 @@ +package com.pudonghot.yo.cms.service.impl; + +import com.pudonghot.yo.cms.form.create.CreateFormRobotAgent; +import com.pudonghot.yo.cms.form.update.UpdateFormRobotAgent; +import com.pudonghot.yo.cms.service.RobotAgentService; +import com.pudonghot.yo.mapper.AgentMapper; +import com.pudonghot.yo.mapper.RobotAgentMapper; +import com.pudonghot.yo.model.domain.Agent; +import com.pudonghot.yo.model.domain.RobotAgent; +import com.wacai.tigon.mybatis.Search; +import com.wacai.tigon.service.support.BaseCrudByFormServiceSupport; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.Assert; + +/** + * @author: qiushui + * @date: 2020-02-27 17:37 + */ +@Service +@Slf4j +public class RobotAgentServiceImpl extends BaseCrudByFormServiceSupport + implements RobotAgentService { + + @Autowired + private AgentMapper agentMapper; + + @Override + public void batchCreate(CreateFormRobotAgent form) { + for(Integer agentId : form.getAgentIds()){ + Agent agent = agentMapper.find(agentId); + Assert.notNull(agent,"agent not exist! id : " + agentId); + + Assert.isTrue(!mapper.exists(new Search(RobotAgent.AGENT_ID,agent.getId())) + ,"坐席" + agent.getAgent() + "已存在!"); + + RobotAgent robotAgent = new RobotAgent(); + form.copy(robotAgent); + robotAgent.setAccount(agent.getAccount()); + robotAgent.setAgent(agent.getAgent()); + robotAgent.setAgentId(agentId); + robotAgent.setStatus(RobotAgent.Status.AVAILABLE); + mapper.insert(robotAgent); + } + } +} 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 new file mode 100644 index 00000000..334b2840 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/SequenceServiceImpl.java @@ -0,0 +1,68 @@ +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.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.update.UpdateFormSequence; +import org.springframework.transaction.annotation.Transactional; +import com.wacai.tigon.service.support.BaseCrudByFormServiceSupport; + +/** + * @author Donghuang
+ * Nov 01, 2019 22:48:24 + */ +@Slf4j +@Service +public class SequenceServiceImpl + extends BaseCrudByFormServiceSupport + implements SequenceService { + + /** + * {@inheritDoc} + */ + @Override + protected void validate(final CreateFormSequence form) { + super.validate(form); + Assert.state(!mapper.exists( + new Search(Sequence.TENANT_ID, form.getTenantId()) + .eq(Sequence.NAME, form.getName())), + () -> "Sequence [" + form.getName() + "] existed"); + } + + /** + * {@inheritDoc} + */ + @Override + @Transactional + 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; + } + + /** + * {@inheritDoc} + */ + @Override + protected void beforeInsert(final Sequence model) { + super.beforeInsert(model); + if (model.getCurrentVal() == null) { + model.setCurrentVal(model.getInitVal()); + } + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/impl/TagServiceImpl.java b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/TagServiceImpl.java new file mode 100644 index 00000000..4fa9f633 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/TagServiceImpl.java @@ -0,0 +1,45 @@ +package com.pudonghot.yo.cms.service.impl; + +import com.pudonghot.yo.cms.form.create.CreateFormTag; +import lombok.extern.slf4j.Slf4j; +import com.pudonghot.yo.mapper.TagMapper; +import com.pudonghot.yo.model.domain.Tag; +import org.springframework.stereotype.Service; +import com.pudonghot.yo.cms.service.TagService; +import com.pudonghot.yo.cms.form.update.UpdateFormTag; +import com.wacai.tigon.service.support.BaseCrudByFormServiceSupport; + +/** + * @author Donghuang
+ * Nov 14, 2019 12:04:36 + */ +@Slf4j +@Service +public class TagServiceImpl + extends BaseCrudByFormServiceSupport + implements TagService { + + /** + * {@inheritDoc} + */ + @Override + protected void beforeCreate(final CreateFormTag form, final Tag model) { + super.beforeCreate(form, model); + model.setOwner(form.getScope() == Tag.Scope.USER ? + form.getAuthUser() : Tag.OWNER_COMMON); + } + + /** + * {@inheritDoc} + */ + @Override + protected void beforeUpdate(final UpdateFormTag form, final Tag model) { + super.beforeUpdate(form, model); + model.setOwner(form.getScope() == Tag.Scope.USER ? + form.getAuthUser() : Tag.OWNER_COMMON); + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/impl/TaggableServiceImpl.java b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/TaggableServiceImpl.java new file mode 100644 index 00000000..b8093119 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/TaggableServiceImpl.java @@ -0,0 +1,184 @@ +package com.pudonghot.yo.cms.service.impl; + +import java.util.*; + +import com.pudonghot.yo.cms.form.SessionForm; +import com.pudonghot.yo.cms.form.TaggableForm; +import com.pudonghot.yo.cms.util.SetUtils; +import lombok.extern.slf4j.Slf4j; +import com.wacai.tigon.mybatis.*; +import java.util.stream.Collectors; +import com.wacai.tigon.model.ViewModel; +import org.springframework.util.Assert; +import org.apache.commons.lang3.tuple.Pair; +import com.wacai.tigon.form.FormCreateApi; +import com.wacai.tigon.form.FormUpdateApi; +import com.pudonghot.yo.mapper.ObjectTagMapper; +import com.pudonghot.yo.model.domain.ObjectTag; +import com.pudonghot.yo.cms.service.TaggableService; +import com.pudonghot.yo.model.domain.TaggableDomain; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; +import com.wacai.tigon.service.support.BaseCrudByFormServiceSupport; + +/** + * @author Donghuang
+ * Nov 02, 2019 12:52:47 + */ +@Slf4j +public abstract class TaggableServiceImpl< + Model extends TaggableDomain, + FormCreate extends FormCreateApi & SessionForm & TaggableForm, + FormUpdate extends FormUpdateApi &SessionForm & TaggableForm, + Mapper extends BaseMapper> + extends BaseCrudByFormServiceSupport< + Integer, Model, FormCreate, FormUpdate, Mapper> + implements TaggableService { + + @Autowired + protected ObjectTagMapper tagMapper; + + /** + * {@inheritDoc} + */ + @Override + public ViewModel findViewModel(final Search search) { + final List models = mapper.list(search); + Assert.state(models.size() < 2, "Multiple results queried"); + return toViewModel(models.isEmpty() ? + null : models.iterator().next()); + } + + /** + * For keys generation + * + * {@inheritDoc} + */ + @Override + @Transactional(propagation = Propagation.REQUIRES_NEW) + public ViewModel create(Model model) { + return super.create(model); + } + + /** + * For keys generation + * + * @param models model + * @return + */ + @Override + @Transactional(propagation = Propagation.REQUIRES_NEW) + public Collection> create(Collection models) { + return super.create(models); + } + + /** + * {@inheritDoc} + */ + @Override + public ViewModel create(final FormCreate form) { + final ViewModel vm = super.create(form); + + final Integer[] tags = form.getTags(); + if (tags != null) { + for (final Integer tagId : tags) { + insertTag(vm.getData(), tagId, form); + } + } + return vm; + } + + /** + * {@inheritDoc} + */ + @Override + public ViewModel update(FormUpdate form) { + final ViewModel vm = super.update(form); + final Model model = vm.getData(); + final Integer id = form.getId(); + + final Pair, Set> tagsDiff = + diff(existedTags(id), form.getTags()); + + // add + for (final Integer tagId : tagsDiff.getLeft()) { + insertTag(model, tagId, form); + } + + // remove + if (!tagsDiff.getRight().isEmpty()) { + tagMapper.delete( + new Search(ObjectTag.OBJECT_TABLE, table) + .eq(ObjectTag.OBJECT_ID, id) + .in(ObjectTag.TAG_ID, tagsDiff.getRight())); + } + + return vm; + } + + /** + * {@inheritDoc} + */ + @Override + @Transactional + public int delete(final Search search) { + tagMapper.delete( + new Search(ObjectTag.OBJECT_TABLE, table) + .in(ObjectTag.OBJECT_ID, list(search) + .stream().map(Model::getId) + .collect(Collectors.toList()))); + return super.delete(search); + } + + /** + * {@inheritDoc} + */ + @Override + @Transactional + public int delete(final Integer id) { + tagMapper.delete(new Search(ObjectTag.OBJECT_TABLE, table) + .eq(ObjectTag.OBJECT_ID, id)); + return super.delete(id); + } + + /** + * insert tag + * + * @param model model + * @param tagId tag id + * @param form form + */ + protected void insertTag(final Model model, final Integer tagId, final SessionForm form) { + final ObjectTag objectTag = form.copy(new ObjectTag()); + objectTag.setObjectTable(table); + objectTag.setObjectId(model.getId()); + objectTag.setTagId(tagId); + objectTag.setCreatedBy(form.getAuthUser()); + objectTag.setUpdatedBy(null); + + log.info("Insert object tag [{}].", objectTag); + tagMapper.insert(objectTag); + } + + protected Set existedTags(final Integer id) { + return tagMapper.list( + new Search(ObjectTag.OBJECT_TABLE, table) + .eq(ObjectTag.OBJECT_ID, id)).stream() + .map(ObjectTag::getTagId) + .collect(Collectors.toSet()); + } + + /** + * set diff + * + * @param existed existed items set + * @param formArray from items id array + * @param element type + * @return diff, left: ADD, right: DELETE + */ + protected Pair, Set> diff(Set existed, T[] formArray) { + return SetUtils.diff(existed, formArray != null ? + new HashSet<>(Arrays.asList(formArray)) : null); + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/impl/TelecomVendorServiceImpl.java b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/TelecomVendorServiceImpl.java new file mode 100644 index 00000000..a24dde63 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/TelecomVendorServiceImpl.java @@ -0,0 +1,25 @@ +package com.pudonghot.yo.cms.service.impl; + +import com.pudonghot.yo.cms.form.create.CreateFormTelecomVendor; +import com.pudonghot.yo.cms.form.update.UpdateFormTelecomVendor; +import com.pudonghot.yo.cms.service.TelecomVendorService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import com.pudonghot.yo.mapper.TelecomVendorMapper; +import com.pudonghot.yo.model.domain.TelecomVendor; +import com.wacai.tigon.service.support.BaseCrudByFormServiceSupport; + +/** + * @author Donghuang
+ * Nov 01, 2019 22:48:24 + */ +@Slf4j +@Service +public class TelecomVendorServiceImpl + extends BaseCrudByFormServiceSupport + implements TelecomVendorService { +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/impl/TenantServiceImpl.java b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/TenantServiceImpl.java new file mode 100644 index 00000000..4b77beb3 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/TenantServiceImpl.java @@ -0,0 +1,139 @@ +package com.pudonghot.yo.cms.service.impl; + +import java.util.List; + +import com.pudonghot.yo.cms.form.create.CreateFormTenant; +import com.pudonghot.yo.redis.RedisClientApi; +import lombok.extern.slf4j.Slf4j; +import java.util.stream.Collectors; +import com.wacai.tigon.mybatis.Search; +import org.springframework.util.Assert; +import com.wacai.tigon.sequence.IdSequence; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import com.pudonghot.yo.model.ValueTextModel; +import com.pudonghot.yo.mapper.TenantMapper; +import com.pudonghot.yo.model.domain.Tenant; +import org.apache.commons.lang3.RandomStringUtils; +import com.pudonghot.yo.cms.service.TenantService; +import org.springframework.beans.factory.annotation.Value; +import com.pudonghot.yo.cms.form.update.UpdateFormTenant; +import org.springframework.beans.factory.annotation.Autowired; +import com.wacai.tigon.service.support.BaseCrudByFormServiceSupport; + +/** + * @author Donghuang
+ * Nov 01, 2019 15:43:13 + */ +@Slf4j +@Service +public class TenantServiceImpl + extends BaseCrudByFormServiceSupport + implements TenantService { + + @Value("${yo.cms.tenant.cache-key:AUTH_TENANT}") + private String tenantCacheKey; + @Value("${yo.cms.tenant.access-secret-length:32}") + private int accessSecretLength; + @Autowired + private RedisClientApi codisClient; + @Autowired + private IdSequence idSeq; + + /** + * {@inheritDoc} + */ + @Override + public void switchTo(final String account, final String tenantCode) { + final Tenant tenant = mapper.find( + new Search(Tenant.CODE, tenantCode).eq(Tenant.ACTIVE, true)); + Assert.state(tenant != null, "Tenant [" + tenantCode + "] not found"); + codisClient.hset(tenantCacheKey, account, tenantCode); + } + + /** + * {@inheritDoc} + */ + @Override + public ValueTextModel current(final String account) { + final String tenantCode = codisClient.hget(tenantCacheKey, account); + if (StringUtils.isNotBlank(tenantCode)) { + final Tenant tenant = mapper.find( + new Search(Tenant.CODE, tenantCode) + .eq(Tenant.ACTIVE, true)); + if (tenant != null) { + return ValueTextModel.of(tenantCode, tenant.getName()); + } + log.info("Tenant [{}] not found, remove from account [{}] cache.", tenantCode, account); + codisClient.hdel(tenantCacheKey, account); + } + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public ValueTextModel clear(final String account) { + final ValueTextModel tenant = current(account); + if (tenant != null) { + codisClient.hdel(tenantCacheKey, account); + } + return tenant; + } + + /** + * {@inheritDoc} + */ + @Override + public List> listOfUser(final String account) { + return toVt(mapper.listOfUser(account)); + } + + /** + * {@inheritDoc} + */ + @Override + public List> listOfAdmin() { + return toVt(mapper.list(new Search(Tenant.ACTIVE, true))); + } + + /** + * {@inheritDoc} + */ + @Override + protected void beforeUpdate(final UpdateFormTenant form, final Tenant model) { + super.beforeUpdate(form, model); + + if (form.isRenewAccessSecret()) { + log.info("Renew tenant [{}] access secret.", model); + model.setAccessSecret( + RandomStringUtils.randomAlphanumeric(accessSecretLength)); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void beforeInsert(final Tenant model) { + super.beforeInsert(model); + if (StringUtils.isBlank(model.getAccessKey())) { + model.setAccessKey(idSeq.get()); + } + if (StringUtils.isBlank(model.getAccessSecret())) { + model.setAccessSecret( + RandomStringUtils.randomAlphanumeric(accessSecretLength)); + } + } + + public List> toVt(final List tenants) { + return tenants.stream() + .map(t -> new ValueTextModel<>(t.getCode(), t.getName())) + .collect(Collectors.toList()); + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/impl/TrunkAttrServiceImpl.java b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/TrunkAttrServiceImpl.java new file mode 100644 index 00000000..2a368a91 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/TrunkAttrServiceImpl.java @@ -0,0 +1,52 @@ +package com.pudonghot.yo.cms.service.impl; + +import com.pudonghot.yo.cms.form.create.CreateFormTrunkAttr; +import lombok.extern.slf4j.Slf4j; +import com.wacai.tigon.mybatis.Search; +import org.springframework.util.Assert; +import org.springframework.stereotype.Service; +import com.pudonghot.yo.model.domain.TrunkAttr; +import com.pudonghot.yo.mapper.TrunkAttrMapper; +import com.pudonghot.yo.cms.service.TrunkAttrService; +import com.pudonghot.yo.cms.form.update.UpdateFormTrunkAttr; +import com.wacai.tigon.service.support.BaseCrudByFormServiceSupport; + +/** + * @author Donghuang
+ * Nov 14, 2019 12:04:36 + */ +@Slf4j +@Service +public class TrunkAttrServiceImpl + extends BaseCrudByFormServiceSupport + implements TrunkAttrService { + + /** + * {@inheritDoc} + */ + @Override + protected void validate(final CreateFormTrunkAttr form) { + super.validate(form); + Assert.state(!mapper.exists( + new Search(TrunkAttr.TENANT_ID, form.getTenantId()) + .eq(TrunkAttr.ATTR, form.getAttr())), + () -> "中继属性[" + form.getAttr() + "]已经存在"); + } + + /** + * {@inheritDoc} + */ + @Override + protected void validate(final UpdateFormTrunkAttr form) { + super.validate(form); + Assert.state(!mapper.exists( + new Search(TrunkAttr.TENANT_ID, form.getTenantId()) + .ne(TrunkAttr.ID, form.getId()) + .eq(TrunkAttr.ATTR, form.getAttr())), + () -> "中继属性[" + form.getAttr() + "]已经存在"); + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/impl/TrunkProhibitedAreaCodeServiceImpl.java b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/TrunkProhibitedAreaCodeServiceImpl.java new file mode 100644 index 00000000..3f292a5c --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/TrunkProhibitedAreaCodeServiceImpl.java @@ -0,0 +1,25 @@ +package com.pudonghot.yo.cms.service.impl; + +import com.pudonghot.yo.cms.form.BaseUpdateForm; +import com.pudonghot.yo.cms.form.create.CreateFormTrunkProhibitedAreaCode; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import com.pudonghot.yo.mapper.TrunkProhibitedAreaCodeMapper; +import com.pudonghot.yo.model.domain.TrunkProhibitedAreaCode; +import com.wacai.tigon.service.support.BaseCrudByFormServiceSupport; +import com.pudonghot.yo.cms.service.TrunkProhibitedAreaCodeService; + +/** + * @author Donghuang
+ * Nov 30, 2019 19:01:05 + */ +@Slf4j +@Service +public class TrunkProhibitedAreaCodeServiceImpl + extends BaseCrudByFormServiceSupport + implements TrunkProhibitedAreaCodeService { +} 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 new file mode 100644 index 00000000..623e2459 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/TrunkServiceImpl.java @@ -0,0 +1,230 @@ +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.wacai.tigon.model.ViewModel; +import org.springframework.util.Assert; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.commons.lang3.StringUtils; +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.cms.service.AreaCodeService; +import com.pudonghot.yo.cms.service.SequenceService; +import org.springframework.beans.factory.annotation.Value; +import com.pudonghot.yo.cms.form.update.UpdateFormTrunk; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * @author Donghuang
+ * Nov 02, 2019 12:46:22 + */ +@Slf4j +@Service +public class TrunkServiceImpl + extends TaggableServiceImpl + implements TrunkService { + + @Autowired + private SequenceService seqService; + @Value("${yo.cms.trunk-prefix.seq-name:TRUNK}") + private String seqName; + @Autowired + private GatewayMapper gatewayMapper; + @Autowired + private TelecomVendorMapper telecomVendorMapper; + @Autowired + private CellphoneService cellphoneService; + @Autowired + private AreaCodeService areaCodeService; + @Autowired + private TrunkStrategyTrunkMapper strategyTrunkMapper; + @Autowired + private TrunkTrunkAttrMapper trunkTrunkAttrMapper; + + /** + * {@inheritDoc} + */ + @Override + public ViewModel create(final CreateFormTrunk form) { + final Integer telecomVendorId = form.getTelecomVendorId(); + Assert.state(telecomVendorMapper.exists( + new Search(telecomVendorId) + .eq(TelecomVendor.ACTIVE, true)), + "No valid telecom vendor [" + telecomVendorId + "] found"); + + final Integer gatewayId = form.getGatewayId(); + Assert.state(gatewayMapper.exists( + new Search(gatewayId) + .eq(Gateway.ACTIVE, true)), + "No valid gateway [" + gatewayId + "] found"); + + final List trunks = validateCreate(form); + create(trunks); + + insertItems(form.getTags(), trunks, (t, i) -> insertTag(t, i, form)); + insertItems(form.getStrategies(), trunks, (t, i) -> insertStrategy(t, i, form)); + insertItems(form.getAttrs(), trunks, (t, i) -> insertAttr(t, i, form)); + + return new ViewModel<>(); + } + + /** + * {@inheritDoc} + */ + @Override + public ViewModel update(final UpdateFormTrunk form) { + ViewModel vm = super.update(form); + final Trunk trunk = vm.getData(); + final Integer id = trunk.getId(); + + // Strategies + final Set strategiesExisted = strategyTrunkMapper.list( + new Search(TrunkStrategyTrunk.TRUNK_ID, id)).stream() + .map(TrunkStrategyTrunk::getTrunkStrategyId) + .collect(Collectors.toSet()); + + final Pair, Set> strategiesDiff = + diff(strategiesExisted, form.getStrategies()); + + for (final Integer strategyId : strategiesDiff.getLeft()) { + insertStrategy(trunk, strategyId, form); + } + + if (!strategiesDiff.getRight().isEmpty()) { + strategyTrunkMapper.delete( + new Search(TrunkStrategyTrunk.TRUNK_ID, id) + .in(TrunkStrategyTrunk.TRUNK_STRATEGY_ID, + strategiesDiff.getRight())); + } + + // Attrs + final Set attrsExisted = trunkTrunkAttrMapper.list( + new Search(TrunkTrunkAttr.TRUNK_ID, id)).stream() + .map(TrunkTrunkAttr::getTrunkAttrId) + .collect(Collectors.toSet()); + + final Pair, Set> attrsDiff = + diff(attrsExisted, form.getAttrs()); + + for (final Integer attrId : attrsDiff.getLeft()) { + insertAttr(trunk, attrId, form); + } + + if (!attrsDiff.getRight().isEmpty()) { + trunkTrunkAttrMapper.delete( + new Search(TrunkTrunkAttr.TRUNK_ID, id) + .in(TrunkTrunkAttr.TRUNK_ATTR_ID, + attrsDiff.getRight())); + } + + return vm; + } + + /** + * {@inheritDoc} + */ + @Override + protected void beforeInsert(final Trunk model) { + super.beforeInsert(model); + if (StringUtils.isBlank(model.getPrefix())) { + model.setPrefix(String.valueOf( + seqService.nextVal(model.getTenantId(), seqName))); + } + } + + List validateCreate(final CreateFormTrunk form) { + final String[] callerNumbers = + form.getCallerNumbers().replaceAll("-", "").split("[^0-9]+"); + final List trunks = new ArrayList<>(callerNumbers.length); + + Arrays.stream(callerNumbers).forEach(callerNumber -> { + Assert.state(callerNumber.matches("^\\d+$"), + () -> "外呼中继号码[ " + callerNumber + "]不是有效电话号码"); + Assert.state(!mapper.exists( + new Search(Trunk.CALLER_NUMBER, callerNumber)), + () -> "外呼中继号码[ " + callerNumber + "]已经存在"); + + final String areaCode = getTrunk(callerNumber); + Assert.state(StringUtils.isNotBlank(areaCode), + () -> "外呼中继号码[ " + callerNumber + "]无法匹配区号"); + + final Trunk trunk = form.copy(modelType); + trunk.setAreaCode(areaCode); + trunk.setCallerNumber(callerNumber); + + trunk.setCpn(form.isCpnWithOutAreaCode() ? + callerNumber.replaceFirst(areaCode, "") : callerNumber); + trunks.add(trunk); + }); + return trunks; + } + + private String getTrunk(final String callerNumber) { + if (MobileUtils.checkMobile(callerNumber)) { + final CellphoneInfo cellphoneInfo = + cellphoneService.lookup(callerNumber); + Assert.state(cellphoneInfo != null, + () -> "No mobile phone [" + callerNumber + "] info found"); + return cellphoneInfo.getAreaCode(); + } + + for (final String areaCode : areaCodeService.getCodes()) { + if (callerNumber.startsWith(areaCode)) { + return areaCode; + } + } + return null; + } + + void insertItems(final Integer[] items, + final List trunks, + final BiConsumer bic) { + + if (items != null) { + for (final Integer item : items) { + for (final Trunk trunk : trunks) { + bic.accept(trunk, item); + } + } + } + } + + void insertStrategy(final Trunk model, final Integer strategyId, final SessionForm form) { + final TrunkStrategyTrunk strategyTrunk = new TrunkStrategyTrunk(); + strategyTrunk.setTenantId(form.getTenantId()); + strategyTrunk.setTenantCode(form.getTenantCode()); + strategyTrunk.setTrunkStrategyId(strategyId); + strategyTrunk.setTrunkId(model.getId()); + strategyTrunk.setCreatedBy(form.getAuthUser()); + strategyTrunk.setCreatedTime(new Date()); + log.info("Insert strategy trunk [{}].", strategyTrunk); + strategyTrunkMapper.insert(strategyTrunk); + } + + void insertAttr(final Trunk model, final Integer attrId, final SessionForm form) { + final TrunkTrunkAttr tta = new TrunkTrunkAttr(); + tta.setTenantId(form.getTenantId()); + tta.setTenantCode(form.getTenantCode()); + tta.setTrunkAttrId(attrId); + tta.setTrunkId(model.getId()); + tta.setCreatedBy(form.getAuthUser()); + tta.setCreatedTime(new Date()); + log.info("Insert trunk trunk attr [{}].", tta); + trunkTrunkAttrMapper.insert(tta); + } +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/service/impl/TrunkStrategyServiceImpl.java b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/TrunkStrategyServiceImpl.java new file mode 100644 index 00000000..a63ce0ab --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/service/impl/TrunkStrategyServiceImpl.java @@ -0,0 +1,25 @@ +package com.pudonghot.yo.cms.service.impl; + +import com.pudonghot.yo.cms.form.create.CreateFormTrunkStrategy; +import com.pudonghot.yo.cms.form.update.UpdateFormTrunkStrategy; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import com.pudonghot.yo.mapper.TrunkStrategyMapper; +import com.pudonghot.yo.model.domain.TrunkStrategy; +import com.pudonghot.yo.cms.service.TrunkStrategyService; +import com.wacai.tigon.service.support.BaseCrudByFormServiceSupport; + +/** + * @author Donghuang
+ * Nov 30, 2019 19:01:05 + */ +@Slf4j +@Service +public class TrunkStrategyServiceImpl + extends BaseCrudByFormServiceSupport + implements TrunkStrategyService { +} diff --git a/cms/src/main/java/com/pudonghot/yo/cms/util/SetUtils.java b/cms/src/main/java/com/pudonghot/yo/cms/util/SetUtils.java new file mode 100644 index 00000000..114f40c3 --- /dev/null +++ b/cms/src/main/java/com/pudonghot/yo/cms/util/SetUtils.java @@ -0,0 +1,34 @@ +package com.pudonghot.yo.cms.util; + +import org.apache.commons.lang3.tuple.Pair; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * @author Donghuang + * @date Feb 08, 2020 13:00:12 + */ +public class SetUtils { + + /** + * set diff + * @param thisSet this set + * @param otherSet other set + * @param element type + * @return diff result. left: add, right: remove + */ + public static Pair, Set> diff(final Set thisSet, final Set otherSet) { + if (otherSet == null || otherSet.isEmpty()) { + return Pair.of(Collections.emptySet(), thisSet); + } + + final Set add = new HashSet<>(otherSet); + add.removeAll(thisSet); + + final Set remove = new HashSet<>(thisSet); + remove.removeAll(otherSet); + return Pair.of(add, remove); + } +} diff --git a/cms/src/main/resources/application.properties b/cms/src/main/resources/application.properties new file mode 100644 index 00000000..ee14fbb6 --- /dev/null +++ b/cms/src/main/resources/application.properties @@ -0,0 +1,38 @@ +server.port=8180 +spring.application.name=yo-br-cms +spring.jackson.time-zone=GMT+8 +spring.jackson.serialization.write-dates-as-timestamps=true +spring.jackson.serialization.fail-on-empty-beans=false +spring.servlet.multipart.max-file-size=256MB +spring.servlet.multipart.max-request-size=256MB +site.context-path= + +# Feign +feign.client.config.default.logger-level=full + +# Datasource +yo.datasource.url=jdbc:mysql://172.18.4.35/yoqw?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai +yo.datasource.username=yoqw +yo.datasource.password=yoqw_query! + +# Datasource +yo.fs.datasource.url=jdbc:mysql://172.18.4.35/freeswitch?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai +yo.fs.datasource.username=freeswitch +yo.fs.datasource.password=RR!h5IpirsnJ + +# Redis +yo.redis.host=172.16.92.232 +yo.redis.port=6379 +yo.redis.password=123456 + +# CAS +tigon.shiro.cas.server.addr=https://cas.wacai.com +tigon.shiro.cas.client.addr=http://localhost:4200 +tigon.shiro.filter-chain=${site.context-path}/auth/login=anon \ +/=anon \ +/state-machine/**=anon \ +/nlp-component/**=anon \ +/index.html=anon \ +/assets/**=anon \ +/**=user + diff --git a/cms/src/main/resources/areacodes.txt b/cms/src/main/resources/areacodes.txt new file mode 100644 index 00000000..412f86dc --- /dev/null +++ b/cms/src/main/resources/areacodes.txt @@ -0,0 +1,332 @@ +010 北京 +020 广州 +021 上海 +022 天津 +023 重庆 +024 沈阳、铁岭、抚顺 +025 南京 +027 武汉 +028 成都、资阳、眉山 +029 西安、咸阳 +0310 邯郸 +0311 石家庄 +0312 保定 +0313 张家口 +0314 承德 +0315 唐山 +0316 廊坊 +0317 沧州 +0318 衡水 +0319 邢台 +0335 秦皇岛 +0349 朔州 +0350 忻州 +0351 太原 +0352 大同 +0353 阳泉 +0354 晋中 +0355 长治 +0356 晋城 +0357 临汾 +0358 吕梁 +0359 运城 +0370 商丘 +0371 郑州 +0372 安阳 +0373 新乡 +0374 许昌 +0375 平顶山 +0376 信阳 +0377 南阳 +0378 开封 +0379 洛阳 +0391 济源、焦作 +0392 鹤壁 +0393 濮阳 +0394 周口 +0395 漯河 +0396 驻马店 +0398 三门峡 +0411 大连 +0412 鞍山 +0414 本溪 +0415 丹东 +0416 锦州 +0417 营口 +0418 阜新 +0419 辽阳 +0421 朝阳 +0427 盘锦 +0429 葫芦岛 +0431 长春 +0432 吉林 +0433 延边朝鲜族自治州 +0434 四平 +0435 通化 +0436 白城 +0437 辽源 +0438 松原 +0439 白山 +0451 哈尔滨 +0452 齐齐哈尔 +0453 牡丹江 +0454 佳木斯 +0455 绥化 +0456 黑河 +0457 大兴安岭地区 +0458 伊春 +0459 大庆 +0464 七台河 +0467 鸡西 +0468 鹤岗 +0469 双鸭山 +0470 呼伦贝尔 +0471 呼和浩特 +0472 包头 +0473 乌海 +0474 乌兰察布 +0475 通辽 +0476 赤峰 +0477 鄂尔多斯 +0478 巴彦淖尔 +0479 锡林郭勒盟 +0482 兴安盟 +0483 阿拉善盟 +0510 无锡 +0511 镇江 +0512 苏州 +0513 南通 +0514 扬州 +0515 盐城 +0516 徐州 +0517 淮安 +0518 连云港 +0519 常州 +0523 泰州 +0527 宿迁 +0530 菏泽 +0531 济南 +0532 青岛 +0533 淄博 +0534 德州 +0535 烟台 +0536 潍坊 +0537 济宁 +0538 泰安 +0539 临沂 +0543 滨州 +0546 东营 +0550 滁州 +0551 合肥 +0552 蚌埠 +0553 芜湖 +0554 淮南 +0555 马鞍山 +0556 安庆 +0557 宿州 +0558 亳州、阜阳 +0559 黄山 +0561 淮北 +0562 铜陵 +0563 宣城 +0564 六安 +0565 巢湖 +0566 池州 +0570 衢州 +0571 杭州 +0572 湖州 +0573 嘉兴 +0574 宁波 +0575 绍兴 +0576 台州 +0577 温州 +0578 丽水 +0579 金华 +0580 舟山 +0591 福州 +0592 厦门 +0593 宁德 +0594 莆田 +0595 泉州 +0596 漳州 +0597 龙岩 +0598 三明 +0599 南平 +0631 威海 +0632 枣庄 +0633 日照 +0634 莱芜 +0635 聊城 +0660 汕尾 +0662 阳江 +0663 揭阳 +0668 茂名 +0691 西双版纳傣族自治州 +0692 德宏傣族景颇族自治州 +0701 鹰潭 +0710 襄樊 +0711 鄂州 +0712 孝感 +0713 黄冈 +0714 黄石 +0715 咸宁 +0716 荆州 +0717 宜昌 +0718 恩施土家族苗族自治州 +0719 十堰 +0722 随州 +0724 荆门 +0730 岳阳 +0731 长沙、湘潭、株洲 +0734 衡阳 +0735 郴州 +0736 常德 +0737 益阳 +0738 娄底 +0739 邵阳 +0743 湘西土家族苗族自治州 +0744 张家界 +0745 怀化 +0746 永州 +0750 江门 +0751 韶关 +0752 惠州 +0753 梅州 +0754 汕头 +0755 深圳 +0756 珠海 +0757 佛山 +0758 肇庆 +0759 湛江 +0760 中山 +0762 河源 +0763 清远 +0766 云浮 +0768 潮州 +0769 东莞 +0770 防城港 +0771 崇左、南宁 +0772 来宾、柳州 +0773 桂林 +0774 贺州、梧州 +0775 贵港、玉林 +0776 百色 +0777 钦州 +0778 河池 +0779 北海 +0790 新余 +0791 南昌 +0792 九江 +0793 上饶 +0794 抚州 +0795 宜春 +0796 吉安 +0797 赣州 +0798 景德镇 +0799 萍乡 +0812 攀枝花 +0813 自贡 +0816 绵阳 +0817 南充 +0818 达州 +0825 遂宁 +0826 广安 +0827 巴中 +0830 泸州 +0831 宜宾 +0832 内江 +0833 乐山 +0834 凉山彝族自治州 +0835 雅安 +0836 甘孜藏族自治州 +0837 阿坝藏族羌族自治州 +0838 德阳 +0839 广元 +0851 贵阳 +0852 遵义 +0853 安顺 +0854 黔南布依族苗族自治州 +0855 黔东南苗族侗族自治州 +0856 铜仁地区 +0857 毕节地区 +0858 六盘水 +0859 黔西南布依族苗族自治州 +0870 昭通 +0871 昆明 +0872 大理白族自治州 +0873 红河哈尼族彝族自治州 +0874 曲靖 +0875 保山 +0876 文山壮族苗族自治州 +0877 玉溪 +0878 楚雄彝族自治州 +0879 思茅 +0883 临沧 +0886 怒江傈僳族自治州 +0887 迪庆藏族自治州 +0888 丽江 +0891 拉萨 +0892 日喀则地区 +0893 山南地区 +0894 林芝地区 +0895 昌都地区 +0896 那曲地区 +0897 阿里地区 +0898 海口、三亚 +0901 省直辖行政单位 +0902 哈密地区 +0903 和田地区 +0906 阿勒泰地区 +0908 克孜勒苏柯尔克孜自治州 +0909 博尔塔拉蒙古自治州 +0911 延安 +0912 榆林 +0913 渭南 +0914 商洛 +0915 安康 +0916 汉中 +0917 宝鸡 +0919 铜川 +0930 临夏回族自治州 +0931 兰州 +0932 定西 +0933 平凉 +0934 庆阳 +0935 金昌、武威 +0936 张掖 +0937 嘉峪关、酒泉 +0938 天水 +0939 陇南 +0941 甘南藏族自治州 +0943 白银 +0951 银川 +0952 石嘴山 +0953 吴忠 +0954 固原 +0955 中卫 +0970 海北藏族自治州 +0971 西宁 +0972 海东地区 +0973 黄南藏族自治州 +0974 海南藏族自治州 +0975 果洛藏族自治州 +0976 玉树藏族自治州 +0979 海西蒙古族藏族自治州 +0990 克拉玛依 +0991 乌鲁木齐 +0994 昌吉回族自治州 +0995 吐鲁番地区 +0996 巴音郭楞蒙古自治州 +0997 阿克苏地区 +0998 喀什地区 +0999 伊犁哈萨克自治州 +8862 基隆市、台北市、台北县 +8863 花莲县、桃园县、新竹县、宜兰县 +88637 苗栗县 +8864 台中市、彰化县 +88649 南投县 +8865 嘉义县、云林县 +8866 澎湖县、台南市、台南县 +8867 高雄县 +8868 屏东县 +88689 台东县 diff --git a/cms/src/main/resources/logback.xml b/cms/src/main/resources/logback.xml new file mode 100644 index 00000000..88ba4a20 --- /dev/null +++ b/cms/src/main/resources/logback.xml @@ -0,0 +1,42 @@ + +> + + + + + + true + + %magenta(%d{"yyyy-MM-dd HH:mm:ss,SSS"}) [%thread] %highlight(%-5level) %cyan(%logger{15}) - %msg %n + + + + + ${log.dir}/${project.artifactId}.log + + %d{"yyyy-MM-dd HH:mm:ss,SSS"} [%thread] %-5level %logger{15} %msg %n + + + ${log.dir}/%d{yyyy-MM, aux}/${project.artifactId}-%d{yyyy-MM-dd}.%i.log + + 32MB + + + + + + + + + + + + + + + + + + + + diff --git a/cms/src/main/resources/spring/spring-ng-conf.xml b/cms/src/main/resources/spring/spring-ng-conf.xml new file mode 100644 index 00000000..ad1a18f0 --- /dev/null +++ b/cms/src/main/resources/spring/spring-ng-conf.xml @@ -0,0 +1,7 @@ + + + + diff --git a/cms/src/main/resources/static/check_backend_active.html b/cms/src/main/resources/static/check_backend_active.html new file mode 100644 index 00000000..a0aba931 --- /dev/null +++ b/cms/src/main/resources/static/check_backend_active.html @@ -0,0 +1 @@ +OK \ No newline at end of file diff --git a/cms/src/test/java/com/pudonghot/yo/cms/TestDriver.java b/cms/src/test/java/com/pudonghot/yo/cms/TestDriver.java new file mode 100644 index 00000000..1eb3a895 --- /dev/null +++ b/cms/src/test/java/com/pudonghot/yo/cms/TestDriver.java @@ -0,0 +1,22 @@ +package com.pudonghot.yo.cms; + +import org.junit.Test; +import org.junit.runner.RunWith; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Donghuang
+ * Oct 26, 2019 20:27:23 + */ +@Slf4j +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest(classes = YoCMS.class) +public class TestDriver { + + @Test + public void run() { + log.info("Run."); + } +} diff --git a/cms/src/test/resources/spring/spring-test.xml b/cms/src/test/resources/spring/spring-test.xml new file mode 100644 index 00000000..ac985d21 --- /dev/null +++ b/cms/src/test/resources/spring/spring-test.xml @@ -0,0 +1,6 @@ + + + diff --git a/codegen.properties b/codegen.properties new file mode 100644 index 00000000..4af1903e --- /dev/null +++ b/codegen.properties @@ -0,0 +1,59 @@ +# Server +server.port=8080 +spring.application.name=tigon-codegen +spring.jackson.time-zone=GMT+8 +spring.jackson.serialization.write-dates-as-timestamps=true +spring.jackson.serialization.fail-on-empty-beans=false +spring.servlet.multipart.max-file-size=256MB +spring.servlet.multipart.max-request-size=256MB + +# Data source +tigon.codegen.datasource.url=jdbc:mysql://172.18.4.35/yoqw?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai +tigon.codegen.datasource.username=yoqw +tigon.codegen.datasource.password=yoqw_query! + +# Code gen +tigon.codegen.base-package=com.pudonghot.corona +tigon.codegen.default-gen-items=model,form,mapper,service,controller +tigon.codegen.mapper.cache-enabled=false + +# Model +tigon.codegen.table-prefix=co +tigon.codegen.model.project-dir=lib/model +tigon.codegen.model.base-package=com.pudonghot.corona +tigon.codegen.model.base-class.name=BaseDomain +tigon.codegen.model.base-class.fields=id,active,createdTime,updatedTime,createdBy,updatedBy,note +tigon.codegen.model.base-class.full-name=com.pudonghot.corona.model.common.BaseDomain +tigon.codegen.model.base-class.import-required=true + +# Form Create +tigon.codegen.form-create.project-dir=lib/model +tigon.codegen.form-create.base-package=com.pudonghot.corona +tigon.codegen.form-create.base-class.name=CreateFormBase +tigon.codegen.form-create.base-class.full-name=com.pudonghot.corona.form.common.CreateFormBase +tigon.codegen.form-create.base-class.import-required=true + +# Form Update +tigon.codegen.form-update.project-dir=lib/model +tigon.codegen.form-update.base-package=com.pudonghot.corona +tigon.codegen.form-update.base-class.name=UpdateFormBase +tigon.codegen.form-update.base-class.full-name=com.pudonghot.corona.form.common.UpdateFormBase +tigon.codegen.form-update.base-class.import-required=true + +# Mapper +tigon.codegen.mapper.project-dir=lib/mapper +tigon.codegen.mapper.base-package=com.pudonghot.corona + +# Service +tigon.codegen.service.project-dir=cms +tigon.codegen.service.base-package=com.pudonghot.corona.cms +tigon.codegen.service-impl.project-dir=cms +tigon.codegen.service-impl.base-package=com.pudonghot.corona.cms + +# Controller +tigon.codegen.controller.project-dir=cms +tigon.codegen.controller.base-package=com.pudonghot.corona.cms + +# File Doc +tigon.codegen.file-doc.gen=true +tigon.codegen.file-doc.author=Donghuang diff --git a/codegen.sh b/codegen.sh new file mode 100755 index 00000000..029efeed --- /dev/null +++ b/codegen.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +### +# 辅助生成代码脚本 +# 可以通过codegen.properties配置数据库、代码包结构等 +## + +# get real path of softlink +get_real_path() { + local f="$1" + while [ -h "$f" ]; do + ls=`ls -ld "$f"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + f="$link" + else + f=`dirname "$f"`/"$link" + fi + done + echo "$f" +} + +prg_path=$(get_real_path "$0") +echo "Script path [$prg_path]" + +# Service Home +pushd $(dirname "$prg_path") +WORK_DIR=$(pwd) +echo "Work dir [$WORK_DIR]" + +mvn -T 2C -am -DskipTests -pl lib/tigon/codegen spring-boot:run \ + -Dspring-boot.run.arguments="--tigon.codegen.work-dir=$WORK_DIR --spring.config.location=$WORK_DIR/codegen.properties --server.port=8088" +popd + diff --git a/doc/images/install-lombok-plugin.png b/doc/images/install-lombok-plugin.png new file mode 100644 index 00000000..38e84c8f Binary files /dev/null and b/doc/images/install-lombok-plugin.png differ diff --git a/fsagent/.obelisk.yml b/fsagent/.obelisk.yml new file mode 100644 index 00000000..d8bbf4dc --- /dev/null +++ b/fsagent/.obelisk.yml @@ -0,0 +1,4 @@ +options: + env: SECURE_AGENT=true +docker: + from: dockerhub.test.wacai.info/yo/debian-freeswitch-jdk:0.0.1 diff --git a/fsagent/pom.xml b/fsagent/pom.xml new file mode 100644 index 00000000..9e68468b --- /dev/null +++ b/fsagent/pom.xml @@ -0,0 +1,121 @@ + + + 4.0.0 + yo-fsagent + 0.0.1-RELEASE + Yo FreeSWITCH Agent + Yo FreeSWITCH Agent + jar + + + com.pudonghot.yo + yo + 0.0.1-SNAPSHOT + ../ + + + + com.pudonghot.yo.fsagent.YoFsAgent + + + + + com.pudonghot.yo + yo-mapper + + + com.pudonghot.yo + yo-redis-raw + + + com.pudonghot.yo + yo-fs-mapper + + + com.pudonghot.yo + yo-util + + + com.pudonghot.yo + yo-cellphone-location + + + com.pudonghot.yo + yo-web-common + + + com.wacai.tigon + tigon-service-support + + + com.wacai.tigon + tigon-common + + + org.springframework.boot + spring-boot-starter-freemarker + + + org.springframework.boot + spring-boot-starter-actuator + + + com.pudonghot.yo + yo-fsesl + + + com.pudonghot.yo + yo-fsagent-api + + + com.pudonghot.yo + yo-http-client-apache + + + org.apache.dubbo + dubbo-spring-boot-starter + + + org.apache.dubbo + dubbo-dependencies-zookeeper + pom + + + org.apache.curator + curator-x-discovery + + + + org.projectlombok + lombok + provided + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + false + true + + + + pl.project13.maven + git-commit-id-plugin + + + + diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/YoFsAgent.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/YoFsAgent.java new file mode 100644 index 00000000..b6ac06a8 --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/YoFsAgent.java @@ -0,0 +1,22 @@ +package com.pudonghot.yo.fsagent; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author Donghuang
+ * Dec 02, 2019 21:06:05 + */ +@Slf4j +@SpringBootApplication +public class YoFsAgent { + + /** + * main + * @param args args + */ + public static void main(final String[] args) { + SpringApplication.run(YoFsAgent.class, args); + } +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/constant/ChannelLeg.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/constant/ChannelLeg.java new file mode 100644 index 00000000..e2208153 --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/constant/ChannelLeg.java @@ -0,0 +1,16 @@ +package com.pudonghot.yo.fsagent.constant; + +/** + * @author bingpo + * @date 2020/3/24 下午2:20 + */ +public enum ChannelLeg { + /** + * A leg 先桥接的leg,当前为主叫端 + */ + A, + /** + * B leg 后桥接的leg,当前为被叫端 + */ + B +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/AgentStatusController.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/AgentStatusController.java new file mode 100644 index 00000000..6a2c37ea --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/AgentStatusController.java @@ -0,0 +1,48 @@ +package com.pudonghot.yo.fsagent.controller; + +import com.pudonghot.yo.fsagent.service.AgentStatusService; +import lombok.extern.slf4j.Slf4j; +import javax.validation.constraints.NotBlank; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + * 为坐席拨打Dialplan设置状态提供接口,参见agent-ready.xml,agent-not-ready.xml + * + * @author Donghuang
+ * Dec 04, 2019 11:23:06 + */ +@Slf4j +@Controller +@RequestMapping("/agent") +public class AgentStatusController { + @Autowired + private AgentStatusService agentStatusService; + + @PostMapping("/ready") + public void ready( + @NotBlank + @RequestParam("domain") + final String domain, + @NotBlank + @RequestParam("user") + final String user) { + log.info("Domain [{}] agent [{}] ready.", domain, user); + agentStatusService.ready(domain, user); + } + + @PostMapping("/not-ready") + public void notReady( + @NotBlank + @RequestParam("domain") + final String domain, + @NotBlank + @RequestParam("user") + final String user) { + log.info("Domain [{}] agent [{}] not ready.", domain, user); + agentStatusService.notReady(domain, user); + } +} 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 new file mode 100644 index 00000000..e21b336e --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/BaseDialplanController.java @@ -0,0 +1,46 @@ +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 org.apache.commons.lang3.time.DateFormatUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.servlet.view.freemarker.FreeMarkerView; + +/** + * @author Donghuang
+ * Oct 26, 2019 15:57:14 + */ +@Slf4j +@RequestMapping(value = "/xml/dialplan") +public class BaseDialplanController extends BaseXmlController { + @Value("${yo.fsagent.recording.file-ext:.ogg}") + protected String recordingFileExt; + + protected FreeMarkerView view(final String domain, final String viewFile) { + final Tenant tenant = tenantService.find( + new Search(Tenant.REALM, domain) + .eq(Tenant.ACTIVE, true)); + if (tenant != null) { + final FreeMarkerView view = view("dialplan/" + viewFile); + attr(view, "tenant", tenant); + attr(view, "realm", domain); + return view; + } + return empty(SECTION_DIALPLAN); + } + + protected String recordingFile( + final String connId, + final String thisDn, + final String otherDn) { + + return "$${recordings_dir}/" + + connId + "_" + + DateFormatUtils.format(System.currentTimeMillis(), + "yyyy-MM-dd_HH-mm-ss") + "_" + + thisDn + "-" + + otherDn + recordingFileExt; + } +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/BaseXmlController.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/BaseXmlController.java new file mode 100644 index 00000000..70a1dbe5 --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/BaseXmlController.java @@ -0,0 +1,77 @@ +package com.pudonghot.yo.fsagent.controller; + +import freemarker.template.Configuration; +import java.nio.charset.StandardCharsets; +import org.springframework.http.MediaType; +import org.springframework.context.ApplicationContext; +import com.pudonghot.yo.fsagent.service.TenantService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.servlet.view.freemarker.FreeMarkerView; + +/** + * @author Donghuang
+ * Dec 12, 2019 09:49:30 + */ +public class BaseXmlController { + @Autowired + protected Configuration configuration; + @Autowired + protected ApplicationContext applicationContext; + @Autowired + protected TenantService tenantService; + + protected static final String SECTION_DIRECTORY = "directory"; + protected static final String SECTION_DIALPLAN = "dialplan"; + protected static final String SECTION_CONFIGURATION = "configuration"; + + /** + * init FreeMarker view + * + * @return init FreeMarker view + */ + protected FreeMarkerView view() { + final FreeMarkerView view = new FreeMarkerView(); + view.setApplicationContext(applicationContext); + view.setConfiguration(configuration); + view.setContentType(MediaType.APPLICATION_XML_VALUE); + view.setEncoding(StandardCharsets.UTF_8.name()); + return view; + } + + /** + * init FreeMarker view + * + * @param path view path + * @return init FreeMarker view + */ + protected FreeMarkerView view(final String path) { + final FreeMarkerView view = view(); + view.setUrl(path); + return view; + } + + /** + * add view attr + * + * @param view view + * @param name attr name + * @param val attr value + * @return view + */ + protected FreeMarkerView attr(FreeMarkerView view, String name, Object val) { + view.addStaticAttribute(name, val); + return view; + } + + /** + * Response empty view + * + * @param section config section + * @return empty xml view + */ + protected FreeMarkerView empty(final String section) { + final FreeMarkerView view = view("empty.xml"); + view.addStaticAttribute("section", section); + return view; + } +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/CdrController.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/CdrController.java new file mode 100644 index 00000000..92f07f30 --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/CdrController.java @@ -0,0 +1,23 @@ +package com.pudonghot.yo.fsagent.controller; + +import java.util.Map; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + * @author Donghuang
+ * Dec 04, 2019 11:23:06 + */ +@Slf4j +@Controller +public class CdrController { + + @RequestMapping("/cdr") + public void ready( + @RequestParam + final Map payload) { + log.info("Request params: [{}].", payload); + } +} 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 new file mode 100644 index 00000000..f5a36b04 --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/ConfigController.java @@ -0,0 +1,94 @@ +package com.pudonghot.yo.fsagent.controller; + +import java.util.Map; +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; +import org.springframework.stereotype.Controller; +import com.pudonghot.yo.fsagent.service.GatewayService; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.servlet.view.freemarker.FreeMarkerView; + +/** + * @author Donghuang
+ * Oct 26, 2019 15:57:14 + */ +@Slf4j +@Controller +@RequestMapping("/xml/config") +public class ConfigController extends BaseXmlController { + @Autowired + private GatewayService gatewayService; + @Autowired + @Qualifier("yoDataSource") + private DataSource dataSource; + + @RequestMapping + public FreeMarkerView config( + @RequestParam("key_value") + final String configKey, + @RequestParam + final Map params) { + + log.info("Fetch [{}] config XML.", configKey); + log.debug("XML config [{}].", params); + + return empty(SECTION_CONFIGURATION); + } + + @RequestMapping(params = "key_value=sofia.conf") + public FreeMarkerView sofiaConfig( + @RequestParam + final Map params) { + + log.info("Fetch sofia config XML."); + log.debug("XML sofia config [{}].", params); + + 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'))); + attr(view, "gateways", gateways); + + return view; + } + + /** + * !!!似乎不起作用!!!,FreeSWITCH还是会加载本地 + * + * @param params request params + * @return ODBC CDR config xml + */ + @RequestMapping(params = "key_value=odbc_cdr.conf") + public FreeMarkerView odbcCdrConfig( + @RequestParam + final Map params) { + + log.info("Fetch ODBC CDR config XML."); + log.debug("XML ODBC CDR [{}].", params); + + final FreeMarkerView view = view("config/odbc_cdr.conf.xml"); + attr(view, "odbcDsn", OdbcUtils.odbcDsn((DruidDataSource) dataSource)); + + return view; + } + + @RequestMapping(params = "key_value=event_socket.conf") + public FreeMarkerView eventSocketConfig( + @RequestParam + final Map params) { + + log.info("Fetch event socket config XML."); + log.debug("XML event socket config [{}].", params); + return view("config/event_socket.conf.xml"); + } +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/DialController.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/DialController.java new file mode 100644 index 00000000..0ebf6a02 --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/DialController.java @@ -0,0 +1,26 @@ +package com.pudonghot.yo.fsagent.controller; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Controller; +import com.pudonghot.yo.fsagent.api.DialService; +import com.pudonghot.yo.fsagent.api.response.RespDial; +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.beans.factory.annotation.Autowired; + +/** + * @author Donghuang + * @date Jun 12, 2020 10:04:20 + */ +@Slf4j +@Controller +public class DialController { + @Autowired + private DialService dialService; + + @PostMapping("/dial") + public RespDial dial(@RequestBody final ReqAgentDial req) { + return dialService.agentDial(req); + } +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/DialplanAgentStatusController.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/DialplanAgentStatusController.java new file mode 100644 index 00000000..6756cd16 --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/DialplanAgentStatusController.java @@ -0,0 +1,69 @@ +package com.pudonghot.yo.fsagent.controller; + +import java.util.Map; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.servlet.view.freemarker.FreeMarkerView; + +/** + * @author Donghuang
+ * Oct 26, 2019 15:57:14 + */ +@Slf4j +@Controller +public class DialplanAgentStatusController extends BaseDialplanController { + + @Value("${yo.fsagent.agent-status.audio.ready.ok}") + private String audioReadyOk; + @Value("${yo.fsagent.agent-status.audio.ready.err}") + private String audioReadyErr; + @Value("${yo.fsagent.agent-status.audio.not-ready.ok}") + private String audioNotReadyOk; + @Value("${yo.fsagent.agent-status.audio.not-ready.err}") + private String audioNotReadyErr; + + @RequestMapping(params = { + "variable_domain_name", + "Caller-Username", + "Caller-Destination-Number=AgentReady" + }) + public FreeMarkerView agentReady( + @RequestParam("variable_domain_name") + final String domain, + @RequestParam("Caller-Username") + final String user, + @RequestParam + final Map params) { + + log.info("XML dialplan ready of domain [{}] user [{}].", domain, user); + log.debug("XML dialplan ready params [{}].", params); + final FreeMarkerView view = view(domain, "agent-ready.xml"); + attr(view, "audioOk", audioReadyOk); + attr(view, "audioErr", audioReadyErr); + return view; + } + + @RequestMapping(params = { + "variable_domain_name", + "Caller-Username", + "Caller-Destination-Number=AgentNotReady" + }) + public FreeMarkerView agentNotReady( + @RequestParam("variable_domain_name") + final String domain, + @RequestParam("Caller-Username") + final String user, + @RequestParam + final Map params) { + + log.info("XML dialplan not ready of domain [{}] user [{}].", domain, user); + log.debug("XML dialplan not ready params [{}].", params); + final FreeMarkerView view = view(domain, "agent-not-ready.xml"); + attr(view, "audioOk", audioNotReadyOk); + attr(view, "audioErr", audioNotReadyErr); + return view; + } +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/DialplanEchoController.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/DialplanEchoController.java new file mode 100644 index 00000000..5c307a1d --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/DialplanEchoController.java @@ -0,0 +1,35 @@ +package com.pudonghot.yo.fsagent.controller; + +import java.util.Map; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.servlet.view.freemarker.FreeMarkerView; + +/** + * @author Donghuang + * @date Feb 13, 2020 14:07:52 + */ +@Slf4j +@Controller +public class DialplanEchoController extends BaseDialplanController { + + @RequestMapping(params = { + "variable_domain_name", + "Caller-Username", + "Caller-Destination-Number=Echo" + }) + public FreeMarkerView echo( + @RequestParam("variable_domain_name") + final String domain, + @RequestParam("Caller-Username") + final String user, + @RequestParam + final Map params) { + + log.info("XML dialplan echo of domain [{}] user [{}].", domain, user); + log.debug("XML dialplan echo params [{}].", params); + return view(domain, "echo.xml"); + } +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/DialplanInboundController.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/DialplanInboundController.java new file mode 100644 index 00000000..dc04ba9d --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/DialplanInboundController.java @@ -0,0 +1,106 @@ +package com.pudonghot.yo.fsagent.controller; + +import java.util.Map; + +import com.pudonghot.yo.fsagent.service.AgentGroupService; +import com.pudonghot.yo.model.domain.Agent; +import com.pudonghot.yo.model.domain.AgentGroup; +import com.pudonghot.yo.model.domain.Tenant; +import com.pudonghot.yo.model.domain.Trunk; +import lombok.extern.slf4j.Slf4j; +import com.wacai.tigon.mybatis.Search; +import com.wacai.tigon.sequence.IdSequence; +import org.springframework.util.DigestUtils; +import org.springframework.stereotype.Controller; +import com.pudonghot.yo.cellphone.privacy.PrivacyLevel; +import com.pudonghot.yo.fsagent.service.AgentService; +import com.pudonghot.yo.fsagent.service.TrunkService; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.beans.factory.annotation.Autowired; +import com.pudonghot.yo.cellphone.privacy.NumberPrivacyUtils; +import org.springframework.web.servlet.view.freemarker.FreeMarkerView; + +/** + * @author Donghuang
+ * Oct 26, 2019 15:57:14 + */ +@Slf4j +@Controller +@RequestMapping(value = "/xml/dialplan") +public class DialplanInboundController extends BaseDialplanController { + @Autowired + private IdSequence idSeq; + @Autowired + private TrunkService trunkService; + @Autowired + private AgentService agentService; + @Autowired + private AgentGroupService agentGroupService; + + @RequestMapping(params = "Caller-Context=public") + public FreeMarkerView inbound( + @RequestParam("Caller-Network-Addr") + final String gateway, + @RequestParam("Caller-ANI") + final String ani, + @RequestParam("Caller-Destination-Number") + final String dnis, + @RequestParam + final Map params) { + log.info("Inbound gateway [{}], ANI [{}], DNIS [{}].", gateway, ani, dnis); + log.debug("XML inbound dialplan params [{}].", params); + + final FreeMarkerView view = view(); + final String connId = idSeq.get(); + attr(view, "connId", connId); + attr(view, "bridgeUuid", idSeq.get()); + attr(view, "recordingLoc", + recordingFile(connId, + DigestUtils.md5DigestAsHex(ani.getBytes()), dnis)); + + final Trunk trunk = trunkService.find( + new Search(Trunk.CPN, dnis).eq(Trunk.ACTIVE, true)); + if (trunk == null) { + log.warn("No trunk of cpn [{}] found, ignore inbound.", dnis); + return empty(SECTION_DIALPLAN); + } + final Integer tenantId = trunk.getTenantId(); + final Tenant tenant = tenantService.find(tenantId); + attr(view, "tenant", tenant); + + final Trunk.InboundTargetType inboundTargetType = trunk.getInboundTargetType(); + final Integer inboundTarget = trunk.getInboundTarget(); + if (inboundTargetType == Trunk.InboundTargetType.AGENT) { + final Agent calledAgent = agentService.find(inboundTarget); + if (calledAgent == null) { + log.error("No agent [{}] found to transfer, ignore inbound.", inboundTarget); + return empty(SECTION_DIALPLAN); + } + final AgentGroup agentGroup = agentGroupService.find( + calledAgent.getGroupId()); + if (agentGroup == null) { + log.error("No agent group [{}] found to transfer, ignore inbound.", + calledAgent.getGroupId()); + return empty(SECTION_DIALPLAN); + } + view.setUrl("dialplan/inbound-to-agent.xml"); + attr(view, "calleeIdNumber", ani); + attr(view, "calleeIdName", ani); + + if (agentGroup.getPrivacyLevel() != AgentGroup.PrivacyLevel.NONE) { + attr(view, "callerIdNumber", "PRIVACY_INBOUND"); + attr(view, "callerIdName", NumberPrivacyUtils.mask(ani, PrivacyLevel.HEAVY)); + } + else { + attr(view, "callerIdNumber", ani); + attr(view, "callerIdName", ani); + } + attr(view, "calledAgent", calledAgent); + } + + // TODO Inbound to QUEUE + + 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 new file mode 100644 index 00000000..f9fee304 --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/DialplanInternalController.java @@ -0,0 +1,66 @@ +package com.pudonghot.yo.fsagent.controller; + +import java.util.Map; + +import com.pudonghot.yo.model.domain.Agent; +import lombok.extern.slf4j.Slf4j; +import com.wacai.tigon.sequence.IdSequence; +import org.springframework.stereotype.Controller; +import com.pudonghot.yo.fsagent.service.AgentService; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.servlet.view.freemarker.FreeMarkerView; + +/** + * @author Donghuang
+ * Oct 26, 2019 15:57:14 + */ +@Slf4j +@Controller +public class DialplanInternalController extends BaseDialplanController { + @Autowired + private AgentService agentService; + @Autowired + private IdSequence idSeq; + + @RequestMapping(params = { + "variable_domain_name", + "Caller-Username", + "Caller-Destination-Number" + }) + public FreeMarkerView domainDialplan( + @RequestParam("variable_domain_name") + final String domain, + @RequestParam("Caller-Username") + final String user, + @RequestParam("Caller-Destination-Number") + final String calledNumber, + @RequestParam + final Map params) { + + log.info("XML dialplan of domain [{}].", domain); + log.debug("XML dialplan params [{}].", params); + + 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())); + return view; + } + + return view(domain, "dialplan.xml"); + } +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/DirectoryController.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/DirectoryController.java new file mode 100644 index 00000000..b1ab24ce --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/DirectoryController.java @@ -0,0 +1,99 @@ +package com.pudonghot.yo.fsagent.controller; + +import java.util.Map; + +import com.pudonghot.yo.fsagent.service.AgentGroupService; +import com.pudonghot.yo.fsagent.service.AgentService; +import com.pudonghot.yo.model.domain.Agent; +import com.pudonghot.yo.model.domain.AgentGroup; +import com.pudonghot.yo.model.domain.Tenant; +import lombok.extern.slf4j.Slf4j; +import com.wacai.tigon.mybatis.Search; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.servlet.view.freemarker.FreeMarkerView; + +/** + * @author Donghuang
+ * Oct 26, 2019 15:57:14 + */ +@Slf4j +@Controller +public class DirectoryController extends BaseXmlController { + @Autowired + private AgentService agentService; + @Autowired + private AgentGroupService agentGroupService; + + @RequestMapping("/xml/directory") + public FreeMarkerView general( + @RequestParam final Map params) { + + log.debug("XML directory params [{}].", params); + final FreeMarkerView view = view("directory/general.xml"); + attr(view, "tenants", + tenantService.list(new Search(Tenant.ACTIVE, true))); + return view; + } + + /** + * purpose=gateways mod_sofia + * purpose=network-list + * + * @param params + * @return + */ + @RequestMapping(value = "/xml/directory", params = "purpose") + public FreeMarkerView purpose( + @RequestParam final Map params) { + + log.debug("XML directory purpose params [{}].", params); + final FreeMarkerView view = view("directory/general.xml"); + attr(view, "tenants", + tenantService.list(new Search(Tenant.ACTIVE, true))); + return view; + } + + @RequestMapping(value = "/xml/directory", + params = {"domain", "user"}) + public FreeMarkerView authDirectory( + @RequestParam("domain") + final String domain, + @RequestParam("user") + final String user, + @RequestParam final Map params) { + + log.info("XML registration directory domain [{}] user [{}].", domain, user); + log.debug("XML registration directory params [{}].", params); + + final Agent agent = agentService.findByDomainAndAgent(domain, user); + if (agent == null) { + log.warn("Agent [{}] not found.", user); + return empty(SECTION_DIRECTORY); + } + + if (!agent.getActive()) { + log.warn("Agent [{}] is not active.", user); + return empty(SECTION_DIRECTORY); + } + + final AgentGroup agentGroup = agentGroupService.find(agent.getGroupId()); + if (agentGroup == null) { + log.warn("No agent [{}] group found.", user); + return empty(SECTION_DIRECTORY); + } + + if (!agentGroup.getActive()) { + log.warn("Agent [{}] group [{}] is not active.", user, agentGroup.getIdentifier()); + return empty(SECTION_DIRECTORY); + } + + final FreeMarkerView view = view("directory/registration.xml"); + attr(view, "domain", domain); + attr(view, "agent", agent); + attr(view, "agentGroup", agentGroup); + return view; + } +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/EslController.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/EslController.java new file mode 100644 index 00000000..abee2f57 --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/EslController.java @@ -0,0 +1,25 @@ +package com.pudonghot.yo.fsagent.controller; + +import lombok.extern.slf4j.Slf4j; +import org.freeswitch.esl.client.inbound.Client; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * @author Donghuang
+ * Dec 09, 2019 17:25:57 + */ +@Slf4j +@Controller +@RequestMapping("/esl") +public class EslController { + @Autowired + private Client client; + + @PostMapping("/connect") + public void connect() { + client.connect(); + } +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/OdbcDsnController.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/OdbcDsnController.java new file mode 100644 index 00000000..587f1eeb --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/OdbcDsnController.java @@ -0,0 +1,41 @@ +package com.pudonghot.yo.fsagent.controller; + +import javax.sql.DataSource; + +import com.pudonghot.yo.fsagent.util.OdbcUtils; +import lombok.extern.slf4j.Slf4j; +import com.alibaba.druid.pool.DruidDataSource; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + * @author Donghuang
+ * Jan 06, 2020 18:05:31 + */ +@Slf4j +@Controller +@RequestMapping("/odbc-dsn") +public class OdbcDsnController { + @Autowired + @Qualifier("yoDataSource") + private DataSource broDataSource; + @Autowired + @Qualifier("yoFsDataSource") + private DataSource fsDataSource; + + @ResponseBody + @PostMapping("/freeswitch") + public String freeSWITCH() { + return OdbcUtils.odbcDsn((DruidDataSource) fsDataSource); + } + + @ResponseBody + @PostMapping("/bro") + public String bro() { + return OdbcUtils.odbcDsn((DruidDataSource) broDataSource); + } +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/OnAnswerController.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/OnAnswerController.java new file mode 100644 index 00000000..fe5cd707 --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/OnAnswerController.java @@ -0,0 +1,44 @@ +package com.pudonghot.yo.fsagent.controller; + +import lombok.extern.slf4j.Slf4j; +import org.freeswitch.esl.client.inbound.Client; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.freeswitch.esl.client.transport.command.SendEvent; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * @author Donghuang + * @date Apr 15, 2020 14:30:59 + */ +@Slf4j +@Controller +public class OnAnswerController { + @Autowired + private Client client; + + @PostMapping("/on-answer") + public void answer( + @RequestParam("tenantId") + final Integer tenantId, + @RequestParam("realm") + final String realm, + @RequestParam("uuid") + final String uuid, + @RequestParam("user") + final String user) { + log.info("On call answer, tenant [{}], realm [{}], uuid [{}], user [{}].", tenantId, realm, uuid, user); + + final SendEvent sendEvent = new SendEvent(SendEvent.NOTIFY) + .addLine("Profile", realm) + .addLine("Event-String", "CALL_ESTABLISHED") + .addLine("User", user) + .addLine("Host", realm) + .addLine("Content-Type", "plain/text") + .addLine("Content-Length", uuid.length()) + .addLine("") + .addLine(uuid); + client.api(sendEvent); + } +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/PatchController.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/PatchController.java new file mode 100644 index 00000000..b971f17c --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/PatchController.java @@ -0,0 +1,23 @@ +package com.pudonghot.yo.fsagent.controller; + +import org.springframework.core.io.Resource; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.core.io.ClassPathResource; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + * @author Donghuang + * @date Mar 05, 2020 10:25:45 + */ +@Controller +public class PatchController { + + @RequestMapping("/patch/fs-conf") + public ResponseEntity fsConfPatch() { + return ResponseEntity.ok() + .contentType(MediaType.TEXT_PLAIN) + .body(new ClassPathResource("/freeswitch-conf.patch")); + } +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/PostRecordingController.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/PostRecordingController.java new file mode 100644 index 00000000..9a436632 --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/PostRecordingController.java @@ -0,0 +1,28 @@ +package com.pudonghot.yo.fsagent.controller; + +import com.pudonghot.yo.fsagent.service.PostRecordingService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * @author Donghuang
+ * Jan 06, 2020 18:05:31 + */ +@Slf4j +@Controller +public class PostRecordingController { + @Autowired + private PostRecordingService postRecordingService; + + @PostMapping("/post-rec") + public void postRecording( + @RequestParam("tenantId") + final Integer tenantId, + @RequestParam("loc") + final String location) { + postRecordingService.postRecording(tenantId, location); + } +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/SiteController.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/SiteController.java new file mode 100644 index 00000000..b47751e2 --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/controller/SiteController.java @@ -0,0 +1,18 @@ +package com.pudonghot.yo.fsagent.controller; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + * @author Donghuang
+ * Oct 26, 2019 15:57:14 + */ +@Slf4j +@Controller +public class SiteController { + + @RequestMapping("/") + public void index() { + } +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/AgentRegister.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/AgentRegister.java new file mode 100644 index 00000000..5c5b3f57 --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/AgentRegister.java @@ -0,0 +1,44 @@ +package com.pudonghot.yo.fsagent.listener; + +import com.pudonghot.yo.fsagent.service.AgentStatusService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +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
+ * Dec 12, 2019 16:07:03 + */ +@Slf4j +@Component +public class AgentRegister { + @Autowired + private AgentStatusService agentStatusService; + + /** + * {@inheritDoc} + */ + @Async + @EventListener(value = Event.class, + condition = "#root.args[0].getName() == 'CUSTOM'" + + " and #root.args[0].getSubclass() == 'sofia::register'") + public void onAgentRegister(final Event event) { + log.debug("On agent register event [{}].", event.getHeaders()); + + final String realm = event.getHeader("realm"); + final String user = event.getHeader("username"); + + log.info("On realm [{}] agent [{}] register.", realm, user); + + if (StringUtils.isNotBlank(realm) && StringUtils.isNotBlank(user)) { + agentStatusService.online(realm, user); + } + else { + log.warn("No valid realm and agent found, ignore."); + } + } +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/AgentUnregister.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/AgentUnregister.java new file mode 100644 index 00000000..781ef6bc --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/AgentUnregister.java @@ -0,0 +1,41 @@ +package com.pudonghot.yo.fsagent.listener; + +import com.pudonghot.yo.fsagent.service.AgentStatusService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +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
+ * Dec 12, 2019 16:07:03 + */ +@Slf4j +@Component +public class AgentUnregister { + @Autowired + private AgentStatusService agentStatusService; + + /** + * {@inheritDoc} + */ + @Async + @EventListener(value = Event.class, + condition = "#root.args[0].getName() == 'CUSTOM'" + + " and #root.args[0].getSubclass() == 'sofia::unregister'") + public void onAgentUnregister(final Event event) { + log.debug("On agent unregister event [{}].", event.getHeaders()); + + final String realm = event.getHeader("realm"); + final String user = event.getHeader("username"); + log.info("On domain [{}] agent [{}] unregister.", realm, user); + + if (StringUtils.isNotBlank(realm) && StringUtils.isNotBlank(user)) { + log.info("Offline realm [{}] user [{}].", realm, user); + agentStatusService.offline(realm, user); + } + } +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/ApplicationStartup.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/ApplicationStartup.java new file mode 100644 index 00000000..00048225 --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/ApplicationStartup.java @@ -0,0 +1,208 @@ +package com.pudonghot.yo.fsagent.listener; + +import java.io.File; +import java.net.InetAddress; +import java.lang.reflect.Field; + +import com.pudonghot.yo.common.httpclient.HttpClient; +import com.pudonghot.yo.util.LanAddrUtils; +import com.pudonghot.yo.util.ShellCommandUtils; +import lombok.extern.slf4j.Slf4j; +import java.util.concurrent.TimeUnit; +import org.springframework.util.Assert; +import org.apache.curator.x.discovery.*; +import org.apache.commons.lang3.StringUtils; +import com.pudonghot.yo.fs.model.FsNode; +import org.springframework.util.ReflectionUtils; +import org.freeswitch.esl.client.inbound.Client; +import org.springframework.stereotype.Component; +import org.apache.dubbo.common.config.Environment; +import org.apache.curator.framework.CuratorFramework; +import org.springframework.context.ApplicationContext; +import org.springframework.scheduling.annotation.Async; +import org.springframework.context.ApplicationListener; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.apache.curator.x.discovery.details.JsonInstanceSerializer; +import org.apache.dubbo.remoting.zookeeper.curator.CuratorZookeeperClient; +import org.apache.dubbo.configcenter.support.zookeeper.ZookeeperDynamicConfiguration; + +/** + * @author Donghuang
+ * Dec 08, 2019 21:52:55 + */ +@Slf4j +@Component +public class ApplicationStartup implements ApplicationListener { + + @Value("${yo.fsagent.freeswitch-config-dir:/etc/freeswitch}") + private String freeswitchConfigDir; + @Value("${yo.fsagent.freeswitch-bin:/usr/bin/freeswitch}") + private String freeswitchBin; + @Value("${yo.fsagent.application-ready-wait-time:8}") + private int appReadyWaitTime; + @Value("${yo.fsagent.freeswitch-start-wait-time:36}") + private int waitTime; + @Autowired + private Client client; + @Autowired + private ApplicationContext applicationContext; + @Autowired + private HttpClient httpClient; + @Value("${dubbo.application.qos-enable:false}") + private boolean dubboQosEnable; + @Value("http://localhost:${dubbo.application.qos-port:22222}/online") + private String dubboQosOnlineAddr; + @Value("${yo.fsagent.online-mode:true}") + private boolean onlineMode; + @Value("${yo.fsagent.fs-sip.port:5060}") + private int fsSipPort; + @Value("${yo.fsagent.fs-reg-path:/FreeSWITCH/Nodes}") + private String fsRegPath; + @Value("${yo.fsagent.fs-reg-name:FreeSWITCH}") + private String fsRegName; + + /** + * {@inheritDoc} + */ + @Async + @Override + public void onApplicationEvent(final ApplicationReadyEvent event) { + log.info("Application ready, will start FreeSWITCH, connect FreeSWITCH ESL, online Dubbo, online FreeSWITCH node."); + try { + TimeUnit.SECONDS.sleep(appReadyWaitTime); + } + catch (InterruptedException e) { + log.error("Thread sleep ERROR caused", e); + } + + startFreeSWITCH(); + connectFsEsl(); + if (onlineMode) { + onlineDubbo(); + onlineFsNode(); + } + else { + log.info("Running on offline mode."); + } + } + + void startFreeSWITCH() { + log.info("Application ready, start FreeSWITCH."); + + if (new File(freeswitchConfigDir).isDirectory()) { + log.info("FreeSWITCH config dir [{}] found, patch it.", freeswitchConfigDir); + final ShellCommandUtils.ExecResult patchResult = + ShellCommandUtils.execCmd(new String[] { + "/bin/bash", "-c", + "curl -Lk http://localhost:8080/patch/fs-conf | patch -f -p 1 -d " + freeswitchConfigDir + }); + + log.info("Patch exit code [{}].", patchResult.getExitCode()); + log.info("Patch stdout [{}].", StringUtils.join(patchResult.getStdout(), "\n")); + log.info("Patch stderr [{}].", StringUtils.join(patchResult.getStderr(), "\n")); + } + + if (new File(freeswitchBin).exists()) { + log.info("FreeSWITCH bin [{}] found, start.", freeswitchBin); + ShellCommandUtils.execCmd(new String[] {freeswitchBin, "-nonat", "-ncwait"}); + try { + TimeUnit.SECONDS.sleep(waitTime); + } + catch (final InterruptedException e) { + log.warn("Wait FreeSWICH start exception caused.", e); + } + } + else { + log.warn("No FreeSWITCH bin [{}] found, ignore start.", freeswitchBin); + } + } + + void connectFsEsl() { + log.info("Application ready, connect FreeSWTICH ESL."); + client.setConnectedCallback(() -> { + log.info("Register FreeSWITCH ESL events."); + client.event("CUSTOM sofia::register"); + client.event("CUSTOM sofia::unregister"); + client.event("CHANNEL_CREATE"); + client.event("CHANNEL_DESTROY"); + client.event("CHANNEL_ANSWER"); + client.event("CHANNEL_HANGUP_COMPLETE"); + }); + client.connect(); + client.addEventListener(applicationContext::publishEvent); + } + + void onlineDubbo() { + log.info("Application ready, online dubbo service."); + if (dubboQosEnable) { + log.info("Online dubbo service."); + final String onlineResult = httpClient.get(dubboQosOnlineAddr).asString(); + log.info("Online dubbo service result [{}].", onlineResult); + Assert.state("OK".equals(onlineResult), "Online dubbo service error caused"); + } + } + + void onlineFsNode() { + log.info("Application ready, online FreeSWITCH node."); + if (!new File(freeswitchBin).exists()) { + log.warn("No FreeSWICH bin [{}] found, ignore.", freeswitchBin); + return; + } + + final CuratorFramework curatorZkClient = getField(CuratorZookeeperClient.class, + getField(ZookeeperDynamicConfiguration.class, + Environment.getInstance().getDynamicConfiguration().get(), "zkClient"), "client"); + final ServiceInstanceBuilder serviceInstanceBuilder; + try { + serviceInstanceBuilder = ServiceInstance.builder(); + } + catch (Exception e) { + log.error("Init service instance build error caused", e); + throw new IllegalStateException( + "Init service instance build error caused", e); + } + + final ServiceDiscovery serviceDiscovery = + ServiceDiscoveryBuilder.builder(FsNode.class) + .basePath(fsRegPath) + .client(curatorZkClient) + .serializer(new JsonInstanceSerializer<>(FsNode.class)) + .build(); + + final InetAddress localAddr = LanAddrUtils.getLocalHostLanAddr(); + final String localIp = localAddr.getHostAddress(); + final String hostName = localAddr.getHostName(); + log.info("Local address [{}][{}] got.", localIp, hostName); + + serviceInstanceBuilder.address(localIp) + .port(fsSipPort) + .name(fsRegName) + .payload(new FsNode(localIp, hostName, "sip:" + localIp + ":" + fsSipPort)); + try { + serviceDiscovery.registerService(serviceInstanceBuilder.build()); + } + catch (Exception e) { + log.error("Register service instance exception caused", e); + throw new IllegalStateException( + "Register service instance exception caused", e); + } + + try { + serviceDiscovery.start(); + } + catch (Exception e) { + log.error("Start service discovery exception caused", e); + throw new IllegalStateException( + "Start service discovery exception caused", e); + } + } + + @SuppressWarnings("unchecked") + T getField(final Class clazz, final Object obj, final String name) { + final Field field = ReflectionUtils.findField(clazz, name); + ReflectionUtils.makeAccessible(field); + return (T) ReflectionUtils.getField(field, obj); + } +} 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 new file mode 100644 index 00000000..c6e68da5 --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/ChannelAnswer.java @@ -0,0 +1,30 @@ +package com.pudonghot.yo.fsagent.listener; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.freeswitch.esl.client.transport.event.Event; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; + +/** + * @author Donghuang + * @date Mar 09, 2020 17:04:28 + */ +@Slf4j +@Component +public class ChannelAnswer { + + /** + * {@inheritDoc} + */ + @Async + @EventListener(value = Event.class, + condition = "#root.args[0].getName() == 'CHANNEL_ANSWER'") + public void onChannelAnswer(final Event event) { + // event.getHeader("Caller-Context") + log.debug("On channel answer event [{}] [{}].", event, event.getHeaders()); + + final String presenceId = event.getHeader("variable_presence_id"); + log.info("On channel [{}] answer event.", presenceId); + } +} 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 new file mode 100644 index 00000000..be419ee7 --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/ChannelCreate.java @@ -0,0 +1,45 @@ +package com.pudonghot.yo.fsagent.listener; + +import com.pudonghot.yo.fsagent.service.AgentStatusService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +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 Mar 09, 2020 17:04:28 + */ +@Slf4j +@Component +public class ChannelCreate { + + @Autowired + private AgentStatusService agentStatusService; + + /** + * {@inheritDoc} + */ + @Async + @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()); + + final String presenceId = event.getHeader("variable_presence_id"); + log.info("On channel [{}] create event.", presenceId); + + if (StringUtils.isNotBlank(presenceId)) { + final String[] userInfo = presenceId.split("@"); + if (userInfo.length == 2) { + agentStatusService.inACall(userInfo[1], userInfo[0]); + } + else { + log.warn("Ignore invalid presence id [{}].", presenceId); + } + } + } +} 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 new file mode 100644 index 00000000..85d7db40 --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/ChannelDestroy.java @@ -0,0 +1,45 @@ +package com.pudonghot.yo.fsagent.listener; + +import com.pudonghot.yo.fsagent.service.AgentStatusService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.freeswitch.esl.client.transport.event.Event; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * @author Donghuang + * @date Mar 09, 2020 17:04:28 + */ +@Slf4j +@Component +public class ChannelDestroy { + + @Autowired + private AgentStatusService agentStatusService; + + /** + * {@inheritDoc} + */ + @Async + @EventListener(value = Event.class, + condition = "#root.args[0].getName() == 'CHANNEL_DESTROY'") + public void onChannelDestroy(final Event event) { + log.debug("On channel create event [{}] [{}].", event, event.getHeaders()); + + final String presenceId = event.getHeader("variable_presence_id"); + log.info("On channel [{}] destroy event.", presenceId); + + if (StringUtils.isNotBlank(presenceId)) { + final String[] userInfo = presenceId.split("@"); + if (userInfo.length == 2) { + agentStatusService.idle(userInfo[1], userInfo[0]); + } + else { + log.warn("Ignore invalid presence id [{}].", presenceId); + } + } + } +} 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 new file mode 100644 index 00000000..3d361a03 --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/listener/ChannelHangupComplete.java @@ -0,0 +1,80 @@ +package com.pudonghot.yo.fsagent.listener; + +import java.util.Date; +import java.text.ParseException; + +import com.pudonghot.yo.mapper.CallingListMapper; +import com.pudonghot.yo.model.domain.CallingList; +import lombok.extern.slf4j.Slf4j; +import com.wacai.tigon.mybatis.Search; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.DateUtils; +import org.springframework.stereotype.Component; +import org.freeswitch.esl.client.transport.event.Event; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import com.pudonghot.yo.fsagent.constant.ChannelLeg; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * @author Donghuang + * @date Mar 09, 2020 17:04:28 + */ +@Slf4j +@Component +public class ChannelHangupComplete { + + @Autowired + private CallingListMapper callingListMapper; + + @Value("${br.fsagent.endcall.topic:loan.yo.br.endcall}") + private String endCallTopic; + + + /** + * {@inheritDoc} + */ + @Async + @EventListener(value = Event.class, + condition = "#root.args[0].getName() == 'CHANNEL_HANGUP_COMPLETE'") + public void onChannelHangupComplete(final Event event) { + log.info("On channel hangup event [{}] [{}].", event, event.getHeaders()); + + // TODO 判断是外呼活动的拨打 + + final String connId = event.getHeader("variable_x_conn_id"); + final String channelLeg =event.getHeader("variable_x_channel_leg"); + log.info("On channel [{}] hangup leg [{}].", connId, channelLeg); + + if (StringUtils.isNotBlank(connId) && ChannelLeg.B.name().equals(channelLeg)) { + final String hangupCause = event.getHeader("Hangup-Cause"); + log.info("Call [{}] hangup [{}].", connId, hangupCause); + final CallingList callingList = callingListMapper.find(new Search(CallingList.CALL_UUID, connId)); + if (callingList != null) { + callingList.setStatus(CallingList.Status.CALLED); + callingList.setCallStartTime(parse(event.getHeader("variable_start_stamp"))); + final Date establishedTime = parse(event.getHeader("variable_answer_stamp")); + callingList.setCallEstablishedTime(establishedTime); + callingList.setCallEndTime(parse(event.getHeader("variable_end_stamp"))); + callingList.setConnected(establishedTime != null); + callingList.setCallResult(hangupCause); + callingListMapper.update(callingList); + } + else { + log.info("no calling list found by connId [{}]", connId); + } + } + } + + private Date parse(final String date) { + try { + return StringUtils.isNotBlank(date) ? + DateUtils.parseDate(date, "yyyy-MM-dd HH:mm:ss") : null; + } + catch (final ParseException e) { + log.error("Parse data [{}] error caused", date, e); + return null; + } + } +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/AgentGroupService.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/AgentGroupService.java new file mode 100644 index 00000000..aef8023e --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/AgentGroupService.java @@ -0,0 +1,12 @@ +package com.pudonghot.yo.fsagent.service; + +import com.wacai.tigon.service.BaseQueryService; +import com.pudonghot.yo.model.domain.AgentGroup; + +/** + * @author Donghuang
+ * Dec 04, 2019 17:03:49 + */ +public interface AgentGroupService + extends BaseQueryService { +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/AgentService.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/AgentService.java new file mode 100644 index 00000000..717880b2 --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/AgentService.java @@ -0,0 +1,46 @@ +package com.pudonghot.yo.fsagent.service; + +import com.pudonghot.yo.model.domain.Agent; +import com.wacai.tigon.service.BaseQueryService; + +/** + * @author Donghuang
+ * Nov 02, 2019 12:52:34 + */ +public interface AgentService + extends BaseQueryService { + + /** + * find agent of domain + * @param domain domain + * @param agent agent + * @return agent + */ + Agent findByDomainAndAgent(String domain, String agent); + + /** + * find agent of called + * @param domain domain + * @param calledNumber called number + * @return agent + */ + Agent findOfCalled(String domain, String calledNumber); + + /** + * lock idle agent of queue + * + * @param tenantId tenant id + * @param queueId queue id + * @return agent locked + */ + Agent lockIdleOfQueue(Integer tenantId, Integer queueId); + + /** + * lock idle agent of group + * + * @param tenantId tenant id + * @param groupId group id + * @return agent locked + */ + Agent lockIdleOfGroup(Integer tenantId, Integer groupId); +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/AgentStatusService.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/AgentStatusService.java new file mode 100644 index 00000000..9101dbbe --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/AgentStatusService.java @@ -0,0 +1,57 @@ +package com.pudonghot.yo.fsagent.service; + +/** + * @author Donghuang + * @date Dec 11, 2019 20:28:07 + */ +public interface AgentStatusService { + + /** + * agent online + * + * @param domain domain + * @param user user + */ + void online(String domain, String user); + + /** + * agent ready + * + * @param domain domain + * @param user user + */ + void ready(String domain, String user); + + /** + * agent not ready + * + * @param domain domain + * @param user user + */ + void notReady(String domain, String user); + + /** + * agent offline + * + * @param domain domain + * @param user user + */ + void offline(String domain, String user); + + /** + * agent in a call + * + * @param domain domain + * @param user user + */ + void inACall(String domain, String user); + + /** + * agent idle + * + * @param domain domain + * @param user user + */ + void idle(String domain, String user); +} + 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 new file mode 100644 index 00000000..c4e1da14 --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/GatewayService.java @@ -0,0 +1,11 @@ +package com.pudonghot.yo.fsagent.service; + +import com.pudonghot.yo.model.domain.Gateway; +import com.wacai.tigon.service.BaseQueryService; + +/** + * @author Donghuang
+ * Dec 04, 2019 19:04:12 + */ +public interface GatewayService extends BaseQueryService { +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/PostRecordingService.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/PostRecordingService.java new file mode 100644 index 00000000..e86044ab --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/PostRecordingService.java @@ -0,0 +1,16 @@ +package com.pudonghot.yo.fsagent.service; + +/** + * @author Donghuang
+ * Jan 06, 2020 18:09:36 + */ +public interface PostRecordingService { + + /** + * post recording + * + * @param tenantId tenant id + * @param location recording location + */ + void postRecording(Integer tenantId, String location); +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/QueueService.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/QueueService.java new file mode 100644 index 00000000..01d1a73f --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/QueueService.java @@ -0,0 +1,11 @@ +package com.pudonghot.yo.fsagent.service; + +import com.pudonghot.yo.model.domain.Queue; +import com.wacai.tigon.service.BaseQueryService; + +/** + * @author Donghuang
+ * Dec 04, 2019 16:47:39 + */ +public interface QueueService extends BaseQueryService { +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/TenantService.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/TenantService.java new file mode 100644 index 00000000..2476dece --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/TenantService.java @@ -0,0 +1,11 @@ +package com.pudonghot.yo.fsagent.service; + +import com.pudonghot.yo.model.domain.Tenant; +import com.wacai.tigon.service.BaseQueryService; + +/** + * @author Donghuang
+ * Dec 02, 2019 21:04:00 + */ +public interface TenantService extends BaseQueryService { +} 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 new file mode 100644 index 00000000..b9802fd4 --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/TrunkService.java @@ -0,0 +1,30 @@ +package com.pudonghot.yo.fsagent.service; + +import com.pudonghot.yo.model.domain.Trunk; +import com.wacai.tigon.service.BaseQueryService; +import org.apache.commons.lang3.tuple.Pair; + +/** + * @author Donghuang
+ * Dec 04, 2019 21:10:08 + */ +public interface TrunkService extends BaseQueryService { + + /** + * build dial string of strategy id + * + * @param strategyId strategy id + * @param number called number + * @param attrs 拨打属性、标签,用于选择外呼中继 + * @return trunk and dial string + */ + Pair dialStr(Integer strategyId, String number, String attrs); + + /** + * append 0 to caller number if required + * @param trunk trunk + * @param number number + * @return called number + */ + String calledNumber(Trunk trunk, String number); +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/dubbo/impl/AbstractDialer.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/dubbo/impl/AbstractDialer.java new file mode 100644 index 00000000..32a71f2d --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/dubbo/impl/AbstractDialer.java @@ -0,0 +1,298 @@ +package com.pudonghot.yo.fsagent.service.dubbo.impl; + +import java.util.List; +import java.util.Arrays; +import java.util.ArrayList; + +import com.pudonghot.yo.mapper.AgentGroupMapper; +import com.pudonghot.yo.model.domain.Agent; +import com.pudonghot.yo.model.domain.AgentGroup; +import com.pudonghot.yo.model.domain.CallDetailRecord; +import com.pudonghot.yo.model.domain.Tenant; +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.Assert; +import com.wacai.tigon.sequence.IdSequence; +import org.apache.commons.lang3.StringUtils; +import org.freeswitch.esl.client.inbound.Client; +import org.apache.commons.lang3.time.DateFormatUtils; +import static org.apache.commons.lang3.StringUtils.join; +import org.springframework.beans.factory.annotation.Value; +import com.pudonghot.yo.fsagent.api.request.BaseReqDial; +import static org.slf4j.helpers.MessageFormatter.arrayFormat; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * @author Donghuang
+ * Dec 09, 2019 16:30:25 + */ +@Slf4j +abstract class AbstractDialer { + @Autowired + private Client fsClient; + @Autowired + private IdSequence idSeq; + @Autowired + private AgentGroupMapper agentGroupMapper; + @Value("${yo.fsagent.recording.dir:/var/lib/freeswitch/recordings}") + private String recDir; + @Value("${yo.fsagent.recording.file-ext:.ogg}") + private String recordingFileExt; + @Value("${yo.fsagent.default-codec:PCMA}") + private String defaultCodec; + + /** + * dial + * @param arg dial arg + */ + public void dial(DialArg arg) { + final BaseReqDial req = arg.getReq(); + final Agent agent = arg.getAgentLegA(); + // validate only + findValidAgentGroup(agent); + + final List varsLegA = varsLegA(arg); + extVarsLegA(arg, varsLegA); + + final List varsLegB = varsLegB(arg); + extVarsLegB(arg, varsLegB); + + final String varsDialStrA; + final String varsDialStrB; + if (req.isOriginateLegB()) { + varsDialStrA = format("[{}]{}", join(varsLegB, ","), dialStrLegB(arg)); + varsDialStrB = format("[{}]{}", join(varsLegA, ","), dialStrLegA(arg)); + } + else { + varsDialStrA = format("[{}]{}", join(varsLegA, ","), dialStrLegA(arg)); + varsDialStrB = format("[{}]{}", join(varsLegB, ","), dialStrLegB(arg)); + } + + final List varsGlobal = varsGlobal(req); + extVarsGlobal(arg, varsGlobal); + + final String commandArg = + format("{{}}{} &bridge({})", + join(varsGlobal, ","), + varsDialStrA, + varsDialStrB); + + log.info("Dial args [{}].", commandArg); + fsClient.bgApi("originate", commandArg); + } + + /** + * ext vars of global + * @param arg arg + * @param vars vars + */ + protected void extVarsGlobal(final DialArg arg, final List vars) { + + } + + /** + * ext vars of leg A + * @param arg arg + * @param vars vars + */ + protected abstract void extVarsLegA(DialArg arg, List vars); + + /** + * dial string of leg a + * @param arg arg + * @return dial string + */ + protected String dialStrLegA(final DialArg arg) { + return "user/" + arg.getAgentLegA().getAgent() + "@" + arg.getTenant().getRealm(); + } + + /** + * ext vars of leg B + * @param arg arg + * @param vars vars + */ + protected abstract void extVarsLegB(DialArg arg, List vars); + + /** + * recording number of leg b + * @param arg arg + * @return recording number + */ + protected abstract String recNumLegB(DialArg arg); + + /** + * dial string of leg b + * @param arg arg + * @return dial string + */ + protected abstract String dialStrLegB(DialArg arg); + + /** + * global vars + * @param req request + * @return global vars + */ + protected List varsGlobal(final BaseReqDial req) { + // Global vars + final List vars = new ArrayList<>(16); + if (req.isIgnoreEarlyMedia()) { + vars.add("ignore_early_media=true"); + } + vars.add("absolute_codec_string=" + + StringUtils.defaultIfBlank(req.getCodec(), defaultCodec)); + vars.add("continue_on_fail=true"); + return vars; + } + + /** + * vars leg A + * @param arg arg + * @return vars leg A + */ + protected List varsLegA(final DialArg arg) { + final List vars = new ArrayList<>(16); + final BaseReqDial req = arg.getReq(); + final Agent agent = arg.getAgentLegA(); + vars.add(agentTypeVar(agent.getType())); + if (agent.isWebrtc()) { + vars.add("media_webrtc=true"); + } + vars.add("odbc-cdr-ignore-leg=true"); + vars.add("x_channel_leg=A"); + final String connId = arg.getConnId(); + vars.add("x_conn_id=" + connId); + final Tenant tenant = arg.getTenant(); + vars.add("x_tenant_id=" + tenant.getId()); + vars.add("x_tenant_code=" + tenant.getCode()); + vars.add("x_realm=" + tenant.getRealm()); + + final CallDetailRecord.DialType dialType = req.getDialType(); + vars.add(dialTypeVar(dialType)); + vars.add(headerDialTypeVar(dialType)); + + // for compatible + vars.add("sip_h_X-Genesys-CallUUID=" + connId); + + bizKey(vars, req.getBizKey()); + // self + vars.add("callee_id_number=" + agent.getAgent()); + vars.add("callee_id_name=" + agent.getAccount()); + + vars.add("sip_auto_answer=true"); + vars.add("origination_uuid=" + connId); + vars.add("sip_invite_call_id=" + connId); + final String attachedData = req.getAttachedData(); + if (StringUtils.isNotBlank(attachedData)) { + vars.add("sip_h_X-Meta-Data=" + attachedData); + } + return vars; + } + + /** + * vars of leg B + * + * @param arg arg + * @return vars of leg B + */ + protected List varsLegB(final DialArg arg) { + + final BaseReqDial req = arg.getReq(); + final Tenant tenant = arg.getTenant(); + final Agent agent = arg.getAgentLegA(); + final String userUri = agent.getAgent() + "@" + tenant.getRealm(); + // Global vars + final List vars = new ArrayList<>(16); + final String recLoc = recLoc(arg); + final String connId = arg.getConnId(); + vars.addAll(Arrays.asList(new String[] { + "x_channel_leg=B", + "x_tenant_id=" + tenant.getId(), + "x_tenant_code=" + tenant.getCode(), + "x_realm=" + tenant.getRealm(), + "x_conn_id=" + connId, + dialTypeVar(req.getDialType()), + callDirectionVar(CallDetailRecord.CallDirection.INCOMING), + agentTypeVar(agent.getType()), + + // for compatible + "sip_h_X-Genesys-CallUUID=" + connId, + + "sip_auto_answer=false", + // FreeSWITCH bug, update call info + "sip_h_Call-Info=" + userUri, + "leg_timeout=" + req.getTimeout(), + "origination_uuid=" + idSeq.get(), + "sip_invite_call_id=" + connId, + "sip_contact_user=" + agent.getAgent(), + "callee_id_number=" + agent.getAgent(), + "callee_id_name=" + agent.getAccount(), + "RECORD_STEREO=true", + "execute_on_media='record_session::" + recLoc + "'", + "execute_on_answer='curl::http://localhost:8080/on-answer post tenantId=" + + tenant.getId() + + "&realm=" + tenant.getRealm() + + "&uuid=" + connId + + "&user=" + agent.getAgent() + "'", + "record_post_process_exec_api='curl:http://localhost:8080/post-rec post tenantId=" + tenant.getId() + "&loc=" + recLoc + "'" + })); + + bizKey(vars, req.getBizKey()); + + return vars; + } + + protected void bizKey(final List vars, String bizKey) { + if (StringUtils.isNotBlank(bizKey)) { + vars.add("x_biz_key=" + bizKey); + } + } + + protected String dialTypeVar(final CallDetailRecord.DialType dialType) { + return "x_dial_type=" + dialType.name(); + } + + protected String headerDialTypeVar(final CallDetailRecord.DialType dialType) { + return "sip_h_X-Dial-Type=" + dialType.name(); + } + + protected String callTypeVar(final CallDetailRecord.CallType callType) { + return "x_call_type=" + callType.name(); + } + + protected String headerCallTypeVar(final CallDetailRecord.CallType callType) { + return "sip_h_X-Call-Type=" + callType.name(); + } + + protected String callDirectionVar(final CallDetailRecord.CallDirection callDirection) { + return "x_call_direction=" + callDirection.name(); + } + + protected String headerCallDirectionVar(final CallDetailRecord.CallDirection callDirection) { + return "sip_h_X-Call-Direction=" + callDirection.name(); + } + + String agentTypeVar(final Agent.Type agentType) { + return "x_agent_type=" + agentType.name(); + } + + AgentGroup findValidAgentGroup(final Agent agent) { + final AgentGroup agentGroup = agentGroupMapper.find(agent.getGroupId()); + Assert.state(agentGroup != null, + () -> "No agent [" + agent.getAccount() + "] group found"); + Assert.state(agentGroup.getActive(), + () -> "Agent [" + agent.getAccount() + "] group is not active"); + return agentGroup; + } + + String format(final String tpl, Object... args) { + return arrayFormat(tpl, args).getMessage(); + } + + private String recLoc(final DialArg arg) { + return recDir + "/" + arg.getConnId() + "_" + + DateFormatUtils.format( + System.currentTimeMillis(), "yyyy-MM-dd_HH-mm-ss") + "_" + + arg.getAgentLegA().getAccount() + "-" + + recNumLegB(arg) + + recordingFileExt; + } +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/dubbo/impl/DialArg.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/dubbo/impl/DialArg.java new file mode 100644 index 00000000..e966331f --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/dubbo/impl/DialArg.java @@ -0,0 +1,25 @@ +package com.pudonghot.yo.fsagent.service.dubbo.impl; + +import com.pudonghot.yo.model.domain.Agent; +import com.pudonghot.yo.model.domain.Tenant; +import lombok.Setter; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import com.pudonghot.yo.fsagent.api.request.BaseReqDial; + +/** + * @author Donghuang + * @date Mar 24, 2020 11:46:36 + */ +@Getter +@RequiredArgsConstructor +class DialArg { + private final String connId; + private final BaseReqDial req; + private final Tenant tenant; + private final Agent agentLegA; + private final T legB; + + @Setter + private Object data; +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/dubbo/impl/DialServiceImpl.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/dubbo/impl/DialServiceImpl.java new file mode 100644 index 00000000..d2f7c8e7 --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/dubbo/impl/DialServiceImpl.java @@ -0,0 +1,228 @@ +package com.pudonghot.yo.fsagent.service.dubbo.impl; + +import com.pudonghot.yo.mapper.*; +import lombok.extern.slf4j.Slf4j; +import com.pudonghot.yo.model.domain.*; +import com.wacai.tigon.mybatis.Search; +import org.springframework.util.Assert; +import org.apache.commons.lang3.tuple.Pair; +import com.wacai.tigon.sequence.IdSequence; +import org.apache.commons.lang3.StringUtils; +import com.pudonghot.yo.fsagent.api.DialService; +import org.apache.dubbo.config.annotation.Service; +import com.pudonghot.yo.fsagent.api.request.CallLeg; +import com.pudonghot.yo.fsagent.api.request.ReqDial; +import com.pudonghot.yo.fsagent.service.AgentService; +import com.pudonghot.yo.fsagent.api.response.RespDial; +import com.pudonghot.yo.fsagent.api.request.BaseReqDial; +import com.pudonghot.yo.fsagent.api.request.ReqAgentDial; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * @author Donghuang
+ * Dec 09, 2019 16:30:25 + */ +@Slf4j +@Service(version = "${yo.fsagent.dubbo.service.version}", register = false) +public class DialServiceImpl implements DialService { + @Autowired + private IdSequence idSeq; + @Autowired + private TenantMapper tenantMapper; + @Autowired + private AgentMapper agentMapper; + @Autowired + private AgentGroupMapper agentGroupMapper; + @Autowired + private AgentGroupTrunkStrategyMapper agentGroupTrunkStrategyMapper; + @Autowired + private QueueMapper queueMapper; + @Autowired + private AgentService agentService; + @Value("${yo.fsagent.recording.file-ext:.ogg}") + private String recordingFileExt; + @Value("${yo.fsagent.default-codec:PCMA}") + private String defaultCodec; + + @Autowired + private DialerAgent dialerAgent; + @Autowired + private DialerOutside dialerOutside; + + /** + * {@inheritDoc} + */ + @Override + public RespDial dial(final ReqDial req) { + final CallLeg callerLeg = req.getCallerLeg(); + final CallLeg.Type callerLegType = callerLeg.getType(); + Assert.state(callerLegType != CallLeg.Type.OUTSIDE, + "Caller leg could not be OUTSIDE"); + + final Integer tenantId = req.getTenantId(); + final Tenant tenant = findValidTenant(tenantId); + + final Agent callerAgent = getIdleAgent(tenant, callerLeg); + Assert.state(callerAgent != null, + () -> "No idle caller agent [" + callerLeg.getSubject() + "] found"); + + final String connId = idSeq.get(); + + final CallLeg calledLeg = req.getCalledLeg(); + if (calledLeg.getType() == CallLeg.Type.OUTSIDE) { + return dialExternal(connId, req, tenant, callerAgent, calledLeg.getTrunkStrategyId(), calledLeg.getSubject()); + } + + final Agent calledAgent = getIdleAgent(tenant, calledLeg); + Assert.state(calledAgent != null, + () -> "No idle called agent [" + calledLeg.getSubject() + "] found"); + return dialInternal(connId, req, tenant, callerAgent, calledAgent); + } + + /** + * {@inheritDoc} + */ + @Override + public RespDial agentDial(final ReqAgentDial req) { + final Integer tenantId = req.getTenantId(); + final Tenant tenant = findValidTenant(tenantId); + + final String account = req.getAccount(); + final Agent agent = agentMapper.find( + new Search(Agent.TENANT_ID, tenantId) + .eq(Agent.ACCOUNT, account)); + Assert.state(agent != null, + () -> "No agent [" + account + "] of tenant [" + tenantId + "] found"); + Assert.state(agent.getActive(), + () -> "Agent [" + account + "] is not active"); + + final String calledNumber = req.getCalledNumber(); + + final Agent otherAgent = agentMapper.find( + new Search(Agent.TENANT_ID, tenantId) + .eq(StringUtils.isNumeric(calledNumber) ? + Agent.AGENT : Agent.ACCOUNT, calledNumber)); + + if (otherAgent != null) { + log.info("Dial internal agent [{}].", otherAgent); + final String otherAgentAccount = otherAgent.getAccount(); + Assert.state(otherAgent.getActive(), + () -> "Other agent [" + otherAgentAccount + "] is not active"); + req.setCalledNumber(otherAgent.getAgent()); + req.setCalledName(otherAgentAccount); + return dialInternal(idSeq.get(), req, tenant, agent, otherAgent); + } + + final AgentGroupTrunkStrategy agentGroupTrunkStrategy = + agentGroupTrunkStrategyMapper.find( + new Search(AgentGroupTrunkStrategy.AGENT_GROUP_ID, + agent.getGroupId())); + Assert.state(agentGroupTrunkStrategy != null, + () -> "No agent [" + agent.getAccount() + "] group trunk strategy found"); + Assert.state(agentGroupTrunkStrategy.getActive(), + () -> "Agent [" + agent.getAccount() + "] group trunk strategy is not active"); + return dialExternal(idSeq.get(), req, tenant, agent, agentGroupTrunkStrategy.getTrunkStrategyId(), calledNumber); + } + + RespDial dialInternal( + final String connId, + final BaseReqDial req, + final Tenant tenant, + final Agent agent, + final Agent otherAgent) { + + dialerAgent.dial(new DialArg<>(connId, req, tenant, agent, otherAgent)); + return new RespDial(connId); + } + + RespDial dialExternal(final String connId, + final BaseReqDial req, + final Tenant tenant, + final Agent agent, + final Integer trunkStrategyId, + final String calledNumber) { + + dialerOutside.dial(new DialArg<>( + connId, req, tenant, agent, Pair.of(trunkStrategyId, calledNumber))); + return new RespDial(connId); + } + + private Agent getIdleAgent(final Tenant tenant, final CallLeg callLeg) { + final Integer tenantId = tenant.getId(); + final CallLeg.Type callerLegType = callLeg.getType(); + + Agent callerAgent = null; + // caller agent found + if (callerLegType == CallLeg.Type.AGENT) { + callerAgent = findValidAgent(tenant, callLeg); + } + else if (callerLegType == CallLeg.Type.AGENT_GROUP) { + callerAgent = agentService.lockIdleOfGroup( + tenantId, findValidAgentGroup(tenant, callLeg).getId()); + } + else if (callerLegType == CallLeg.Type.QUEUE) { + callerAgent = agentService.lockIdleOfQueue( + tenantId, findValidQueue(tenant, callLeg).getId()); + } + return callerAgent; + } + + private Tenant findValidTenant(final Integer tenantId) { + final Tenant tenant = tenantMapper.find(tenantId); + Assert.state(tenant != null, + () -> "No tenant [" + tenantId + "] found"); + Assert.state(tenant.getActive(), + () -> "Tenant [" + tenantId + "] is not active"); + return tenant; + } + + private Agent findValidAgent(final Tenant tenant, final CallLeg callLeg) { + final Integer tenantId = tenant.getId(); + final String subject = callLeg.getSubject(); + Agent agent = agentMapper.find( + new Search(Agent.TENANT_ID, tenantId) + .eq(Agent.ACCOUNT, subject)); + if (agent == null) { + agent = agentMapper.find( + new Search(Agent.TENANT_ID, tenantId) + .eq(Agent.AGENT, subject)); + } + + Assert.state(agent != null, + () -> "No agent [" + subject + "] found"); + Assert.state(agent.getActive(), + () -> "Agent [" + subject + "] is not active"); + + return agent; + } + + private Queue findValidQueue(final Tenant tenant, final CallLeg callLeg) { + final Integer tenantId = tenant.getId(); + final String subject = callLeg.getSubject(); + final Queue queue = queueMapper.find( + new Search(Queue.TENANT_ID, tenantId) + .eq(Queue.IDENTIFIER, subject)); + + Assert.state(queue != null, + () -> "No queue [" + subject + "] found"); + Assert.state(queue.getActive(), + () -> "Queue [" + subject + "] is not active"); + + return queue; + } + + private AgentGroup findValidAgentGroup(final Tenant tenant, final CallLeg callLeg) { + final String subject = callLeg.getSubject(); + final AgentGroup agentGroup = agentGroupMapper.find( + new Search(Queue.TENANT_ID, tenant.getId()) + .eq(Queue.IDENTIFIER, subject)); + + Assert.state(agentGroup != null, + () -> "No agent group [" + subject + "] found"); + Assert.state(agentGroup.getActive(), + () -> "Agent group [" + subject + "] is not active"); + + return agentGroup; + } +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/dubbo/impl/DialerAgent.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/dubbo/impl/DialerAgent.java new file mode 100644 index 00000000..a917ae79 --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/dubbo/impl/DialerAgent.java @@ -0,0 +1,71 @@ +package com.pudonghot.yo.fsagent.service.dubbo.impl; + +import java.util.List; + +import com.pudonghot.yo.model.domain.Agent; +import com.pudonghot.yo.model.domain.CallDetailRecord; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +/** + * @author Donghuang
+ * Dec 09, 2019 16:30:25 + */ +@Slf4j +@Service +public class DialerAgent extends AbstractDialer { + + /** + * {@inheritDoc} + */ + @Override + protected void extVarsGlobal(final DialArg arg, final List vars) { + vars.add("ringback=%(2000\\,4000\\,440\\,480)"); + } + + /** + * {@inheritDoc} + */ + @Override + protected void extVarsLegA(final DialArg arg, final List vars) { + final Agent legB = arg.getLegB(); + final String calledNumber = legB.getAgent(); + vars.add("sip_contact_user=" + calledNumber); + vars.add("origination_caller_id_number=" + calledNumber); + vars.add("origination_caller_id_name=" + legB.getAccount()); + + vars.add(callTypeVar(CallDetailRecord.CallType.INTERNAL)); + vars.add(headerCallTypeVar(CallDetailRecord.CallType.INTERNAL)); + vars.add(headerCallDirectionVar(CallDetailRecord.CallDirection.OUTGOING)); + } + + /** + * {@inheritDoc} + */ + @Override + protected void extVarsLegB(final DialArg arg, final List vars) { + final Agent agentLegA = arg.getAgentLegA(); + vars.add(callTypeVar(CallDetailRecord.CallType.INTERNAL)); + vars.add(headerCallTypeVar(CallDetailRecord.CallType.INTERNAL)); + vars.add(headerCallDirectionVar(CallDetailRecord.CallDirection.INCOMING)); + + vars.add("origination_caller_id_number=" + agentLegA.getAgent()); + vars.add("origination_caller_id_name=" + agentLegA.getAccount()); + } + + /** + * {@inheritDoc} + */ + @Override + protected String recNumLegB(DialArg arg) { + return arg.getLegB().getAccount(); + } + + /** + * {@inheritDoc} + */ + @Override + protected String dialStrLegB(final DialArg arg) { + return "user/" + arg.getLegB().getAgent() + "@" + arg.getTenant().getRealm(); + } +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/dubbo/impl/DialerOutside.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/dubbo/impl/DialerOutside.java new file mode 100644 index 00000000..f7f3e649 --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/dubbo/impl/DialerOutside.java @@ -0,0 +1,86 @@ +package com.pudonghot.yo.fsagent.service.dubbo.impl; + +import java.util.List; + +import com.pudonghot.yo.model.domain.Agent; +import com.pudonghot.yo.model.domain.CallDetailRecord; +import com.pudonghot.yo.model.domain.Trunk; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.commons.lang3.StringUtils; +import org.springframework.util.DigestUtils; +import org.springframework.stereotype.Service; +import com.pudonghot.yo.cellphone.privacy.PrivacyLevel; +import com.pudonghot.yo.fsagent.service.TrunkService; +import com.pudonghot.yo.fsagent.api.request.BaseReqDial; +import com.pudonghot.yo.cellphone.privacy.NumberPrivacyUtils; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * @author Donghuang
+ * Dec 09, 2019 16:30:25 + */ +@Slf4j +@Service +public class DialerOutside extends AbstractDialer> { + @Autowired + private TrunkService trunkService; + + /** + * {@inheritDoc} + */ + @Override + protected void extVarsLegA( + final DialArg> arg, + final List vars) { + + vars.add(callTypeVar(CallDetailRecord.CallType.OUTBOUND)); + vars.add(headerCallTypeVar(CallDetailRecord.CallType.OUTBOUND)); + vars.add(headerCallDirectionVar(CallDetailRecord.CallDirection.OUTGOING)); + // hide customer info + vars.add("sip_contact_user=SYSTEM"); + vars.add("origination_caller_id_number=SYSTEM"); + vars.add("origination_caller_id_name=" + + calledName(arg.getReq(), arg.getAgentLegA(), arg.getLegB().getRight())); + } + + /** + * {@inheritDoc} + */ + @Override + protected void extVarsLegB(final DialArg> arg, + final List vars) { + vars.add(callTypeVar(CallDetailRecord.CallType.OUTBOUND)); + final BaseReqDial req = arg.getReq(); + final Pair legB = arg.getLegB(); + final Pair trunkDial = + trunkService.dialStr(legB.getLeft(), legB.getRight(), req.getAttrs()); + arg.setData(trunkDial); + vars.add("origination_caller_id_number=" + trunkDial.getLeft().getCpn()); + } + + /** + * {@inheritDoc} + */ + @Override + protected String recNumLegB(final DialArg> arg) { + return DigestUtils.md5DigestAsHex(arg.getLegB().getRight().getBytes()); + } + + /** + * {@inheritDoc} + */ + @Override + protected String dialStrLegB(DialArg> arg) { + return ((Pair) arg.getData()).getRight(); + } + + String calledName(final BaseReqDial req, final Agent agent, final String calledNumber) { + final String calledName = req.getCalledName(); + if (StringUtils.isNotBlank(calledName)) { + return calledName; + } + return NumberPrivacyUtils.mask(calledNumber, + PrivacyLevel.valueOf(findValidAgentGroup(agent).getPrivacyLevel().name())); + } +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/impl/AgentGroupServiceImpl.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/impl/AgentGroupServiceImpl.java new file mode 100644 index 00000000..67cffafa --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/impl/AgentGroupServiceImpl.java @@ -0,0 +1,19 @@ +package com.pudonghot.yo.fsagent.service.impl; + +import com.pudonghot.yo.fsagent.service.AgentGroupService; +import com.pudonghot.yo.mapper.AgentGroupMapper; +import com.pudonghot.yo.model.domain.AgentGroup; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import com.wacai.tigon.service.support.BaseQueryServiceSupport; + +/** + * @author Donghuang
+ * Dec 04, 2019 17:04:11 + */ +@Slf4j +@Service +public class AgentGroupServiceImpl + extends BaseQueryServiceSupport + implements AgentGroupService { +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/impl/AgentServiceImpl.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/impl/AgentServiceImpl.java new file mode 100644 index 00000000..4437fc56 --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/impl/AgentServiceImpl.java @@ -0,0 +1,75 @@ +package com.pudonghot.yo.fsagent.service.impl; + +import lombok.extern.slf4j.Slf4j; +import java.util.function.Consumer; +import com.wacai.tigon.mybatis.Search; +import org.springframework.util.Assert; +import com.wacai.tigon.sequence.IdSequence; +import com.pudonghot.yo.mapper.AgentMapper; +import com.pudonghot.yo.model.domain.Agent; +import org.springframework.stereotype.Service; +import com.pudonghot.yo.mapper.AgentStatusMapper; +import com.pudonghot.yo.model.domain.AgentStatus; +import com.pudonghot.yo.fsagent.service.AgentService; +import org.springframework.beans.factory.annotation.Autowired; +import com.wacai.tigon.service.support.BaseQueryServiceSupport; + +/** + * @author Donghuang
+ * Dec 02, 2019 21:02:49 + */ +@Slf4j +@Service +public class AgentServiceImpl + extends BaseQueryServiceSupport + implements AgentService { + + @Autowired + private IdSequence idSeq; + @Autowired + private AgentStatusMapper agentStatusMapper; + + /** + * {@inheritDoc} + */ + @Override + public Agent findByDomainAndAgent(final String domain, final String agent) { + return mapper.findByDomainAndAgent(domain, agent); + } + + /** + * {@inheritDoc} + */ + @Override + public Agent findOfCalled(final String domain, final String calledNumber) { + final Agent agent = mapper.findByDomainAndAgent(domain, calledNumber); + return agent != null ? agent : mapper.findByDomainAndAccount(domain, calledNumber); + } + + /** + * {@inheritDoc} + */ + @Override + public Agent lockIdleOfQueue(final Integer tenantId, final Integer queueId) { + return lockIdle(lockKey -> + agentStatusMapper.lockIdleOfQueue(tenantId, queueId, lockKey, 1)); + } + + /** + * {@inheritDoc} + */ + @Override + public Agent lockIdleOfGroup(final Integer tenantId, final Integer groupId) { + return lockIdle(lockKey -> + agentStatusMapper.lockIdleOfGroup(tenantId, groupId, lockKey, 1)); + } + + private Agent lockIdle(final Consumer locker) { + final String lockKey = idSeq.get(); + locker.accept(lockKey); + final AgentStatus agentStatus = agentStatusMapper.find( + new Search(AgentStatus.LOCK_KEY, lockKey)); + Assert.state(agentStatus != null, "No READY and IDLE agent found"); + return mapper.find(agentStatus.getAgentId()); + } +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/impl/AgentStatusServiceImpl.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/impl/AgentStatusServiceImpl.java new file mode 100644 index 00000000..2f2894f0 --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/impl/AgentStatusServiceImpl.java @@ -0,0 +1,145 @@ +package com.pudonghot.yo.fsagent.service.impl; + +import com.pudonghot.yo.fsagent.service.AgentStatusService; +import com.pudonghot.yo.mapper.AgentMapper; +import com.pudonghot.yo.mapper.AgentStatusMapper; +import com.pudonghot.yo.model.domain.Agent; +import com.pudonghot.yo.model.domain.AgentStatus; +import lombok.extern.slf4j.Slf4j; +import com.wacai.tigon.mybatis.Search; +import org.springframework.util.Assert; +import org.springframework.stereotype.Service; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * @author Donghuang
+ * Dec 02, 2019 21:02:49 + */ +@Slf4j +@Service +public class AgentStatusServiceImpl + implements AgentStatusService { + + @Autowired + private AgentMapper agentMapper; + @Autowired + private AgentStatusMapper agentStatusMapper; + + @Override + public void online(final String domain, final String user) { + log.info("Online domain [{}] agent [{}].", domain, user); + + final Agent agent = agentMapper.findByDomainAndAgent(domain, user); + Assert.state(agent != null, + () -> "No domain [" + domain + "] agent [" + user + "] found"); + Assert.state(agent.getActive(), + () -> "Agent [" + user + "] is not active"); + + final Integer agentId = agent.getId(); + + if (!agentStatusMapper.exists( + new Search(AgentStatus.AGENT_ID, agentId))) { + + log.info("No agent [{}] status found, init.", user); + final AgentStatus agentStatus = new AgentStatus(); + agentStatus.setTenantId(agent.getTenantId()); + agentStatus.setTenantCode(agent.getTenantCode()); + agentStatus.setAgentId(agentId); + agentStatus.setAccount(agent.getAccount()); + agentStatus.setAgent(agent.getAgent()); + agentStatus.setStatus(AgentStatus.Status.NOT_READY); + agentStatus.setState(AgentStatus.State.IDLE); + agentStatusMapper.insert(agentStatus); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void ready(final String domain, final String user) { + log.info("Ready domain [{}] agent [{}]. ", domain, user); + + final AgentStatus agentStatus = findAgentStatus(domain, user); + if (agentStatus != null) { + agentStatus.setStatus(AgentStatus.Status.READY); + agentStatus.setState(AgentStatus.State.IDLE); + agentStatusMapper.update(agentStatus); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void notReady(final String domain, final String user) { + log.info("Not ready domain [{}] agent [{}]. ", domain, user); + + final AgentStatus agentStatus = findAgentStatus(domain, user); + if (agentStatus != null) { + agentStatus.setStatus(AgentStatus.Status.NOT_READY); + agentStatus.setState(AgentStatus.State.IDLE); + agentStatusMapper.update(agentStatus); + } + else { + log.info("No domain [{}] agent [{}] status found, ignore.", domain, user); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void offline(String domain, String user) { + final AgentStatus agentStatus = findAgentStatus(domain, user); + + if (agentStatus != null) { + agentStatus.setStatus(AgentStatus.Status.OFFLINE); + agentStatus.setState(AgentStatus.State.IDLE); + agentStatus.setLockKey(null); + agentStatus.setLockTime(null); + agentStatusMapper.update(agentStatus); + } + else { + log.info("No domain [{}] agent [{}] status found, ignore.", domain, user); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void inACall(String domain, String user) { + updateState(domain, user, AgentStatus.State.IN_A_CALL); + } + + /** + * {@inheritDoc} + */ + @Override + public void idle(String domain, String user) { + updateState(domain, user, AgentStatus.State.IDLE); + } + + private AgentStatus findAgentStatus(String domain, String user) { + final Agent agent = agentMapper.findByDomainAndAgent(domain, user); + Assert.state(agent != null, + () -> "No domain [" + domain + "] agent [" + user + "] found"); + return agentStatusMapper.find(new Search(AgentStatus.AGENT_ID, agent.getId())); + } + + private void updateState(final String domain, final String user, final AgentStatus.State state) { + log.info("Update realm [{}] agent [{}] state [{}].", domain, user, state); + final AgentStatus agentStatus = findAgentStatus(domain, user); + + if (agentStatus != null) { + agentStatus.setState(state); + agentStatus.setLockKey(null); + agentStatus.setLockTime(null); + agentStatusMapper.update(agentStatus); + } + else { + log.info("No domain [{}] agent [{}] status found, ignore.", domain, user); + } + } +} 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 new file mode 100644 index 00000000..aae3be78 --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/impl/GatewayerviceImpl.java @@ -0,0 +1,21 @@ +package com.pudonghot.yo.fsagent.service.impl; + +import com.pudonghot.yo.fsagent.service.GatewayService; +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.wacai.tigon.service.support.BaseQueryServiceSupport; + +/** + * @author Donghuang
+ * Dec 04, 2019 19:04:54 + */ +@Slf4j +@Service +public class GatewayerviceImpl + extends BaseQueryServiceSupport + implements GatewayService { +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/impl/PostRecordingServiceImpl.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/impl/PostRecordingServiceImpl.java new file mode 100644 index 00000000..ae7d79b2 --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/impl/PostRecordingServiceImpl.java @@ -0,0 +1,96 @@ +package com.pudonghot.yo.fsagent.service.impl; + +import java.io.File; +import java.util.Map; +import java.util.HashMap; + +import com.pudonghot.yo.fsagent.service.PostRecordingService; +import com.pudonghot.yo.mapper.CallRecordingMapper; +import com.pudonghot.yo.mapper.TenantMapper; +import com.pudonghot.yo.model.domain.CallRecording; +import com.pudonghot.yo.model.domain.Tenant; +import com.pudonghot.yo.common.httpclient.HttpClient; +import com.pudonghot.yo.common.httpclient.HttpRequestResult; +import com.pudonghot.yo.common.httpclient.RequestBodyParams; +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.Assert; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.scheduling.annotation.Async; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * @author Donghuang
+ * Jan 06, 2020 18:11:09 + */ +@Slf4j +@Service +public class PostRecordingServiceImpl + implements PostRecordingService, InitializingBean { + @Autowired + private HttpClient httpClient; + @Value("${yo.fsagent.recording-server.base-path}") + private String fileServerBasePath; + @Value("${yo.fsagent.recording-server.auth:Basic dXNlcjpwYXNzd29yZA==}") + private String fileServerAuth; + private final Map REQ_HEADERS = new HashMap<>(4); + @Autowired + private TenantMapper tenantMapper; + @Autowired + private CallRecordingMapper callRecordingMapper; + + /** + * {@inheritDoc} + */ + @Async + @Override + public void postRecording(final Integer tenantId, final String location) { + log.info("Post recording file [{}].", location); + final File file = new File(location); + if (!file.exists()) { + log.warn("Recording file [{}] does not exist.", file); + return; + } + + final String name = file.getName(); + final String url = fileServerBasePath + name; + final RequestBodyParams params = + new RequestBodyParams<>(file); + params.setHeaders(REQ_HEADERS); + final HttpRequestResult result = httpClient.put(url, params); + final int status = result.getStatus(); + log.info("Upload recording file [{}] result [{}]", url, status); + if (status >= 200 && status < 300) { + log.info("Upload recording file [{}] successfully, delete file.", location); + file.delete(); + } + + final String connId = name.substring(0, name.indexOf("_")); + if (StringUtils.isNotBlank(connId)) { + final Tenant tenant = tenantMapper.find(tenantId); + Assert.state(tenant != null, + () -> "No tenant [" + tenantId + "] found"); + + final CallRecording callRec = new CallRecording(); + callRec.setTenantId(tenantId); + callRec.setTenantCode(tenant.getCode()); + callRec.setConnId(connId); + callRec.setUrl(url); + log.info("Insert call [{}] recording [{}].", connId, callRec); + callRecordingMapper.insert(callRec); + } + else { + log.warn("No conn id found in call recording file [{}], ignore.", name); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void afterPropertiesSet() { + REQ_HEADERS.put("Authorization", fileServerAuth); + } +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/impl/QueueServiceImpl.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/impl/QueueServiceImpl.java new file mode 100644 index 00000000..a9ea1dee --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/impl/QueueServiceImpl.java @@ -0,0 +1,21 @@ +package com.pudonghot.yo.fsagent.service.impl; + +import com.pudonghot.yo.mapper.QueueMapper; +import com.pudonghot.yo.model.domain.Queue; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import com.pudonghot.yo.fsagent.service.QueueService; +import com.wacai.tigon.service.support.BaseQueryServiceSupport; + +/** + * @author Donghuang
+ * Dec 04, 2019 19:05:01 + */ +@Slf4j +@Service +public class QueueServiceImpl + extends BaseQueryServiceSupport + implements QueueService { +} diff --git a/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/impl/TenantServiceImpl.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/impl/TenantServiceImpl.java new file mode 100644 index 00000000..e93feb6b --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/impl/TenantServiceImpl.java @@ -0,0 +1,21 @@ +package com.pudonghot.yo.fsagent.service.impl; + +import com.pudonghot.yo.mapper.TenantMapper; +import com.pudonghot.yo.model.domain.Tenant; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import com.pudonghot.yo.fsagent.service.TenantService; +import com.wacai.tigon.service.support.BaseQueryServiceSupport; + +/** + * @author Donghuang
+ * Nov 01, 2019 15:43:13 + */ +@Slf4j +@Service +public class TenantServiceImpl + extends BaseQueryServiceSupport + implements TenantService { +} 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 new file mode 100644 index 00000000..1419257e --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/service/impl/TrunkServiceImpl.java @@ -0,0 +1,107 @@ +package com.pudonghot.yo.fsagent.service.impl; + +import java.util.List; + +import com.pudonghot.yo.fsagent.service.TrunkService; +import com.pudonghot.yo.mapper.GatewayMapper; +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.Trunk; +import com.pudonghot.yo.model.domain.TrunkStrategy; +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.Assert; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.RandomUtils; +import org.springframework.stereotype.Service; +import com.pudonghot.yo.cellphone.CellphoneInfo; +import com.pudonghot.yo.cellphone.CellphoneService; +import org.springframework.beans.factory.annotation.Autowired; +import com.wacai.tigon.service.support.BaseQueryServiceSupport; + +/** + * @author Donghuang
+ * Dec 04, 2019 21:10:28 + */ +@Slf4j +@Service +public class TrunkServiceImpl + extends BaseQueryServiceSupport + implements TrunkService { + + @Autowired + private TrunkStrategyMapper trunkStrategyMapper; + @Autowired + private GatewayMapper gatewayMapper; + @Autowired + private CellphoneService cellphoneService; + + /** + * {@inheritDoc} + */ + @Override + public Pair dialStr( + final Integer strategyId, + final String number, + final String attrs) { + + final TrunkStrategy trunkStrategy = trunkStrategyMapper.find(strategyId); + Assert.state(trunkStrategy != null, + () -> "No trunk strategy [" + strategyId + "] found"); + Assert.state(trunkStrategy.getActive(), + () -> "Trunk strategy [" + strategyId + "] is not active"); + + // TODO trunk strategies support + Assert.state(TrunkStrategy.Strategy.RANDOM == trunkStrategy.getStrategy(), + "Random trunk strategy supports only"); + + final List trunks = mapper.listOfStrategyId(strategyId); + Assert.state(!trunks.isEmpty(), + () -> "No trunk found of strategy [" + trunkStrategy.getName() + "]"); + + final Trunk trunk = trunks.get(RandomUtils.nextInt(0, trunks.size())); + + final Gateway gateway = gatewayMapper.find(trunk.getGatewayId()); + Assert.state(gateway != null, + () -> "Trunk [" + trunk.getPrefix() + "] gateway not found"); + 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') + + "/" + calledNumber(trunk, number)); + } + + /** + * {@inheritDoc} + */ + @Override + public String calledNumber(final Trunk trunk, final String calledNumber) { + log.debug("Process called number [{}], if mobile phone, may prepend 0 to call.", calledNumber); + + // 外埠加0,针对手机号 + if (trunk.isOcAddZero() && cellphoneService.isCellphone(calledNumber)) { + log.info("Trunk [{}] need prepend 0 to call out.", trunk.getCallerNumber()); + final CellphoneInfo phoneInfo = cellphoneService.lookup(calledNumber); + if (phoneInfo != null) { + log.debug("Caller number [{}] phone info [{}] found.", calledNumber, phoneInfo); + if (!trunk.getAreaCode().equals(phoneInfo.getAreaCode())) { + log.debug("Caller number [{}] area code is not same to trunk, prepend 0.", calledNumber); + return addPrefix(trunk, "0" + calledNumber); + } + } + else { + log.warn("Caller number [{}] phone info not found.", calledNumber); + } + } + return addPrefix(trunk, calledNumber); + } + + 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/OdbcUtils.java b/fsagent/src/main/java/com/pudonghot/yo/fsagent/util/OdbcUtils.java new file mode 100644 index 00000000..6b75001f --- /dev/null +++ b/fsagent/src/main/java/com/pudonghot/yo/fsagent/util/OdbcUtils.java @@ -0,0 +1,45 @@ +package com.pudonghot.yo.fsagent.util; + +import java.net.URI; +import java.util.List; +import java.util.ArrayList; +import lombok.SneakyThrows; +import java.util.stream.Collectors; +import org.apache.commons.lang3.tuple.Pair; +import com.alibaba.druid.pool.DruidDataSource; + +/** + * @author Donghuang + * @date Mar 04, 2020 16:41:52 + */ +public class OdbcUtils { + + /** + * get ODBC DSN from data source + * + * @param dataSource data source + * @return odbc dsn + */ + public static String odbcDsn(final DruidDataSource dataSource) { + final List> odbcParams = new ArrayList<>(8); + odbcParams.add(Pair.of("DRIVER", "mysql")); + odbcParams.add(Pair.of("OPTION", "67108864")); + odbcParams.add(Pair.of("CHARSET", "utf8mb4")); + + final URI jdbcURI = getURI(dataSource); + odbcParams.add(Pair.of("SERVER", jdbcURI.getHost())); + final int port = jdbcURI.getPort(); + odbcParams.add(Pair.of("PORT", port > 0 ? port : 3306)); + odbcParams.add(Pair.of("DATABASE", jdbcURI.getPath().substring(1))); + odbcParams.add(Pair.of("UID", dataSource.getUsername())); + odbcParams.add(Pair.of("PWD", dataSource.getPassword())); + return "odbc://" + odbcParams.stream() + .map(v -> v.getKey() + "=" + v.getValue()) + .collect(Collectors.joining(";")); + } + + @SneakyThrows + private static URI getURI(final DruidDataSource dataSource) { + return new URI(new URI(dataSource.getUrl()).getSchemeSpecificPart()); + } +} diff --git a/fsagent/src/main/resources/application.properties b/fsagent/src/main/resources/application.properties new file mode 100644 index 00000000..fdf936ca --- /dev/null +++ b/fsagent/src/main/resources/application.properties @@ -0,0 +1,57 @@ +server.port=8080 +spring.application.name=yo-br-fsagent +spring.jackson.time-zone=GMT+8 +spring.jackson.serialization.write-dates-as-timestamps=true +spring.jackson.serialization.fail-on-empty-beans=false +site.context-path= + +# spring.freemarker.template-loader-path=classpath:/templates +# spring.freemarker.suffix=.ftl + + +# Datasource +yo.datasource.url=jdbc:mysql://172.18.4.35/yoqw?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai +yo.datasource.username=yoqw +yo.datasource.password=yoqw_query! + +# Datasource +yo.fs.datasource.url=jdbc:mysql://172.18.4.35/freeswitch?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai +yo.fs.datasource.username=freeswitch +yo.fs.datasource.password=RR!h5IpirsnJ + +# Redis +yo.redis.host=172.16.92.232 +yo.redis.port=6379 +yo.redis.password=123456 + +# Dubbo + +# Base packages to scan Dubbo Component: @org.apache.dubbo.config.annotation.Service +dubbo.scan.base-packages=com.pudonghot.yo.fsagent.service.dubbo.impl + +# Dubbo Protocol +dubbo.protocol.name=dubbo +## Random port +dubbo.protocol.port=-1 + +# QOS +dubbo.application.qos-enable=true +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.file=${user.home}/dubbo-cache/${spring.application.name}/dubbo.cache + +# Service Version +yo.fsagent.dubbo.service.version=1.0.0 +yo.fsagent.recording-server.base-path=http://172.16.52.80/fs/rec/ + +# Agent Status Audio +yo.fsagent.agent-status.audio.ready.ok=http://172.16.46.35/voice/20191126/5ddcd2c882745b00016922cd.wav +yo.fsagent.agent-status.audio.ready.err=http://172.16.46.35/voice/20191126/5ddcfef182745b00016922d5.wav +yo.fsagent.agent-status.audio.not-ready.ok=http://172.16.46.35/voice/20191126/5ddcd2ca82745b00016922d1.wav +yo.fsagent.agent-status.audio.not-ready.err=http://172.16.46.35/voice/20191126/5ddcfef282745b00016922d9.wav + +# ESL +yo.br.esl.client.host=172.16.65.165 diff --git a/fsagent/src/main/resources/freeswitch-conf.patch b/fsagent/src/main/resources/freeswitch-conf.patch new file mode 100644 index 00000000..6e267daf --- /dev/null +++ b/fsagent/src/main/resources/freeswitch-conf.patch @@ -0,0 +1,2783 @@ +diff --git a/autoload_configs/acl.conf.xml b/autoload_configs/acl.conf.xml +index 61cf92f..3ce2568 100644 +--- a/autoload_configs/acl.conf.xml ++++ b/autoload_configs/acl.conf.xml +@@ -9,6 +9,9 @@ + loopback.auto - ACL for your local lan. + --> + ++ ++ ++ + + + +diff --git a/autoload_configs/modules.conf.xml b/autoload_configs/modules.conf.xml +index 3f0b53e..d722442 100644 +--- a/autoload_configs/modules.conf.xml ++++ b/autoload_configs/modules.conf.xml +@@ -14,14 +14,14 @@ + + + +- ++ + + + + + + +- ++ + + + +diff --git a/autoload_configs/odbc_cdr.conf.xml b/autoload_configs/odbc_cdr.conf.xml +new file mode 100644 +index 0000000..617373e +--- /dev/null ++++ b/autoload_configs/odbc_cdr.conf.xml +@@ -0,0 +1,50 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++
++
++
+diff --git a/autoload_configs/sofia.conf.xml b/autoload_configs/sofia.conf.xml +deleted file mode 100644 +index 629a001..0000000 +--- a/autoload_configs/sofia.conf.xml ++++ /dev/null +@@ -1,29 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/autoload_configs/switch.conf.xml b/autoload_configs/switch.conf.xml +index 5ee62f3..2dd6bbe 100644 +--- a/autoload_configs/switch.conf.xml ++++ b/autoload_configs/switch.conf.xml +@@ -183,6 +183,7 @@ + + + ++ + +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- ]]> +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/dialplan/default/00_ladspa.xml b/dialplan/default/00_ladspa.xml +deleted file mode 100644 +index a26b193..0000000 +--- a/dialplan/default/00_ladspa.xml ++++ /dev/null +@@ -1,77 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/dialplan/default/00_pizza_demo.xml b/dialplan/default/00_pizza_demo.xml +deleted file mode 100644 +index 4b308fe..0000000 +--- a/dialplan/default/00_pizza_demo.xml ++++ /dev/null +@@ -1,9 +0,0 @@ +- +- +- +- +- +- +- +- +- +diff --git a/dialplan/default/01_Talking_Clock.xml b/dialplan/default/01_Talking_Clock.xml +deleted file mode 100644 +index 1424f2e..0000000 +--- a/dialplan/default/01_Talking_Clock.xml ++++ /dev/null +@@ -1,32 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/dialplan/default/01_example.com.xml b/dialplan/default/01_example.com.xml +deleted file mode 100644 +index bd61cd2..0000000 +--- a/dialplan/default/01_example.com.xml ++++ /dev/null +@@ -1,30 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/dialplan/default/ideasip.com.noload b/dialplan/default/ideasip.com.noload +deleted file mode 100644 +index 8b963e3..0000000 +--- a/dialplan/default/ideasip.com.noload ++++ /dev/null +@@ -1,7 +0,0 @@ +- +- +- +- +- +- +- +diff --git a/dialplan/default/pulver.com.noload b/dialplan/default/pulver.com.noload +deleted file mode 100644 +index 7b86caa..0000000 +--- a/dialplan/default/pulver.com.noload ++++ /dev/null +@@ -1,7 +0,0 @@ +- +- +- +- +- +- +- +diff --git a/dialplan/default/sipbroker.com.noload b/dialplan/default/sipbroker.com.noload +deleted file mode 100644 +index 4f5e4f8..0000000 +--- a/dialplan/default/sipbroker.com.noload ++++ /dev/null +@@ -1,7 +0,0 @@ +- +- +- +- +- +- +- +diff --git a/dialplan/default/sipphone.com.noload b/dialplan/default/sipphone.com.noload +deleted file mode 100644 +index a6ea9e8..0000000 +--- a/dialplan/default/sipphone.com.noload ++++ /dev/null +@@ -1,7 +0,0 @@ +- +- +- +- +- +- +- +diff --git a/dialplan/default/tollfreegateway.com.noload b/dialplan/default/tollfreegateway.com.noload +deleted file mode 100644 +index b7526bf..0000000 +--- a/dialplan/default/tollfreegateway.com.noload ++++ /dev/null +@@ -1,7 +0,0 @@ +- +- +- +- +- +- +- +diff --git a/dialplan/features.xml b/dialplan/features.xml +deleted file mode 100644 +index 665925f..0000000 +--- a/dialplan/features.xml ++++ /dev/null +@@ -1,67 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/dialplan/public.xml b/dialplan/public.xml +deleted file mode 100644 +index 020bb62..0000000 +--- a/dialplan/public.xml ++++ /dev/null +@@ -1,74 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/dialplan/public/00_inbound_did.xml b/dialplan/public/00_inbound_did.xml +deleted file mode 100644 +index 22b7223..0000000 +--- a/dialplan/public/00_inbound_did.xml ++++ /dev/null +@@ -1,18 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +diff --git a/directory/default.xml b/directory/default.xml +deleted file mode 100644 +index 54100b9..0000000 +--- a/directory/default.xml ++++ /dev/null +@@ -1,81 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/directory/default/1000.xml b/directory/default/1000.xml +deleted file mode 100644 +index 9bee406..0000000 +--- a/directory/default/1000.xml ++++ /dev/null +@@ -1,18 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/directory/default/1001.xml b/directory/default/1001.xml +deleted file mode 100644 +index 97c1931..0000000 +--- a/directory/default/1001.xml ++++ /dev/null +@@ -1,18 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/directory/default/1002.xml b/directory/default/1002.xml +deleted file mode 100644 +index d33691d..0000000 +--- a/directory/default/1002.xml ++++ /dev/null +@@ -1,18 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/directory/default/1003.xml b/directory/default/1003.xml +deleted file mode 100644 +index f8b5926..0000000 +--- a/directory/default/1003.xml ++++ /dev/null +@@ -1,18 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/directory/default/1004.xml b/directory/default/1004.xml +deleted file mode 100644 +index c3e7da5..0000000 +--- a/directory/default/1004.xml ++++ /dev/null +@@ -1,18 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/directory/default/1005.xml b/directory/default/1005.xml +deleted file mode 100644 +index 0e1165f..0000000 +--- a/directory/default/1005.xml ++++ /dev/null +@@ -1,18 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/directory/default/1006.xml b/directory/default/1006.xml +deleted file mode 100644 +index beaaa7e..0000000 +--- a/directory/default/1006.xml ++++ /dev/null +@@ -1,18 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/directory/default/1007.xml b/directory/default/1007.xml +deleted file mode 100644 +index 10470a5..0000000 +--- a/directory/default/1007.xml ++++ /dev/null +@@ -1,18 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/directory/default/1008.xml b/directory/default/1008.xml +deleted file mode 100644 +index 0e63fcf..0000000 +--- a/directory/default/1008.xml ++++ /dev/null +@@ -1,18 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/directory/default/1009.xml b/directory/default/1009.xml +deleted file mode 100644 +index 24db7f8..0000000 +--- a/directory/default/1009.xml ++++ /dev/null +@@ -1,18 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/directory/default/1010.xml b/directory/default/1010.xml +deleted file mode 100644 +index 6d8e0c1..0000000 +--- a/directory/default/1010.xml ++++ /dev/null +@@ -1,18 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/directory/default/1011.xml b/directory/default/1011.xml +deleted file mode 100644 +index 79d731d..0000000 +--- a/directory/default/1011.xml ++++ /dev/null +@@ -1,18 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/directory/default/1012.xml b/directory/default/1012.xml +deleted file mode 100644 +index 3839824..0000000 +--- a/directory/default/1012.xml ++++ /dev/null +@@ -1,18 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/directory/default/1013.xml b/directory/default/1013.xml +deleted file mode 100644 +index 6f9c8e4..0000000 +--- a/directory/default/1013.xml ++++ /dev/null +@@ -1,18 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/directory/default/1014.xml b/directory/default/1014.xml +deleted file mode 100644 +index 6a554c0..0000000 +--- a/directory/default/1014.xml ++++ /dev/null +@@ -1,18 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/directory/default/1015.xml b/directory/default/1015.xml +deleted file mode 100644 +index e94b888..0000000 +--- a/directory/default/1015.xml ++++ /dev/null +@@ -1,18 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/directory/default/1016.xml b/directory/default/1016.xml +deleted file mode 100644 +index 4f856fc..0000000 +--- a/directory/default/1016.xml ++++ /dev/null +@@ -1,18 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/directory/default/1017.xml b/directory/default/1017.xml +deleted file mode 100644 +index b0e43a0..0000000 +--- a/directory/default/1017.xml ++++ /dev/null +@@ -1,18 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/directory/default/1018.xml b/directory/default/1018.xml +deleted file mode 100644 +index 6d70719..0000000 +--- a/directory/default/1018.xml ++++ /dev/null +@@ -1,18 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/directory/default/1019.xml b/directory/default/1019.xml +deleted file mode 100644 +index f23a95f..0000000 +--- a/directory/default/1019.xml ++++ /dev/null +@@ -1,18 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/directory/default/brian.xml b/directory/default/brian.xml +deleted file mode 100644 +index 98f720f..0000000 +--- a/directory/default/brian.xml ++++ /dev/null +@@ -1,92 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/directory/default/default.xml b/directory/default/default.xml +deleted file mode 100644 +index aa138f1..0000000 +--- a/directory/default/default.xml ++++ /dev/null +@@ -1,26 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/directory/default/example.com.xml b/directory/default/example.com.xml +deleted file mode 100644 +index 42a33dd..0000000 +--- a/directory/default/example.com.xml ++++ /dev/null +@@ -1,26 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/directory/default/skinny-example.xml b/directory/default/skinny-example.xml +deleted file mode 100644 +index 357eb72..0000000 +--- a/directory/default/skinny-example.xml ++++ /dev/null +@@ -1,35 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +-