diff --git a/contact-center/app/src/main/java/com/cskefu/cc/aspect/ChannelAspect.java b/contact-center/app/src/main/java/com/cskefu/cc/aspect/ChannelAspect.java new file mode 100644 index 00000000..499c4701 --- /dev/null +++ b/contact-center/app/src/main/java/com/cskefu/cc/aspect/ChannelAspect.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2023 Beijing Huaxia Chunsong Technology Co., Ltd. + * , Licensed under the Chunsong Public + * License, Version 1.0 (the "License"), https://docs.cskefu.com/licenses/v1.html + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.cskefu.cc.aspect; + +import com.cskefu.cc.basic.MainContext; +import com.cskefu.cc.exception.BillingQuotaException; +import com.cskefu.cc.exception.BillingResourceException; +import com.cskefu.cc.model.Channel; +import com.cskefu.cc.model.User; +import com.cskefu.cc.proxy.LicenseProxy; +import org.apache.commons.lang3.StringUtils; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Aspect +@Component +public class ChannelAspect { + + private final static Logger logger = LoggerFactory.getLogger(ChannelAspect.class); + + @Autowired + private LicenseProxy licenseProxy; + + @Before("execution(* com.cskefu.cc.persistence.repository.ChannelRepository.save(..))") + public void beforeSave(final JoinPoint joinPoint) throws BillingResourceException, BillingQuotaException { + final Channel channel = (Channel) joinPoint.getArgs()[0]; + logger.info("[beforeSave] before channel id {}, type {}", channel.getId(), channel.getType()); + if (StringUtils.isBlank(channel.getId())) { + // create new Channel + if (StringUtils.equals(channel.getType(), MainContext.ChannelType.WEBIM.toString())) { + // create new WEBIM channel + licenseProxy.writeDownResourceUsageInStore(MainContext.BillingResource.CHANNELWEBIM, 1); + } + } else { + // update existed Channel + } + } +} diff --git a/contact-center/app/src/main/java/com/cskefu/cc/controller/admin/channel/ChannelController.java b/contact-center/app/src/main/java/com/cskefu/cc/controller/admin/channel/ChannelController.java index 93c5ff0f..53a7cf18 100644 --- a/contact-center/app/src/main/java/com/cskefu/cc/controller/admin/channel/ChannelController.java +++ b/contact-center/app/src/main/java/com/cskefu/cc/controller/admin/channel/ChannelController.java @@ -1,15 +1,15 @@ /* - * Copyright (C) 2023 Beijing Huaxia Chunsong Technology Co., Ltd. - * , Licensed under the Chunsong Public + * Copyright (C) 2023 Beijing Huaxia Chunsong Technology Co., Ltd. + * , Licensed under the Chunsong Public * License, Version 1.0 (the "License"), https://docs.cskefu.com/licenses/v1.html * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * Copyright (C) 2018- Jun. 2023 Chatopera Inc, , Licensed under the Apache License, Version 2.0, + * Copyright (C) 2018- Jun. 2023 Chatopera Inc, , Licensed under the Apache License, Version 2.0, * http://www.apache.org/licenses/LICENSE-2.0 - * Copyright (C) 2017 优客服-多渠道客服系统, Licensed under the Apache License, Version 2.0, + * Copyright (C) 2017 优客服-多渠道客服系统, Licensed under the Apache License, Version 2.0, * http://www.apache.org/licenses/LICENSE-2.0 */ package com.cskefu.cc.controller.admin.channel; @@ -18,6 +18,7 @@ import com.cskefu.cc.basic.MainContext; import com.cskefu.cc.basic.MainUtils; import com.cskefu.cc.cache.Cache; import com.cskefu.cc.controller.Handler; +import com.cskefu.cc.exception.BillingQuotaException; import com.cskefu.cc.model.*; import com.cskefu.cc.persistence.repository.ConsultInviteRepository; import com.cskefu.cc.persistence.repository.OrganRepository; @@ -27,6 +28,8 @@ import com.cskefu.cc.proxy.OrganProxy; import com.cskefu.cc.util.Base62; import com.cskefu.cc.util.Menu; import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Controller; @@ -37,6 +40,8 @@ import org.springframework.web.servlet.ModelAndView; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; + +import java.lang.reflect.UndeclaredThrowableException; import java.security.NoSuchAlgorithmException; import java.util.Date; import java.util.List; @@ -48,6 +53,7 @@ import java.util.Map; @Controller @RequestMapping("/admin/im") public class ChannelController extends Handler { + private final static Logger logger = LoggerFactory.getLogger(ChannelController.class); @Autowired private ChannelRepository snsAccountRes; @@ -93,40 +99,59 @@ public class ChannelController extends Handler { return request(super.createView("/admin/channel/im/add")); } + /** + * 创建新的网站渠道 + * + * @param request + * @param channel + * @return + * @throws NoSuchAlgorithmException + */ @RequestMapping("/save") - @Menu(type = "admin", subtype = "weixin") + @Menu(type = "admin", subtype = "im") public ModelAndView save(HttpServletRequest request, @Valid Channel channel) throws NoSuchAlgorithmException { Organ currentOrgan = super.getOrgan(request); String status = "new_webim_fail"; if (StringUtils.isNotBlank(channel.getBaseURL())) { - channel.setSnsid(Base62.encode(channel.getBaseURL()).toLowerCase()); - int count = snsAccountRes.countBySnsid(channel.getSnsid()); - if (count == 0) { - status = "new_webim_success"; - channel.setType(MainContext.ChannelType.WEBIM.toString()); - channel.setCreatetime(new Date()); - User curr = super.getUser(request); - channel.setCreater(curr.getId()); - channel.setOrgan(currentOrgan.getId()); + try { + channel.setSnsid(Base62.encode(channel.getBaseURL()).toLowerCase()); + int count = snsAccountRes.countBySnsid(channel.getSnsid()); + if (count == 0) { + status = "new_webim_success"; + channel.setType(MainContext.ChannelType.WEBIM.toString()); + channel.setCreatetime(new Date()); + User curr = super.getUser(request); + channel.setCreater(curr.getId()); + channel.setOrgan(currentOrgan.getId()); - snsAccountRes.save(channel); + snsAccountRes.save(channel); - /** - * 同时创建CousultInvite 记录 - */ - CousultInvite coultInvite = invite.findBySnsaccountid(channel.getSnsid()); - if (coultInvite == null) { - coultInvite = new CousultInvite(); - coultInvite.setSnsaccountid(channel.getSnsid()); - coultInvite.setCreate_time(new Date()); - coultInvite.setName(channel.getName()); - coultInvite.setOwner(channel.getCreater()); - coultInvite.setSkill(false); // 不启动技能组 - coultInvite.setConsult_skill_fixed(false); // 不绑定唯一技能组 - coultInvite.setAi(false); - coultInvite.setAifirst(false); - invite.save(coultInvite); + /** + * 同时创建CousultInvite 记录 + */ + CousultInvite coultInvite = invite.findBySnsaccountid(channel.getSnsid()); + if (coultInvite == null) { + coultInvite = new CousultInvite(); + coultInvite.setSnsaccountid(channel.getSnsid()); + coultInvite.setCreate_time(new Date()); + coultInvite.setName(channel.getName()); + coultInvite.setOwner(channel.getCreater()); + coultInvite.setSkill(false); // 不启动技能组 + coultInvite.setConsult_skill_fixed(false); // 不绑定唯一技能组 + coultInvite.setAi(false); + coultInvite.setAifirst(false); + invite.save(coultInvite); + } + } + } catch (Exception e) { + if (e instanceof UndeclaredThrowableException) { + logger.error("[save] BillingQuotaException", e); + if (StringUtils.startsWith(e.getCause().getMessage(), BillingQuotaException.SUFFIX)) { + status = e.getCause().getMessage(); + } + } else { + logger.error("[save] err", e); } } } diff --git a/contact-center/app/src/main/resources/static/js/CSKeFu_Admin.v1.js b/contact-center/app/src/main/resources/static/js/CSKeFu_Admin.v1.js index 6d333315..0aac693b 100644 --- a/contact-center/app/src/main/resources/static/js/CSKeFu_Admin.v1.js +++ b/contact-center/app/src/main/resources/static/js/CSKeFu_Admin.v1.js @@ -11,80 +11,136 @@ * Licensed under the Apache License, Version 2.0 * http://www.apache.org/licenses/LICENSE-2.0 */ -function processUserAddOrUpdateResult(responsecode, cb){ +/** + * 处理系统用户的创建的返回值 + * @param responsecode + * @param cb + */ +function processUserAddOrUpdateResult(responsecode, cb) { switch (responsecode) { case 'username_exist': - layer.msg('用户名存在,请重新填写',{icon: 2, time: 3000}); + layer.msg('用户名存在,请重新填写', {icon: 2, time: 3000}); // 清空用户名 $('input[name="username"]').val(""); break; case 'email_exist': - layer.msg('邮件存在,请重新填写',{icon: 2, time: 3000}); + layer.msg('邮件存在,请重新填写', {icon: 2, time: 3000}); // 清空邮件 $('input[name="email"]').val(""); break; case 'mobile_exist': - layer.msg('手机存在,请重新填写',{icon: 2, time: 3000}); + layer.msg('手机存在,请重新填写', {icon: 2, time: 3000}); // 清空手机号 $('input[name="mobile"]').val(""); break; case 'sip_account_exist': - layer.msg('SIP地址已经存在,请重新填写',{icon: 2, time: 3000}); + layer.msg('SIP地址已经存在,请重新填写', {icon: 2, time: 3000}); // 清空SIP $('input[name="sipaccount"]').val(""); break; case 'extension_binded': - layer.msg('分机号已经被其他用户绑定',{icon: 2, time: 3000}); + layer.msg('分机号已经被其他用户绑定', {icon: 2, time: 3000}); $('input[name="extensionid"]').val(""); break; case 'extension_not_exist': - layer.msg('绑定分机不存在',{icon: 2, time: 3000}); + layer.msg('绑定分机不存在', {icon: 2, time: 3000}); $('input[name="extensionid"]').val(""); break; case 'pbxhost_not_exist': - layer.msg('指定的呼叫中心语音平台不存在',{icon: 2, time: 3000}); + layer.msg('指定的呼叫中心语音平台不存在', {icon: 2, time: 3000}); $('input[name="pbxhostid"]').val(""); break; case 't1': - layer.msg('当前用户坐席就绪或对话未结束,不能切换为非坐席',{icon: 2, time: 3000}); + layer.msg('当前用户坐席就绪或对话未结束,不能切换为非坐席', {icon: 2, time: 3000}); break; case 'new_user_success': - layer.msg('新用户创建成功',{icon: 1, time: 1000}); + layer.msg('新用户创建成功', {icon: 1, time: 1000}); cb(); break; case 'edit_user_success': - layer.msg('用户编辑成功',{icon: 1, time: 1000}); + layer.msg('用户编辑成功', {icon: 1, time: 1000}); cb(); break; + default: + handleGeneralCodeInQueryPathOrApiResp(responsecode, cb); + } +} + +/** + * 处理在 RedirectURL, API 中返回的 code 信息: status, msg, etc. + * code 为约定的返回值,通过下面的函数进行展示 + * @param code + * @param cb + */ +function handleGeneralCodeInQueryPathOrApiResp(code, cb) { + switch (code) { case 'billingquotaexception.no_license_found': - layer.msg('【使用授权证书】证书不存在,联系系统超级管理员导入。',{icon: 2, time: 5000}); + layer.msg('【使用授权证书】证书不存在,联系系统超级管理员导入。', {icon: 2, time: 5000}); + if (cb && (typeof (x) === 'function')) { + cb() + } break; case 'billingquotaexception.response_unexpected': - layer.msg('【使用授权证书】证书商店返回异常,稍后再试。',{icon: 2, time: 5000}); + layer.msg('【使用授权证书】证书商店返回异常,稍后再试。', {icon: 2, time: 5000}); + if (cb && (typeof (x) === 'function')) { + cb() + } break; case 'billingquotaexception.invalid_request_body': - layer.msg('【使用授权证书】请求证书商店参数不合法,请获取最新软件代码。',{icon: 2, time: 5000}); + layer.msg('【使用授权证书】请求证书商店参数不合法,请获取最新软件代码。', {icon: 2, time: 5000}); + if (cb && (typeof (x) === 'function')) { + cb() + } break; case 'billingquotaexception.license_invalid': - layer.msg('【使用授权证书】证书商店中不存在该证书,请联系系统超级管理员导入。',{icon: 2, time: 5000}); + layer.msg('【使用授权证书】证书商店中不存在该证书,请联系系统超级管理员导入。', {icon: 2, time: 5000}); + if (cb && (typeof (x) === 'function')) { + cb() + } break; case 'billingquotaexception.product_invalid': - layer.msg('【使用授权证书】产品或产品款式不存在,请联系系统超级管理员导入新证书。',{icon: 2, time: 5000}); + layer.msg('【使用授权证书】产品或产品款式不存在,请联系系统超级管理员导入新证书。', {icon: 2, time: 5000}); + if (cb && (typeof (x) === 'function')) { + cb() + } break; case 'billingquotaexception.license_expired_or_exhausted': - layer.msg('【使用授权证书】证书过期或耗尽,请升级证书或绑定新证书。',{icon: 2, time: 5000}); + layer.msg('【使用授权证书】证书过期或耗尽,请升级证书或绑定新证书。', {icon: 2, time: 5000}); + if (cb && (typeof (x) === 'function')) { + cb() + } break; case 'billingquotaexception.license_disabled_serverinst': - layer.msg('【使用授权证书】证书商店禁用了本服务实例。',{icon: 2, time: 5000}); + layer.msg('【使用授权证书】证书商店禁用了本服务实例。', {icon: 2, time: 5000}); + if (cb && (typeof (x) === 'function')) { + cb() + } break; case 'billingquotaexception.license_unsupport_refund': - layer.msg('【使用授权证书】目前使用的证书不支持回退配额。',{icon: 2, time: 5000}); + layer.msg('【使用授权证书】目前使用的证书不支持回退配额。', {icon: 2, time: 5000}); + if (cb && (typeof (x) === 'function')) { + cb() + } break; case 'billingquotaexception.license_quota_inadequate': - layer.msg('【使用授权证书】本次操作需要使用的配额资源超过证书中剩余配额,请联系系统超级管理员升级证书。',{icon: 2, time: 5000}); + layer.msg('【使用授权证书】本次操作需要使用的配额资源超过证书中剩余配额,请联系系统超级管理员升级证书。', { + icon: 2, + time: 5000 + }); + if (cb && (typeof (x) === 'function')) { + cb() + } break; case 'billingquotaexception.internal_error': - layer.msg('【使用授权证书】系统错误,稍后再试。',{icon: 2, time: 5000}); + layer.msg('【使用授权证书】系统错误,稍后再试。', {icon: 2, time: 5000}); + if (cb && (typeof (x) === 'function')) { + cb() + } break; + default: + console.log("[handleGeneralCodeInQueryPathOrApiResp] none code matched", code); + if (cb && (typeof (x) === 'function')) { + cb("no_code_matched"); + } } } \ No newline at end of file diff --git a/contact-center/app/src/main/resources/templates/admin/channel/im/index.pug b/contact-center/app/src/main/resources/templates/admin/channel/im/index.pug index 6e47a370..1cc3695c 100644 --- a/contact-center/app/src/main/resources/templates/admin/channel/im/index.pug +++ b/contact-center/app/src/main/resources/templates/admin/channel/im/index.pug @@ -63,13 +63,13 @@ block content layui.use('layer', function () { var layer = layui.layer; - console.log(window.location.href) var status = '#{status}'; if (status == 'new_webim_success') layer.msg('网站添加成功', {icon: 1, time: 1000}) else if (status == 'new_webim_fail') layer.msg('网站添加失败', {icon: 2, time: 3000}) - + else + handleGeneralCodeInQueryPathOrApiResp(status); }); layui.use(['laypage', 'layer'], function () { var laypage = layui.laypage diff --git a/contact-center/app/src/main/resources/templates/admin/include/layout.pug b/contact-center/app/src/main/resources/templates/admin/include/layout.pug index ee14da9b..6bd354e0 100644 --- a/contact-center/app/src/main/resources/templates/admin/include/layout.pug +++ b/contact-center/app/src/main/resources/templates/admin/include/layout.pug @@ -33,6 +33,7 @@ html(xmlns='http://www.w3.org/1999/xhtml', xmlns:th='http://www.thymeleaf.org', script(src='/js/echarts.common.min.js') script(language='javascript', src='/js/theme/wonderland.js') script(src='/layui.js') + script(src="/js/CSKeFu_Admin.v1.js") script(src='/js/cskefu.js') if userExpTelemetry == 'on' script(src='https://www.googletagmanager.com/gtag/js?id=G-SBBX10RKTC' async) diff --git a/contact-center/app/src/main/resources/templates/apps/index.pug b/contact-center/app/src/main/resources/templates/apps/index.pug index 2cfe8656..a7378bbb 100644 --- a/contact-center/app/src/main/resources/templates/apps/index.pug +++ b/contact-center/app/src/main/resources/templates/apps/index.pug @@ -38,8 +38,6 @@ html(xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xm script(src="/js/moment-timezone.js") script(src="/js/moment-timezone-with-data.js") script(src="/layui.js") - //- #894 和原生Map行为不一致 - //- script(src="/js/cskefu.js") script(src="/im/js/socket.io.js") script(src="/js/CSKeFu_IM.v1.js")