mirror of
https://github.com/chatopera/cosin.git
synced 2025-06-16 18:30:03 +08:00
add previous code for v3.10.0
This commit is contained in:
commit
26ddc81aa8
20
.drone.yml
Normal file
20
.drone.yml
Normal file
@ -0,0 +1,20 @@
|
||||
pipeline:
|
||||
build-contact-center:
|
||||
group: build
|
||||
image: plugins/docker
|
||||
context: contact-center
|
||||
dockerfile: contact-center/Dockerfile
|
||||
repo: chatopera/contact-center
|
||||
tags: develop
|
||||
secrets: [ docker_username, docker_password ]
|
||||
|
||||
build-cc-switch:
|
||||
group: build
|
||||
image: plugins/docker
|
||||
context: cc-switch/app
|
||||
dockerfile: cc-switch/app/Dockerfile
|
||||
repo: chatopera/cc-switch
|
||||
tags: develop
|
||||
secrets: [ docker_username, docker_password ]
|
||||
|
||||
branches: develop
|
12
.github/ISSUE_TEMPLATE.md
vendored
Normal file
12
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
# 描述
|
||||
|
||||
## 现在行为
|
||||
|
||||
## 预期行为
|
||||
|
||||
# 解决方案
|
||||
|
||||
# 环境
|
||||
|
||||
* 代码版本:
|
||||
Git commit hash (`git rev-parse HEAD`)
|
29
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
29
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
<!--- 在标题中简略说明问题 -->
|
||||
|
||||
## 描述
|
||||
<!--- 详细的描述变更 -->
|
||||
|
||||
## 解决的问题
|
||||
<!--- 为什么变更是必要的? -->
|
||||
<!--- 如果这个PR解决了其他Issue,添加链接 -->
|
||||
|
||||
## 测试情况
|
||||
<!--- 详细介绍怎么测试变更了 -->
|
||||
<!--- 介绍测试环境 -->
|
||||
<!--- 变更对其他代码的影响 -->
|
||||
|
||||
## 截屏
|
||||
|
||||
## 变更的类型
|
||||
<!--- 变更有哪些特点,添加 `x` 到下面的对应项目中: -->
|
||||
- [ ] 解决Bug
|
||||
- [ ] 新功能(不影响其他功能)
|
||||
- [ ] 对其他功能有影响
|
||||
|
||||
## 检查:
|
||||
<!--- 检查下面,各项,添加 `x` 到下面的对应项目中: -->
|
||||
- [ ] 我的变更和代码规范一致
|
||||
- [ ] 我的变更需要更新文档
|
||||
- [ ] 我已经更新了对应的文档
|
||||
- [ ] 我增加的代码有单元测试
|
||||
- [ ] 所有单元测试都能通过
|
17
.gitignore
vendored
Normal file
17
.gitignore
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
*.swp
|
||||
*.swo
|
||||
*.sublime-*
|
||||
*.pyc
|
||||
jmeter.log
|
||||
__pycache__
|
||||
tmp/
|
||||
node_modules/
|
||||
sftp-config.json
|
||||
.DS_Store
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
*.idea
|
||||
~$*.xls*
|
||||
~$*.ppt*
|
||||
~$*.doc*
|
37
.gitlab/issue_templates/bug.md
Normal file
37
.gitlab/issue_templates/bug.md
Normal file
@ -0,0 +1,37 @@
|
||||
<!--- Provide a general summary of the issue in the Title above -->
|
||||
|
||||
## 预期行为
|
||||
|
||||
<!--- Tell us what should happen -->
|
||||
|
||||
## 实际行为
|
||||
|
||||
<!--- Tell us what happens instead of the expected behavior -->
|
||||
|
||||
## 解决方案
|
||||
|
||||
<!--- Not obligatory, but suggest a fix/reason for the bug, -->
|
||||
|
||||
## 重现步骤
|
||||
|
||||
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
|
||||
<!--- reproduce this bug. Include code to reproduce, if relevant -->
|
||||
|
||||
### 第一步
|
||||
|
||||
### 第二步
|
||||
|
||||
### 第三步
|
||||
|
||||
### 第四步
|
||||
|
||||
## 环境
|
||||
|
||||
<!--- How has this issue affected you? What are you trying to accomplish? -->
|
||||
<!--- Providing context helps us come up with a solution that is most useful in the real world -->
|
||||
|
||||
<!--- Provide a general summary of the issue in the Title above -->
|
||||
|
||||
### 版本
|
||||
|
||||
<!--- Git commit hash (`git rev-parse HEAD`) -->
|
5
.gitlab/issue_templates/task.md
Normal file
5
.gitlab/issue_templates/task.md
Normal file
@ -0,0 +1,5 @@
|
||||
# description
|
||||
|
||||
## parent #
|
||||
|
||||
# solution
|
6
.gitlab/issue_templates/user-story.md
Normal file
6
.gitlab/issue_templates/user-story.md
Normal file
@ -0,0 +1,6 @@
|
||||
# description
|
||||
|
||||
# Others
|
||||
|
||||
企业聊天机器人/产品需求汇总
|
||||
https://wiki.chatopera.com/pages/viewpage.action?pageId=4686818
|
5
.gitlab/merge_request_templates/bugfix.md
Normal file
5
.gitlab/merge_request_templates/bugfix.md
Normal file
@ -0,0 +1,5 @@
|
||||
# 描述
|
||||
|
||||
## 关联问题
|
||||
|
||||
<!-- BUG Issue 链接 -->
|
5
.gitlab/merge_request_templates/feature.md
Normal file
5
.gitlab/merge_request_templates/feature.md
Normal file
@ -0,0 +1,5 @@
|
||||
# 描述
|
||||
|
||||
## 新功能
|
||||
|
||||
<!-- 提交说明 -->
|
5
.gitlab/merge_request_templates/hotfix.md
Normal file
5
.gitlab/merge_request_templates/hotfix.md
Normal file
@ -0,0 +1,5 @@
|
||||
# 描述
|
||||
|
||||
## 关联问题
|
||||
|
||||
<!-- BUG Issue 链接 -->
|
5
.gitlab/merge_request_templates/release.md
Normal file
5
.gitlab/merge_request_templates/release.md
Normal file
@ -0,0 +1,5 @@
|
||||
# 描述
|
||||
|
||||
## 更新日志
|
||||
|
||||
<!-- 新版本 -->
|
15
.vscode/sftp.json
vendored
Normal file
15
.vscode/sftp.json
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "sftp",
|
||||
"remotePath": "git/cosinee",
|
||||
"protocol": "sftp",
|
||||
"uploadOnSave": true,
|
||||
"port": 22,
|
||||
"profiles": {
|
||||
"gamera": {
|
||||
"name": "gamera",
|
||||
"host": "gamera",
|
||||
"username": "hain",
|
||||
"privateKeyPath": "/Users/hain/.ssh/id_rsa"
|
||||
}
|
||||
}
|
||||
}
|
0
CHANGELOG.md
Normal file
0
CHANGELOG.md
Normal file
20
CONTRIBUTING.md
Normal file
20
CONTRIBUTING.md
Normal file
@ -0,0 +1,20 @@
|
||||
# 贡献注意事项
|
||||
|
||||
* 在[Issue](https://github.com/chatopera/cosin/issues)中查看有无相关问题
|
||||
|
||||
* 详细的介绍问题
|
||||
|
||||
* 注意文明用语
|
||||
|
||||
# 感谢您有意加入春松客服开源社区
|
||||
|
||||
<p align="center">
|
||||
<b>春松客服QQ交流群:185659917, <a href="https://jq.qq.com/?_wv=1027&k=5I1cJLP" target="_blank">点击链接加入群聊</a></b><br>
|
||||
<img src="https://user-images.githubusercontent.com/3538629/44917177-432d9700-ad6a-11e8-9420-46b0281073e6.png" width="200">
|
||||
</p>
|
||||
|
||||
|
||||
[![chatoper banner][co-banner-image]][co-url]
|
||||
|
||||
[co-banner-image]: https://user-images.githubusercontent.com/3538629/42383104-da925942-8168-11e8-8195-868d5fcec170.png
|
||||
[co-url]: https://www.chatopera.com
|
201
LICENSE
Normal file
201
LICENSE
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright (2018) 北京华夏春松科技有限公司
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
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.
|
252
README.md
Normal file
252
README.md
Normal file
@ -0,0 +1,252 @@
|
||||
[](https://microbadger.com/images/chatopera/contact-center:develop "Get your own image badge on microbadger.com") [](https://microbadger.com/images/chatopera/contact-center:develop "Get your own version badge on microbadger.com") [](https://hub.docker.com/r/chatopera/contact-center/) [](https://hub.docker.com/r/chatopera/contact-center/) [](https://microbadger.com/images/chatopera/contact-center:develop "Get your own commit badge on microbadger.com")
|
||||
|
||||
<p align="center">
|
||||
<b>春松客服QQ交流群:185659917, <a href="https://jq.qq.com/?_wv=1027&k=5I1cJLP" target="_blank">点击链接加入群聊</a></b><br>
|
||||
<img src="https://user-images.githubusercontent.com/3538629/44917177-432d9700-ad6a-11e8-9420-46b0281073e6.png" width="200">
|
||||
</p>
|
||||
|
||||
# 春松客服: 全渠道智能客服
|
||||
|
||||
春松客服是帮助中小型企业快速而低成本的获得好用的智能客服系统。
|
||||
|
||||
|
||||
<img src="https://user-images.githubusercontent.com/3538629/61031891-fc311900-a3f2-11e9-80cf-c8d0700538a0.png" width="600">
|
||||
|
||||
|
||||
春松客服是Chatopera自主研发以及基于且增强其它开源软件的方式实现的,春松客服会不断增强客服系统的智能化,这包括利用自然语言处理、机器学习和语音识别等技术让客服工作更有效率、客服满意度更高、成本更低。
|
||||
|
||||
**开源项目地址:** [https://github.com/chatopera/cosin](https://github.com/chatopera/cosin)
|
||||
|
||||
**开发环境搭建:** [https://github.com/chatopera/cosin/wiki/春松客服:开发环境](https://github.com/chatopera/cosin/wiki/%E6%98%A5%E6%9D%BE%E5%AE%A2%E6%9C%8D%EF%BC%9A%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83)
|
||||
|
||||
|
||||
## 内容结构
|
||||
|
||||
[产品演示](https://github.com/chatopera/cosin#%E4%BA%A7%E5%93%81%E6%BC%94%E7%A4%BA)
|
||||
|
||||
[功能](https://github.com/chatopera/cosin#%E5%8A%9F%E8%83%BD)
|
||||
|
||||
[开发文档](https://github.com/chatopera/cosin#%E5%BC%80%E5%8F%91%E6%96%87%E6%A1%A3)
|
||||
|
||||
[产品截图](https://github.com/chatopera/cosin#%E4%BA%A7%E5%93%81%E6%88%AA%E5%9B%BE)
|
||||
|
||||
[产品体系](https://github.com/chatopera/cosin#%E4%BA%A7%E5%93%81%E4%BD%93%E7%B3%BB)
|
||||
|
||||
[立即部署](https://github.com/chatopera/cosin#%E7%AB%8B%E5%8D%B3%E9%83%A8%E7%BD%B2)
|
||||
|
||||
[鸣谢](https://github.com/chatopera/cosin#%E9%B8%A3%E8%B0%A2)
|
||||
|
||||
[开源许可协议](https://github.com/chatopera/cosin#%E5%BC%80%E6%BA%90%E8%AE%B8%E5%8F%AF%E5%8D%8F%E8%AE%AE)
|
||||
|
||||
## 产品演示
|
||||
* 坐席工作台
|
||||
|
||||
[http://cc.chatopera.com/](http://cc.chatopera.com/)
|
||||
|
||||
| **登录账号** | **密码** |
|
||||
| --- | --- |
|
||||
| admin | admin1234 |
|
||||
|
||||
* 网页端访客程序
|
||||
|
||||
[http://cc.chatopera.com/testclient.html](http://cc.chatopera.com/testclient.html)
|
||||
|
||||
|
||||
## 功能
|
||||
|
||||
* 账号及组织机构管理:按组织、角色分配账号权限
|
||||
|
||||
* 联系人管理:细粒度维护客户信息
|
||||
|
||||
* 网页聊天组件:一分钟接入对话窗口
|
||||
|
||||
* 坐席工作台:汇聚多渠道访客请求
|
||||
|
||||
* 机器人客服:集成机器人平台服务,完成多轮对话和知识库问答
|
||||
|
||||
* 外呼系统:自动外呼,手动外呼,监听和报表等
|
||||
|
||||
<b> 《春松客服产品系列视频》 </b>
|
||||
<table align="center">
|
||||
<tr>
|
||||
<th>序号</th>
|
||||
<th>内容</th>
|
||||
<th>腾讯视频</th>
|
||||
<th>百度网盘</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>No. 1</td>
|
||||
<td>产品概述</td>
|
||||
<td><a href="https://v.qq.com/x/page/z0776g0osqu.html" target="_blank">观看</a></td>
|
||||
<td><a href="https://pan.baidu.com/s/1BzUveFSkCtfyeU1gUIjp1Q" target="_blank">下载</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>No. 2</td>
|
||||
<td>安装部署</td>
|
||||
<td><a href="https://v.qq.com/x/page/b07760u7f8t.html" target="_blank">观看</a></td>
|
||||
<td><a href="https://pan.baidu.com/s/1CnZBIIuWpDWATjKTMluzUA" target="_blank">下载</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>No. 3</td>
|
||||
<td>功能演示</td>
|
||||
<td><a href="https://v.qq.com/x/page/h077670ceg2.html" target="_blank">观看</a></td>
|
||||
<td><a href="https://pan.baidu.com/s/1bCAqUWwk_KQGyfUyvUMt9w" target="_blank">下载</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>No. 4</td>
|
||||
<td>账号体系</td>
|
||||
<td><a href="https://v.qq.com/x/page/b0776jwl6w1.html" target="_blank">观看</a></td>
|
||||
<td><a href="https://pan.baidu.com/s/18QA9jvXYtwa8Zt78Di9wrg" target="_blank">下载</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>No. 5</td>
|
||||
<td>客户关系管理</td>
|
||||
<td><a href="https://v.qq.com/x/page/d0776p8ghpr.html" target="_blank">观看</a></td>
|
||||
<td><a href="https://pan.baidu.com/s/1stfkufWkF4byWqvF-ch9Dw" target="_blank">下载</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>No. 6</td>
|
||||
<td>即时通信</td>
|
||||
<td><a href="https://v.qq.com/x/page/r0776rdgt6z.html" target="_blank">观看</a></td>
|
||||
<td><a href="https://pan.baidu.com/s/1eYEO5OtVu0gyxDI9a-xqJQ" target="_blank">下载</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>No. 7</td>
|
||||
<td>呼叫中心</td>
|
||||
<td><a href="https://v.qq.com/x/page/i07785u58jm.html" target="_blank">观看</a></td>
|
||||
<td><a href="https://pan.baidu.com/s/1qQOSNMHgcNdXuq0Xib1wXA" target="_blank">下载</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>No. 8</td>
|
||||
<td>数据报表</td>
|
||||
<td><a href="https://v.qq.com/x/page/e0778ptg6b0.html" target="_blank">观看</a></td>
|
||||
<td><a href="https://pan.baidu.com/s/1iQOz9HR3dkO4lgf5VI4CVw" target="_blank">下载</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
[*下载视频合集*](https://pan.baidu.com/s/1YH7d7nMm5wZQp7P8kID3KA)
|
||||
|
||||
## 使用说明
|
||||
|
||||
关于产品的具体使用说明,请参考[文档中心](https://docs.chatopera.com/omni-channel-customer-support-system.html)。
|
||||
|
||||
## 来自真实用户的反馈
|
||||
|
||||
```
|
||||
项目代码写的挺好的,容易维护,是不错的开源项目。
|
||||
```
|
||||
|
||||
-- 海洋 (深圳银之杰项目经理)
|
||||
|
||||
|
||||
```
|
||||
Amazing! 要的就是这个效果。
|
||||
```
|
||||
|
||||
-- 常经理 (某电器世界五百强企业)
|
||||
|
||||
|
||||
```
|
||||
我要在APP内集成,我看了好多项目了,就你们这个最好,基本就是一个商用化的项目。
|
||||
```
|
||||
|
||||
-- Engine X (某二手车出售平台技术负责人)
|
||||
|
||||
|
||||
## 开发文档
|
||||
|
||||
<p align="center">
|
||||
<b><a href="https://github.com/chatopera/cosin/wiki" target="_blank">开发文档</a></b><br>
|
||||
<a href="https://github.com/chatopera/cosin/wiki" target="_blank">
|
||||
<img src="https://user-images.githubusercontent.com/3538629/44992890-38be0800-afcb-11e8-8fde-a5a671d29764.png" width="300">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## 产品截图
|
||||
|
||||
<p align="center">
|
||||
<b>欢迎页</b><br>
|
||||
<img src="https://user-images.githubusercontent.com/3538629/44915395-6bff5d80-ad65-11e8-817a-8abb812fb5ee.png" width="900">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<b>坐席工作台</b><br>
|
||||
<img src="https://user-images.githubusercontent.com/3538629/44915582-eb8d2c80-ad65-11e8-8876-86c8b5bb5cc7.png" width="900">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<b>坐席监控</b><br>
|
||||
<img src="https://user-images.githubusercontent.com/3538629/44915711-432b9800-ad66-11e8-899b-1ea02244925d.png" width="900">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<b>外呼计划</b><br>
|
||||
<img src="https://user-images.githubusercontent.com/3538629/44915831-ab7a7980-ad66-11e8-88a5-a2cd23b8c689.png" width="900">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<b>通话记录</b><br>
|
||||
<img src="https://user-images.githubusercontent.com/3538629/44915218-feebc800-ad64-11e8-90fc-36ce96b0c09a.png" width="900">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<b>集成客服机器人</b><br>
|
||||
<img src="https://user-images.githubusercontent.com/3538629/51080565-4b82df00-1719-11e9-8cc4-dbbec0459224.png" width="900">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<b>客服机器人应答</b><br>
|
||||
<img src="https://user-images.githubusercontent.com/3538629/51080567-50479300-1719-11e9-85d8-d209370c9d10.png" width="900">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<b>更多功能,敬请期待 ...</b><br>
|
||||
<img src="https://user-images.githubusercontent.com/3538629/44916014-28a5ee80-ad67-11e8-936a-a2cdbe62f529.png" width="900">
|
||||
</p>
|
||||
|
||||
## 产品体系
|
||||
|
||||
<p align="center">
|
||||
<b>观看视频介绍</b><br>
|
||||
<a href="https://pan.baidu.com/s/1tqxqfYSvtjDGhh6bDQ-Vog" target="_blank">
|
||||
<img src="https://user-images.githubusercontent.com/3538629/45403926-6a039b80-b68f-11e8-86e2-5d1f04e3a7c7.png" width="900">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## 立即部署
|
||||
|
||||
* 企业版
|
||||
|
||||
通过青云AppCenter部署,青云AppCenter是开发运维一体化(DevOps)管理企业应用的平台,Chatopera的春松客服在2018年10月登录AppCenter,并借助PaaS平台强大的计算能力实现计算节点集群、存储节点HADR。从而保证了服务高可靠性、高性能、动态伸缩、一键备份和一键回滚等功能。
|
||||
|
||||
青云AppCenter以其提供的资源秒级计算特点,企业使用AppCenter中的春松客服应用,可以按需付费,灵活升配和降配,Chatopera也非常推荐客户使用青云服务。
|
||||
|
||||
<p align="center">
|
||||
<b>春松客服 on QingCloud</b><br>
|
||||
<a href="https://appcenter.qingcloud.com/apps/app-zdh88kz7/%E6%98%A5%E6%9D%BE%E5%AE%A2%E6%9C%8D" target="_blank">
|
||||
<img src="https://user-images.githubusercontent.com/3538629/47984143-a17f4900-e110-11e8-95c9-d8302e000c34.png" width="900">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
更为详细的部署文档见[春松客服上架青云AppCenter](https://github.com/chatopera/cosin/wiki/%E6%98%A5%E6%9D%BE%E5%AE%A2%E6%9C%8D%E4%B8%8A%E6%9E%B6%E9%9D%92%E4%BA%91AppCenter)。
|
||||
|
||||
* 社区版
|
||||
|
||||
参考部署[开源社区版本文档](https://github.com/chatopera/cosin/wiki/%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%83%A8%E7%BD%B2)。
|
||||
|
||||
## 鸣谢
|
||||
|
||||
[优客服](https://gitee.com/beimigame/ukefu)
|
||||
|
||||
[FreeSWITCH中国社区](http://www.freeswitch.org.cn/)
|
||||
|
||||
## 开源许可协议
|
||||
|
||||
Copyright (2018) <a href="https://www.chatopera.com/" target="_blank">北京华夏春松科技有限公司</a>
|
||||
|
||||
[Apache License Version 2.0](https://github.com/chatopera/cosin/blob/master/LICENSE)
|
||||
|
||||
[![chatoper banner][co-banner-image]][co-url]
|
||||
|
||||
[co-banner-image]: https://user-images.githubusercontent.com/3538629/42383104-da925942-8168-11e8-8195-868d5fcec170.png
|
||||
[co-url]: https://www.chatopera.com
|
18
cc-chatbot/.gitignore
vendored
Normal file
18
cc-chatbot/.gitignore
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
*.swp
|
||||
*.swo
|
||||
*.sublime-*
|
||||
*.pyc
|
||||
jmeter.log
|
||||
__pycache__
|
||||
tmp/
|
||||
node_modules/
|
||||
sftp-config.json
|
||||
.DS_Store
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
*.idea
|
||||
~$*.xls*
|
||||
~$*.ppt*
|
||||
~$*.doc*
|
||||
app/target/
|
73
cc-chatbot/README.md
Normal file
73
cc-chatbot/README.md
Normal file
@ -0,0 +1,73 @@
|
||||
# cc-chatbot
|
||||
Chatopera智能问答引擎的Java SDK.
|
||||
https://docs.chatopera.com/
|
||||
|
||||
支持
|
||||
|
||||
* 创建聊天机器人
|
||||
* 查询聊天机器人列表
|
||||
* 更新聊天机器人画像
|
||||
* 查询聊天机器人使用情况
|
||||
* 管理和检索多轮对话
|
||||
* 管理和检索知识库
|
||||
* 检索意图识别
|
||||
|
||||
|
||||
# 配置
|
||||
|
||||
使用maven,需要配置Chatopera的Nexus OSS仓库,具体见[文档](https://github.com/chatopera/cosin/wiki/%E6%98%A5%E6%9D%BE%E5%AE%A2%E6%9C%8D%EF%BC%9A%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83#%E4%BF%AE%E6%94%B9maven2%E9%85%8D%E7%BD%AE)。
|
||||
|
||||
```
|
||||
<dependency>
|
||||
<groupId>com.chatopera.chatbot</groupId>
|
||||
<artifactId>sdk</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
# API
|
||||
|
||||
## Chatbot v1
|
||||
|
||||
聊天机器人类,构造参数(tcp协议,hostname, 端口, 版本)
|
||||
|
||||
### Chatbot#getChatbots
|
||||
获取聊天机器人列表,支持检索查询,分页
|
||||
|
||||
### Chatbot#getChatbot
|
||||
通过聊天机器人ID获得聊天机器人详情
|
||||
|
||||
### Chatbot#conversation
|
||||
与指定的聊天机器人进行多轮对话
|
||||
|
||||
### Chatbot#faq
|
||||
与指定的聊天机器人进行知识库问答
|
||||
|
||||
|
||||
# 测试
|
||||
|
||||
```
|
||||
mvn test
|
||||
```
|
||||
|
||||
# 示例
|
||||
|
||||
```
|
||||
Chatbot cb = new Chatbot("http", "lhc-dev", 8003, "v1");
|
||||
JSONObject resp = cb.conversation("co_bot_1", "sdktest", "华夏春松在哪里", false);
|
||||
```
|
||||
|
||||
返回值参考 [智能问答引擎文档](https://docs.chatopera.com/chatbot-engine.html)。
|
||||
|
||||
|
||||
## 开源许可协议
|
||||
|
||||
Copyright (2018) <a href="https://www.chatopera.com/" target="_blank">北京华夏春松科技有限公司</a>
|
||||
|
||||
[Apache License Version 2.0](https://github.com/chatopera/cosin/blob/master/LICENSE)
|
||||
|
||||
[![chatoper banner][co-banner-image]][co-url]
|
||||
|
||||
[co-banner-image]: https://user-images.githubusercontent.com/3538629/42383104-da925942-8168-11e8-8195-868d5fcec170.png
|
||||
[co-url]: https://www.chatopera.com
|
||||
|
13
cc-chatbot/admin/deploy.sh
Executable file
13
cc-chatbot/admin/deploy.sh
Executable file
@ -0,0 +1,13 @@
|
||||
#! /bin/bash
|
||||
###########################################
|
||||
#
|
||||
###########################################
|
||||
|
||||
# constants
|
||||
baseDir=$(cd `dirname "$0"`;pwd)
|
||||
# functions
|
||||
|
||||
# main
|
||||
[ -z "${BASH_SOURCE[0]}" -o "${BASH_SOURCE[0]}" = "$0" ] || return
|
||||
cd $baseDir/../app
|
||||
mvn clean deploy -Dmaven.test.skip=true
|
13
cc-chatbot/admin/gen-idea.sh
Executable file
13
cc-chatbot/admin/gen-idea.sh
Executable file
@ -0,0 +1,13 @@
|
||||
#! /bin/bash
|
||||
###########################################
|
||||
#
|
||||
###########################################
|
||||
|
||||
# constants
|
||||
baseDir=$(cd `dirname "$0"`;pwd)
|
||||
# functions
|
||||
|
||||
# main
|
||||
[ -z "${BASH_SOURCE[0]}" -o "${BASH_SOURCE[0]}" = "$0" ] || return
|
||||
cd $baseDir/../app
|
||||
mvn idea:idea
|
13
cc-chatbot/admin/package.sh
Executable file
13
cc-chatbot/admin/package.sh
Executable file
@ -0,0 +1,13 @@
|
||||
#! /bin/bash
|
||||
###########################################
|
||||
#
|
||||
###########################################
|
||||
|
||||
# constants
|
||||
baseDir=$(cd `dirname "$0"`;pwd)
|
||||
# functions
|
||||
|
||||
# main
|
||||
[ -z "${BASH_SOURCE[0]}" -o "${BASH_SOURCE[0]}" = "$0" ] || return
|
||||
cd $baseDir/../app
|
||||
mvn package
|
13
cc-chatbot/admin/test.sh
Executable file
13
cc-chatbot/admin/test.sh
Executable file
@ -0,0 +1,13 @@
|
||||
#! /bin/bash
|
||||
###########################################
|
||||
#
|
||||
###########################################
|
||||
|
||||
# constants
|
||||
baseDir=$(cd `dirname "$0"`;pwd)
|
||||
# functions
|
||||
|
||||
# main
|
||||
[ -z "${BASH_SOURCE[0]}" -o "${BASH_SOURCE[0]}" = "$0" ] || return
|
||||
cd $baseDir/../app
|
||||
mvn test
|
44
cc-chatbot/app/.classpath
Normal file
44
cc-chatbot/app/.classpath
Normal file
@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" path="target/generated-sources/annotations">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="ignore_optional_problems" value="true"/>
|
||||
<attribute name="m2e-apt" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="ignore_optional_problems" value="true"/>
|
||||
<attribute name="m2e-apt" value="true"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
23
cc-chatbot/app/.project
Normal file
23
cc-chatbot/app/.project
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>sdk</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
@ -0,0 +1,4 @@
|
||||
eclipse.preferences.version=1
|
||||
encoding//src/main/java=UTF-8
|
||||
encoding//src/test/java=UTF-8
|
||||
encoding/<project>=UTF-8
|
2
cc-chatbot/app/.settings/org.eclipse.jdt.apt.core.prefs
Normal file
2
cc-chatbot/app/.settings/org.eclipse.jdt.apt.core.prefs
Normal file
@ -0,0 +1,2 @@
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.apt.aptEnabled=false
|
7
cc-chatbot/app/.settings/org.eclipse.jdt.core.prefs
Normal file
7
cc-chatbot/app/.settings/org.eclipse.jdt.core.prefs
Normal file
@ -0,0 +1,7 @@
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
|
||||
org.eclipse.jdt.core.compiler.compliance=1.8
|
||||
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
|
||||
org.eclipse.jdt.core.compiler.processAnnotations=disabled
|
||||
org.eclipse.jdt.core.compiler.release=disabled
|
||||
org.eclipse.jdt.core.compiler.source=1.8
|
4
cc-chatbot/app/.settings/org.eclipse.m2e.core.prefs
Normal file
4
cc-chatbot/app/.settings/org.eclipse.m2e.core.prefs
Normal file
@ -0,0 +1,4 @@
|
||||
activeProfiles=
|
||||
eclipse.preferences.version=1
|
||||
resolveWorkspaceProjects=true
|
||||
version=1
|
141
cc-chatbot/app/pom.xml
Normal file
141
cc-chatbot/app/pom.xml
Normal file
@ -0,0 +1,141 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.chatopera.chatbot</groupId>
|
||||
<artifactId>sdk</artifactId>
|
||||
<version>1.1.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>sdk</name>
|
||||
<description>Java SDK for Chatopera Conversational Engine.</description>
|
||||
<url>https://www.chatopera.com</url>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.3.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpasyncclient</artifactId>
|
||||
<version>4.0.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpmime</artifactId>
|
||||
<version>4.3.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
<version>20140107</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mashape.unirest</groupId>
|
||||
<artifactId>unirest-java</artifactId>
|
||||
<version>1.4.9</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>3.8.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-clean-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-site-plugin</artifactId>
|
||||
<version>3.7</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-project-info-reports-plugin</artifactId>
|
||||
<version>2.9</version>
|
||||
</plugin>
|
||||
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.7.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.20.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-install-plugin</artifactId>
|
||||
<version>2.5.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<version>2.8.2</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
<distributionManagement>
|
||||
<snapshotRepository>
|
||||
<id>chatopera</id>
|
||||
<url>http://192.168.2.217:8029/repository/maven-snapshots/</url>
|
||||
</snapshotRepository>
|
||||
<repository>
|
||||
<id>chatopera</id>
|
||||
<url>http://192.168.2.217:8029/repository/maven-releases/</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>hain</id>
|
||||
<name>Hai Liang Wang</name>
|
||||
<email>hailiang.hl.wang@gmail.com</email>
|
||||
<url>https://github.com/Samurais</url>
|
||||
<organization>Chatopera Inc.</organization>
|
||||
<organizationUrl>http://www.chatopera.com</organizationUrl>
|
||||
<roles>
|
||||
<role>architect</role>
|
||||
<role>developer</role>
|
||||
</roles>
|
||||
<timezone>Asia/Shanghai</timezone>
|
||||
</developer>
|
||||
</developers>
|
||||
<reporting>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-project-info-reports-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</reporting>
|
||||
</project>
|
@ -0,0 +1,332 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.chatbot;
|
||||
|
||||
import com.mashape.unirest.http.exceptions.UnirestException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class ChatbotAPI {
|
||||
private String schema;
|
||||
private String hostname;
|
||||
private int port;
|
||||
private String baseUrl;
|
||||
|
||||
private ChatbotAPI() {
|
||||
}
|
||||
|
||||
|
||||
public ChatbotAPI(final String baseUrl) throws ChatbotAPIRuntimeException, MalformedURLException {
|
||||
if (StringUtils.isBlank(baseUrl))
|
||||
throw new ChatbotAPIRuntimeException("智能问答引擎URL不能为空。");
|
||||
|
||||
URL url = new URL(baseUrl);
|
||||
this.schema = url.getProtocol();
|
||||
this.hostname = url.getHost();
|
||||
this.port = url.getPort();
|
||||
|
||||
if (port == -1) {
|
||||
this.baseUrl = this.schema + "://" + this.hostname + "/api/v1";
|
||||
} else {
|
||||
this.baseUrl = this.schema + "://" + this.hostname + ":" + this.port + "/api/v1";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public ChatbotAPI(final String schema, final String hostname, final int port, final String version) {
|
||||
this.schema = schema;
|
||||
this.hostname = hostname;
|
||||
this.port = port;
|
||||
this.baseUrl = schema + "://" + hostname + ":" + Integer.toString(this.port) + "/api/" + version;
|
||||
}
|
||||
|
||||
public ChatbotAPI(final String schema, final String hostname, final int port) {
|
||||
this(schema, hostname, port, "v1");
|
||||
}
|
||||
|
||||
public ChatbotAPI(final String hostname, final int port) {
|
||||
this("http", hostname, port);
|
||||
}
|
||||
|
||||
public String getSchema() {
|
||||
return schema;
|
||||
}
|
||||
|
||||
public String getHostname() {
|
||||
return hostname;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public String getBaseUrl() {
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取聊天机器人列表
|
||||
*
|
||||
* @return
|
||||
* @throws ChatbotAPIRuntimeException
|
||||
*/
|
||||
public JSONObject getChatbots(final String fields, final String q, final int page, final int limit) throws ChatbotAPIRuntimeException {
|
||||
try {
|
||||
HashMap<String, Object> queryString = new HashMap<String, Object>();
|
||||
if (StringUtils.isNotBlank(fields)) {
|
||||
queryString.put("fields", fields);
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(q)) {
|
||||
queryString.put("q", q);
|
||||
}
|
||||
|
||||
queryString.put("page", page);
|
||||
|
||||
if (limit > 0) {
|
||||
queryString.put("limit", limit);
|
||||
}
|
||||
|
||||
return RestAPI.get(this.getBaseUrl() + "/chatbot", queryString);
|
||||
} catch (UnirestException e) {
|
||||
throw new ChatbotAPIRuntimeException(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过ChatbotID检查一个聊天机器人是否存在
|
||||
*
|
||||
* @param chatbotID
|
||||
* @return
|
||||
*/
|
||||
public boolean exists(final String chatbotID) throws ChatbotAPIRuntimeException {
|
||||
try {
|
||||
JSONObject result = this.getChatbot(chatbotID);
|
||||
int rc = result.getInt("rc");
|
||||
if (rc == 0) {
|
||||
return true;
|
||||
} else if (rc == 3) {
|
||||
return false;
|
||||
} else {
|
||||
throw new ChatbotAPIRuntimeException("查询聊天机器人异常返回。");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new ChatbotAPIRuntimeException(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建聊天机器人
|
||||
*
|
||||
* @param chatbotID 聊天机器人标识,由[a-zA-Z0-9-]组成,字母开头
|
||||
* @param name 拟人化的名字
|
||||
* @param primaryLanguage 首选语言,支持 [zh_CN|en_US]
|
||||
* @param fallback 兜底回复
|
||||
* @param description 描述
|
||||
* @param welcome 欢迎语
|
||||
* @return
|
||||
*/
|
||||
public JSONObject createBot(final String chatbotID,
|
||||
final String name,
|
||||
final String primaryLanguage,
|
||||
final String fallback,
|
||||
final String description,
|
||||
final String welcome) throws ChatbotAPIRuntimeException {
|
||||
HashMap<String, Object> body = new HashMap<String, Object>();
|
||||
body.put("chatbotID", chatbotID);
|
||||
body.put("name", name);
|
||||
body.put("primaryLanguage", primaryLanguage);
|
||||
body.put("description", description);
|
||||
body.put("fallback", fallback);
|
||||
body.put("welcome", welcome);
|
||||
|
||||
try {
|
||||
return RestAPI.post(this.getBaseUrl() + "/chatbot/" + chatbotID, body);
|
||||
} catch (UnirestException e) {
|
||||
throw new ChatbotAPIRuntimeException(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新聊天机器人
|
||||
*
|
||||
* @param chatbotID
|
||||
* @param description
|
||||
* @param fallback
|
||||
* @param welcome
|
||||
* @return
|
||||
* @throws ChatbotAPIRuntimeException
|
||||
*/
|
||||
public boolean updateByChatbotID(final String chatbotID,
|
||||
final String name,
|
||||
final String description,
|
||||
final String fallback,
|
||||
final String welcome) throws ChatbotAPIRuntimeException {
|
||||
if (StringUtils.isBlank(chatbotID))
|
||||
throw new ChatbotAPIRuntimeException("不合法的参数,【chatbotID】不能为空。");
|
||||
|
||||
HashMap<String, Object> body = new HashMap<String, Object>();
|
||||
if (StringUtils.isNotBlank(description))
|
||||
body.put("description", description);
|
||||
if (StringUtils.isNotBlank(fallback))
|
||||
body.put("fallback", fallback);
|
||||
if (StringUtils.isNotBlank(welcome))
|
||||
body.put("welcome", welcome);
|
||||
if (StringUtils.isNotBlank(name))
|
||||
body.put("name", name);
|
||||
|
||||
try {
|
||||
JSONObject result = RestAPI.put(this.baseUrl + "/chatbot/" + chatbotID, body, null);
|
||||
if (result.getInt("rc") == 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (UnirestException e) {
|
||||
throw new ChatbotAPIRuntimeException(e.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 删除聊天机器人
|
||||
*
|
||||
* @param chatbotID
|
||||
* @return
|
||||
* @throws ChatbotAPIRuntimeException
|
||||
*/
|
||||
public boolean deleteByChatbotID(final String chatbotID) throws ChatbotAPIRuntimeException {
|
||||
if (StringUtils.isBlank(chatbotID))
|
||||
throw new ChatbotAPIRuntimeException("聊天机器人ID不能为空。");
|
||||
try {
|
||||
JSONObject result = RestAPI.delete(this.getBaseUrl() + "/chatbot/" + chatbotID, null);
|
||||
if (result.getInt("rc") == 0)
|
||||
return true;
|
||||
return false;
|
||||
} catch (UnirestException e) {
|
||||
throw new ChatbotAPIRuntimeException(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取聊天机器人详情
|
||||
*
|
||||
* @param chatbotID
|
||||
* @return
|
||||
* @throws ChatbotAPIRuntimeException
|
||||
*/
|
||||
public JSONObject getChatbot(final String chatbotID) throws ChatbotAPIRuntimeException {
|
||||
try {
|
||||
return RestAPI.get(this.getBaseUrl() + "/chatbot/" + chatbotID);
|
||||
} catch (UnirestException e) {
|
||||
throw new ChatbotAPIRuntimeException(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* validate params
|
||||
*
|
||||
* @param chatbotID
|
||||
* @param fromUserId
|
||||
* @param textMessage
|
||||
*/
|
||||
private void v(final String chatbotID, final String fromUserId, final String textMessage) throws ChatbotAPIRuntimeException {
|
||||
if (StringUtils.isBlank(chatbotID))
|
||||
throw new ChatbotAPIRuntimeException("[conversation] 不合法的聊天机器人标识。");
|
||||
|
||||
if (StringUtils.isBlank(fromUserId))
|
||||
throw new ChatbotAPIRuntimeException("[conversation] 不合法的用户标识。");
|
||||
|
||||
if (StringUtils.isBlank(textMessage))
|
||||
throw new ChatbotAPIRuntimeException("[conversation] 不合法的消息内容。");
|
||||
}
|
||||
|
||||
/**
|
||||
* 与聊天机器人进行多轮对话
|
||||
*
|
||||
* @param fromUserId
|
||||
* @param textMessage
|
||||
* @param debug
|
||||
* @return
|
||||
*/
|
||||
public JSONObject conversation(final String chatbotID, final String fromUserId, final String textMessage, boolean debug) throws ChatbotAPIRuntimeException {
|
||||
v(chatbotID, fromUserId, textMessage);
|
||||
HashMap<String, Object> body = new HashMap<String, Object>();
|
||||
body.put("fromUserId", fromUserId);
|
||||
body.put("textMessage", textMessage);
|
||||
body.put("isDebug", debug);
|
||||
|
||||
try {
|
||||
JSONObject resp = RestAPI.post(this.getBaseUrl() + "/chatbot/" + chatbotID + "/conversation/query", body);
|
||||
return resp;
|
||||
} catch (UnirestException e) {
|
||||
throw new ChatbotAPIRuntimeException(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 意图识别
|
||||
* @param chatbotID
|
||||
* @param clientId
|
||||
* @param textMessage
|
||||
* @return
|
||||
* @throws UnirestException
|
||||
*/
|
||||
public JSONObject intent(final String chatbotID, final String clientId, final String textMessage) throws ChatbotAPIRuntimeException {
|
||||
if(StringUtils.isBlank(chatbotID) || StringUtils.isBlank(clientId) || StringUtils.isBlank(textMessage))
|
||||
throw new ChatbotAPIRuntimeException("参数不合法,不能为空。");
|
||||
|
||||
HashMap<String, Object> body = new HashMap<String, Object>();
|
||||
body.put("clientId", clientId);
|
||||
body.put("query", textMessage);
|
||||
try {
|
||||
JSONObject result = RestAPI.post(this.baseUrl + "/chatbot/" + chatbotID, body);
|
||||
return result;
|
||||
} catch (UnirestException e) {
|
||||
throw new ChatbotAPIRuntimeException(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检索知识库
|
||||
*
|
||||
* @param chatbotID
|
||||
* @param fromUserId
|
||||
* @param textMessage
|
||||
* @param isDebug
|
||||
* @return
|
||||
*/
|
||||
public JSONObject faq(final String chatbotID, final String fromUserId, final String textMessage, final boolean isDebug) throws ChatbotAPIRuntimeException {
|
||||
v(chatbotID, fromUserId, textMessage);
|
||||
HashMap<String, Object> body = new HashMap<String, Object>();
|
||||
body.put("fromUserId", fromUserId);
|
||||
body.put("query", textMessage);
|
||||
body.put("isDebug", isDebug);
|
||||
try {
|
||||
JSONObject resp = RestAPI.post(this.getBaseUrl() + "/chatbot/" + chatbotID + "/faq/query", body);
|
||||
return resp;
|
||||
} catch (UnirestException e) {
|
||||
throw new ChatbotAPIRuntimeException(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.chatbot;
|
||||
|
||||
import com.mashape.unirest.http.exceptions.UnirestException;
|
||||
|
||||
public class ChatbotAPIRuntimeException extends Exception{
|
||||
public ChatbotAPIRuntimeException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
120
cc-chatbot/app/src/main/java/com/chatopera/chatbot/RestAPI.java
Normal file
120
cc-chatbot/app/src/main/java/com/chatopera/chatbot/RestAPI.java
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.chatbot;
|
||||
|
||||
import com.mashape.unirest.http.HttpResponse;
|
||||
import com.mashape.unirest.http.JsonNode;
|
||||
import com.mashape.unirest.http.Unirest;
|
||||
import com.mashape.unirest.http.exceptions.UnirestException;
|
||||
import com.mashape.unirest.request.GetRequest;
|
||||
import com.mashape.unirest.request.HttpRequestWithBody;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* RestAPI接口
|
||||
*/
|
||||
public class RestAPI {
|
||||
|
||||
/**
|
||||
* patch headers
|
||||
*
|
||||
* @param headers
|
||||
*/
|
||||
private static void x(HashMap<String, String> headers) {
|
||||
if (headers == null) {
|
||||
headers = new HashMap<String, String>();
|
||||
headers.put("accept", "application/json");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!headers.containsKey("Content-Type"))
|
||||
headers.put("Content-Type", "application/json");
|
||||
|
||||
|
||||
if (!headers.containsKey("accept"))
|
||||
headers.put("accept", "application/json");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Post
|
||||
*
|
||||
* @param url
|
||||
* @param body
|
||||
* @param query
|
||||
* @param headers
|
||||
* @return
|
||||
* @throws UnirestException
|
||||
*/
|
||||
public static JSONObject post(final String url, final HashMap<String, Object> body, final HashMap<String, Object> query, HashMap<String, String> headers) throws UnirestException {
|
||||
HttpRequestWithBody request = Unirest.post(url);
|
||||
x(headers);
|
||||
HttpResponse<JsonNode> resp = request
|
||||
.headers(headers)
|
||||
.queryString(query)
|
||||
.fields(body)
|
||||
.asJson();
|
||||
|
||||
// parse response
|
||||
JSONObject obj = resp.getBody().getObject();
|
||||
return obj;
|
||||
}
|
||||
|
||||
public static JSONObject post(final String url, final HashMap<String, Object> body) throws UnirestException {
|
||||
return post(url, body, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get
|
||||
*
|
||||
* @param url
|
||||
* @param queryString
|
||||
* @param headers
|
||||
* @return
|
||||
* @throws UnirestException
|
||||
*/
|
||||
public static JSONObject get(final String url, final HashMap<String, Object> queryString, HashMap<String, String> headers) throws UnirestException {
|
||||
GetRequest request = Unirest.get(url);
|
||||
x(headers);
|
||||
HttpResponse<JsonNode> resp = request
|
||||
.headers(headers)
|
||||
.queryString(queryString)
|
||||
.asJson();
|
||||
// parse response
|
||||
JSONObject obj = resp.getBody().getObject();
|
||||
return obj;
|
||||
}
|
||||
|
||||
public static JSONObject get(final String url) throws UnirestException {
|
||||
return get(url, null, null);
|
||||
}
|
||||
|
||||
public static JSONObject get(final String url, HashMap<String, Object> queryString) throws UnirestException {
|
||||
return get(url, queryString, null);
|
||||
}
|
||||
|
||||
public static JSONObject delete(final String url, HashMap<String, String> headers) throws UnirestException {
|
||||
x(headers);
|
||||
return Unirest.delete(url).headers(headers).asJson().getBody().getObject();
|
||||
}
|
||||
|
||||
public static JSONObject put(final String url, HashMap<String, Object> body, HashMap<String, String> headers) throws UnirestException {
|
||||
x(headers);
|
||||
return Unirest.put(url).headers(headers).fields(body).asJson().getBody().getObject();
|
||||
}
|
||||
}
|
26
cc-chatbot/app/src/site/site.xml
Normal file
26
cc-chatbot/app/src/site/site.xml
Normal file
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project name="sdk" xmlns="http://maven.apache.org/DECORATION/1.8.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/DECORATION/1.8.0 http://maven.apache.org/xsd/decoration-1.8.0.xsd">
|
||||
<bannerLeft>
|
||||
<name>sdk</name>
|
||||
<src>https://maven.apache.org/images/apache-maven-project.png</src>
|
||||
<href>https://www.apache.org/</href>
|
||||
</bannerLeft>
|
||||
|
||||
<bannerRight>
|
||||
<src>https://maven.apache.org/images/maven-logo-black-on-white.png</src>
|
||||
<href>https://maven.apache.org/</href>
|
||||
</bannerRight>
|
||||
|
||||
<skin>
|
||||
<groupId>org.apache.maven.skins</groupId>
|
||||
<artifactId>maven-fluido-skin</artifactId>
|
||||
<version>1.7</version>
|
||||
</skin>
|
||||
|
||||
<body>
|
||||
<menu ref="parent" />
|
||||
<menu ref="reports" />
|
||||
</body>
|
||||
</project>
|
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.chatbot;
|
||||
|
||||
import junit.framework.Test;
|
||||
import junit.framework.TestCase;
|
||||
import junit.framework.TestSuite;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
|
||||
/**
|
||||
* Unit test for simple App.
|
||||
*/
|
||||
public class ChatbotAPITest
|
||||
extends TestCase {
|
||||
private ChatbotAPI cb;
|
||||
|
||||
/**
|
||||
* Create the test case
|
||||
*
|
||||
* @param testName name of the test case
|
||||
*/
|
||||
public ChatbotAPITest(String testName) {
|
||||
super(testName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the suite of tests being tested
|
||||
*/
|
||||
public static Test suite() {
|
||||
return new TestSuite(ChatbotAPITest.class);
|
||||
}
|
||||
|
||||
public void setUp() {
|
||||
this.cb = new ChatbotAPI("http", "lhc-dev", 8003, "v1");
|
||||
}
|
||||
|
||||
/**
|
||||
* Rigourous Test :-)
|
||||
*/
|
||||
public void testChatbot() {
|
||||
assertEquals(this.cb.getPort(), 8003);
|
||||
}
|
||||
|
||||
public void testGetChatbot() {
|
||||
try {
|
||||
JSONObject resp = this.cb.getChatbot("co_bot_1");
|
||||
System.out.println("[testGetChatbot] " + resp.toString());
|
||||
} catch (ChatbotAPIRuntimeException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetChatbots() {
|
||||
try {
|
||||
JSONObject resp = this.cb.getChatbots("name chatbotID", null, 0, 10);
|
||||
System.out.println("[testGetChatbots] resp " + resp.toString());
|
||||
} catch (ChatbotAPIRuntimeException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void testConversation() {
|
||||
try {
|
||||
JSONObject resp = this.cb.conversation("co_bot_1", "sdktest", "华夏春松在哪里", false);
|
||||
System.out.println("[testConversation] resp " + resp.toString());
|
||||
} catch (ChatbotAPIRuntimeException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void testFaq() {
|
||||
try {
|
||||
JSONObject resp = this.cb.faq("co_bot_1", "sdktest", "华夏春松在哪里", false);
|
||||
System.out.print("[testFaq] resp " + resp.toString());
|
||||
} catch (ChatbotAPIRuntimeException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void testParseUrl() {
|
||||
try {
|
||||
ChatbotAPI c = new ChatbotAPI("https://local:8000/");
|
||||
System.out.println("chatbot baseUrl " + c.getBaseUrl());
|
||||
assertEquals("https://local:8000/api/v1", c.getBaseUrl());
|
||||
} catch (ChatbotAPIRuntimeException e) {
|
||||
e.printStackTrace();
|
||||
} catch (MalformedURLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void testExists() {
|
||||
JSONObject profile = null;
|
||||
try {
|
||||
assertTrue(this.cb.exists("co_bot_1"));
|
||||
} catch (ChatbotAPIRuntimeException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void testCreateBot() {
|
||||
try {
|
||||
JSONObject j = this.cb.createBot("cc_bot_2",
|
||||
"小云2",
|
||||
"zh_CN",
|
||||
"我不了解。",
|
||||
"小云机器人",
|
||||
"你好,我是小云。");
|
||||
} catch (ChatbotAPIRuntimeException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
14
cc-switch/.gitignore
vendored
Normal file
14
cc-switch/.gitignore
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
*.swp
|
||||
*.swo
|
||||
*.sublime-*
|
||||
*.pyc
|
||||
__pycache__
|
||||
node_modules/
|
||||
sftp-config.json
|
||||
.DS_Store
|
||||
~$*
|
||||
.vscode
|
||||
.idea/
|
||||
.composer.phar
|
||||
app/config/dev.env
|
||||
ax.js
|
1
cc-switch/README.md
Normal file
1
cc-switch/README.md
Normal file
@ -0,0 +1 @@
|
||||
# cc-switch
|
16
cc-switch/admin/dev.sh
Executable file
16
cc-switch/admin/dev.sh
Executable file
@ -0,0 +1,16 @@
|
||||
#! /bin/bash
|
||||
###########################################
|
||||
#
|
||||
###########################################
|
||||
|
||||
# constants
|
||||
baseDir=$(cd `dirname "$0"`;pwd)
|
||||
export ACTIVEMQ_HOST=corsair
|
||||
export REDIS_HOST=corsair
|
||||
|
||||
# functions
|
||||
|
||||
# main
|
||||
[ -z "${BASH_SOURCE[0]}" -o "${BASH_SOURCE[0]}" = "$0" ] || return
|
||||
cd $baseDir/../app
|
||||
DEBUG=cc* npm run dev:start
|
14
cc-switch/admin/install.sh
Executable file
14
cc-switch/admin/install.sh
Executable file
@ -0,0 +1,14 @@
|
||||
#! /bin/bash
|
||||
###########################################
|
||||
#
|
||||
###########################################
|
||||
|
||||
# constants
|
||||
baseDir=$(cd `dirname "$0"`;pwd)
|
||||
# functions
|
||||
|
||||
# main
|
||||
[ -z "${BASH_SOURCE[0]}" -o "${BASH_SOURCE[0]}" = "$0" ] || return
|
||||
cd $baseDir/..
|
||||
cd app
|
||||
npm install
|
24
cc-switch/admin/test.sh
Executable file
24
cc-switch/admin/test.sh
Executable file
@ -0,0 +1,24 @@
|
||||
#! /bin/bash
|
||||
###########################################
|
||||
#
|
||||
###########################################
|
||||
|
||||
# constants
|
||||
baseDir=$(cd `dirname "$0"`;pwd)
|
||||
export ACTIVEMQ_HOST=corsair
|
||||
export REDIS_HOST=corsair
|
||||
|
||||
# functions
|
||||
|
||||
# main
|
||||
[ -z "${BASH_SOURCE[0]}" -o "${BASH_SOURCE[0]}" = "$0" ] || return
|
||||
cd $baseDir/../app
|
||||
|
||||
# for x in {1..100}; do
|
||||
# echo $x
|
||||
# DEBUG=cc* ava --timeout=10hrs
|
||||
# sleep 5
|
||||
# done
|
||||
|
||||
DEBUG=cc* ava --timeout=10hrs $*
|
||||
|
14
cc-switch/app/Dockerfile
Normal file
14
cc-switch/app/Dockerfile
Normal file
@ -0,0 +1,14 @@
|
||||
FROM node:carbon-alpine
|
||||
|
||||
RUN apk add --no-cache tzdata && \
|
||||
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
|
||||
echo "Asia/Shanghai" > /etc/timezone
|
||||
|
||||
RUN mkdir -p /usr/src/app
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY package.json /usr/src/app/
|
||||
RUN npm install && npm cache clean --force
|
||||
COPY . /usr/src/app
|
||||
|
||||
CMD [ "npm", "start" ]
|
7
cc-switch/app/config/dev.env.sample
Normal file
7
cc-switch/app/config/dev.env.sample
Normal file
@ -0,0 +1,7 @@
|
||||
|
||||
PBX_CHANNEL_ID=bxzq
|
||||
FREESWITCH_HOST=freeswitch
|
||||
REDIS_HOST=redis
|
||||
MINIO_END_POINT=mini
|
||||
MINIO_ACCESS_KEY=key
|
||||
MINIO_SECRET_KEY=secret
|
42
cc-switch/app/config/index.js
Normal file
42
cc-switch/app/config/index.js
Normal file
@ -0,0 +1,42 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const dotenv = require('dotenv');
|
||||
|
||||
const PBX_CHANNEL_ID = 'test';
|
||||
|
||||
const FREESWITCH_HOST = 'localhost';
|
||||
const FREESWITCH_PORT = 8021;
|
||||
const FREESWITCH_MAX_CHANNEL = 5;
|
||||
|
||||
const MINIO_END_POINT = 'localhost';
|
||||
const MINIO_ACCESS_KEY = 'key';
|
||||
const MINIO_SECRET_KEY = 'secret';
|
||||
|
||||
const REDIS_HOST = 'localhost';
|
||||
const REDIS_PORT = 6379;
|
||||
|
||||
const config = {
|
||||
PBX_CHANNEL_ID,
|
||||
FREESWITCH_HOST,
|
||||
FREESWITCH_PORT,
|
||||
FREESWITCH_MAX_CHANNEL,
|
||||
REDIS_HOST,
|
||||
REDIS_PORT,
|
||||
MINIO_END_POINT,
|
||||
MINIO_ACCESS_KEY,
|
||||
MINIO_SECRET_KEY,
|
||||
};
|
||||
|
||||
let envFile = path.join(__dirname, 'dev.env');
|
||||
if (fs.existsSync(envFile)) {
|
||||
dotenv.config({ path: envFile });
|
||||
}
|
||||
|
||||
for (let key in config) {
|
||||
let value = process.env[key];
|
||||
if (value) {
|
||||
config[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = config;
|
81
cc-switch/app/control.js
Normal file
81
cc-switch/app/control.js
Normal file
@ -0,0 +1,81 @@
|
||||
const Redis = require('ioredis');
|
||||
const _ = require('lodash');
|
||||
const config = require('./config');
|
||||
const debug = require('debug')('cc-switch:control');
|
||||
|
||||
const FS_SIP_STATUS = `pbx:${config.PBX_CHANNEL_ID}:sips`;
|
||||
const FS_CHANNE_CC_TO_FS = `pbx:${config.PBX_CHANNEL_ID}:execute`;
|
||||
const FS_DIALPLAN_STATUS = `pbx:${config.PBX_CHANNEL_ID}:status`;
|
||||
const FS_DIALPLAN_TARGET = `pbx:${config.PBX_CHANNEL_ID}:targets`;
|
||||
const FS_EVENT_TO_CC = `pbx:${config.PBX_CHANNEL_ID}:events`;
|
||||
|
||||
const sub = new Redis({
|
||||
host: config.REDIS_HOST,
|
||||
port: config.REDIS_PORT,
|
||||
db: 2,
|
||||
});
|
||||
|
||||
const redis = new Redis({
|
||||
host: config.REDIS_HOST,
|
||||
port: config.REDIS_PORT,
|
||||
db: 2,
|
||||
});
|
||||
|
||||
module.exports = exports = {
|
||||
subExecute(fn) {
|
||||
return new Promise((resolve, reject) => {
|
||||
debug('订阅控制信道');
|
||||
sub.subscribe(FS_CHANNE_CC_TO_FS, (err, count) => {
|
||||
if (err) {
|
||||
debug('订阅失败: %o', err);
|
||||
reject(err);
|
||||
} else {
|
||||
debug('订阅成功: %s', count);
|
||||
sub.on('message', (channel, message) => {
|
||||
if (channel == FS_CHANNE_CC_TO_FS) {
|
||||
message = JSON.parse(message);
|
||||
fn(message);
|
||||
}
|
||||
});
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
getStatus() {
|
||||
return redis.hgetall(FS_DIALPLAN_STATUS).then(rows =>
|
||||
_.map(rows, (v, k) => {
|
||||
let obj = JSON.parse(v);
|
||||
obj.id = k;
|
||||
return obj;
|
||||
}),
|
||||
);
|
||||
},
|
||||
getNextCall(dialplanId) {
|
||||
return redis
|
||||
.rpop(`${FS_DIALPLAN_TARGET}:${dialplanId}`)
|
||||
.then(data => JSON.parse(data));
|
||||
},
|
||||
getSips() {
|
||||
return redis.hgetall(FS_SIP_STATUS).then(sips =>
|
||||
_.map(sips, (v, k) => ({
|
||||
no: k,
|
||||
state: v,
|
||||
})),
|
||||
);
|
||||
},
|
||||
removeSips(nos) {
|
||||
return redis.hdel(FS_SIP_STATUS, ...nos);
|
||||
},
|
||||
setSips(sips) {
|
||||
let args = [];
|
||||
_.forEach(sips, s => {
|
||||
args.push(s.no);
|
||||
args.push(s.state);
|
||||
});
|
||||
return redis.hmset(FS_SIP_STATUS, ...args);
|
||||
},
|
||||
sendEvent(data) {
|
||||
return redis.publish(FS_EVENT_TO_CC, JSON.stringify(data));
|
||||
},
|
||||
};
|
109
cc-switch/app/dialplan.js
Normal file
109
cc-switch/app/dialplan.js
Normal file
@ -0,0 +1,109 @@
|
||||
const _ = require('lodash');
|
||||
const moment = require('moment');
|
||||
const fs = require('./fs');
|
||||
const control = require('./control');
|
||||
const debug = require('debug')('cc-switch:dialplan');
|
||||
const { access } = require('fs');
|
||||
const minio = require('./minio');
|
||||
|
||||
class Dialplan {
|
||||
constructor(id, concurrency, sips) {
|
||||
this.id = id;
|
||||
this.concurrency = concurrency;
|
||||
this.tasks = [];
|
||||
this.sips = sips;
|
||||
this.state = 'create';
|
||||
}
|
||||
|
||||
start(cb) {
|
||||
debug('开始呼叫计划: %o', this.id);
|
||||
this.time = setInterval(() => this.loop(), 1000);
|
||||
this.start = 'start';
|
||||
this.cb = cb;
|
||||
}
|
||||
|
||||
callTask(to, channel) {
|
||||
debug('呼叫目标号码: %s', to);
|
||||
|
||||
let message = { to, channel, dialplan: this.id, type: 'callout' };
|
||||
let call = fs.call(to);
|
||||
call.sips = this.sips;
|
||||
let removeThisCall = () => _.remove(this.tasks, call);
|
||||
|
||||
call.on('channel_answer', uuid => {
|
||||
debug('用户接听 %s', to);
|
||||
message.uuid = uuid;
|
||||
message.from = call.sip;
|
||||
message.ops = 'answer';
|
||||
message.createtime = moment().valueOf();
|
||||
control.sendEvent(message);
|
||||
});
|
||||
|
||||
call.on('channel_hangup', () => {
|
||||
debug('用户挂机 %s', to);
|
||||
message.ops = 'hangup';
|
||||
message.createtime = moment().valueOf();
|
||||
removeThisCall();
|
||||
|
||||
if (call.state != 'call') {
|
||||
let file = `/usr/recordings/archive/${call.uuid}.wav`;
|
||||
message.record = `${moment().format('YYYY-MM-DD')}/${call.uuid}.wav`;
|
||||
|
||||
access(file, err => {
|
||||
if (err) {
|
||||
debug('录音文件不存在: %s', call.uuid);
|
||||
} else {
|
||||
debug('上传录音: %s', call.uuid);
|
||||
|
||||
setTimeout(() => {
|
||||
minio.fPutObject('chatopera', message.record, file).catch(err => {
|
||||
debug('上传录音失败 %s error: %o', call.uuid, err);
|
||||
});
|
||||
}, 5000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
control.sendEvent(message);
|
||||
});
|
||||
|
||||
call.on('error', () => {
|
||||
debug('呼叫错误 %s', to);
|
||||
message.ops = 'hangup';
|
||||
removeThisCall();
|
||||
control.sendEvent(message);
|
||||
});
|
||||
|
||||
return call;
|
||||
}
|
||||
|
||||
async loop() {
|
||||
// debug('呼叫计划循环: %s', this.id);
|
||||
while (this.tasks.length < this.concurrency) {
|
||||
let info = await control.getNextCall(this.id);
|
||||
|
||||
if (info) {
|
||||
let { to, channel } = info;
|
||||
let call = this.callTask(to, channel);
|
||||
this.tasks.push(call);
|
||||
} else {
|
||||
debug('完成呼叫计划: %s', this.id);
|
||||
this.start = 'finish';
|
||||
clearInterval(this.time);
|
||||
if (this.cb) {
|
||||
this.cb();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async stop() {
|
||||
debug('停止呼叫计划: %s', this.id);
|
||||
clearInterval(this.time);
|
||||
// await Promise.all(this.tasks);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = Dialplan;
|
60
cc-switch/app/engine.js
Normal file
60
cc-switch/app/engine.js
Normal file
@ -0,0 +1,60 @@
|
||||
const _ = require('lodash');
|
||||
const debug = require('debug')('cc-switch:engine');
|
||||
|
||||
const config = require('./config');
|
||||
const control = require('./control');
|
||||
const Dialplan = require('./dialplan');
|
||||
const fs = require('./fs');
|
||||
|
||||
class Engine {
|
||||
constructor() {
|
||||
this.dialplans = [];
|
||||
}
|
||||
|
||||
createDialplan(id, concurrency, sips) {
|
||||
debug('创建呼叫计划: %s ,并发: %s', id, concurrency);
|
||||
if (!_.find(this.dialplans, { id })) {
|
||||
let dialplan = new Dialplan(id, concurrency, sips);
|
||||
this.dialplans.push(dialplan);
|
||||
dialplan.start(() => _.remove(this.dialplans, dialplan));
|
||||
}
|
||||
}
|
||||
|
||||
async init() {
|
||||
await fs.init();
|
||||
|
||||
await control.subExecute(message => {
|
||||
debug('接收控制命令: %o', message);
|
||||
let { ops, channel } = message;
|
||||
if (channel == config.PBX_CHANNEL_ID) {
|
||||
if (ops == 'start') {
|
||||
this.createDialplan(
|
||||
message.dialplan,
|
||||
message.concurrency,
|
||||
message.sips,
|
||||
);
|
||||
} else if (ops == 'monitor') {
|
||||
fs.eavesdrop(message.sip, message.uuid);
|
||||
} else {
|
||||
let dialplan = _.find(this.dialplans, { id: message.dialplan });
|
||||
if (dialplan) {
|
||||
dialplan.stop();
|
||||
_.remove(this.dialplans, dialplan);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
debug('获取初始状态');
|
||||
let dialplanTasks = await control.getStatus();
|
||||
|
||||
for (let task of dialplanTasks) {
|
||||
let { id, concurrency, status } = task;
|
||||
if (status == '执行中') {
|
||||
this.createDialplan(id, concurrency);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = Engine;
|
406
cc-switch/app/fs.js
Normal file
406
cc-switch/app/fs.js
Normal file
@ -0,0 +1,406 @@
|
||||
const EventEmitter = require('events');
|
||||
const _ = require('lodash');
|
||||
const esl = require('modesl');
|
||||
const debug = require('debug')('cc-switch:fs');
|
||||
const config = require('./config');
|
||||
const control = require('./control');
|
||||
const { access } = require('fs');
|
||||
const minio = require('./minio');
|
||||
const moment = require('moment');
|
||||
|
||||
const { FREESWITCH_HOST, FREESWITCH_PORT, FREESWITCH_MAX_CHANNEL } = config;
|
||||
|
||||
const parseTable = body => {
|
||||
let list = [];
|
||||
let rows = body.split('\n');
|
||||
let first = rows.shift();
|
||||
if (first) {
|
||||
let head = first.split(',');
|
||||
for (let r of rows) {
|
||||
if (r) {
|
||||
let obj = {};
|
||||
let cell = r.split(',');
|
||||
for (let i = 0; i < head.length; i++) {
|
||||
obj[head[i]] = cell[i];
|
||||
}
|
||||
list.push(obj);
|
||||
} else break;
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
};
|
||||
|
||||
const parseHeaders = headers => {
|
||||
let obj = {};
|
||||
for (let h of headers) {
|
||||
obj[h.name] = h.value;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
|
||||
let conn;
|
||||
|
||||
const doConnect = reg =>
|
||||
new Promise((resolve, reject) => {
|
||||
conn = new esl.Connection(
|
||||
FREESWITCH_HOST,
|
||||
FREESWITCH_PORT,
|
||||
'ClueCon',
|
||||
() => {
|
||||
reg();
|
||||
resolve();
|
||||
},
|
||||
);
|
||||
|
||||
conn.once('error', err => {
|
||||
debug('connect freeswitch error: %o', err);
|
||||
reject(err);
|
||||
|
||||
setTimeout(() => doConnect(reg), 2000);
|
||||
});
|
||||
});
|
||||
|
||||
const show = cmd =>
|
||||
new Promise((resolve, reject) => {
|
||||
// debug('执行命令: %s', cmd);
|
||||
|
||||
conn.bgapi(cmd, ({ body }) => {
|
||||
resolve(parseTable(body));
|
||||
});
|
||||
});
|
||||
|
||||
const showRegistrations = () => show('show registrations');
|
||||
|
||||
const showChannels = () => show('show channels');
|
||||
|
||||
const showCalls = () => show('show calls');
|
||||
|
||||
const record = uuid =>
|
||||
new Promise((resolve, reject) => {
|
||||
debug('开始录音: %s', uuid);
|
||||
|
||||
conn.bgapi(
|
||||
`uuid_record ${uuid} start /usr/recordings/archive/${uuid}.wav`,
|
||||
({ body }) => {
|
||||
console.log(body);
|
||||
resolve();
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
const callOut = phone =>
|
||||
new Promise((resolve, reject) => {
|
||||
debug('拨打电话: %s', phone);
|
||||
|
||||
conn.bgapi(`originate sofia/gateway/goipx/${phone} &park`, ({ body }) => {
|
||||
let match = body.match(/([a-f\d]{8}(-[a-f\d]{4}){3}-[a-f\d]{12}?)/i);
|
||||
if (match) {
|
||||
resolve(match[1]);
|
||||
} else {
|
||||
reject(new Error(`呼叫 ${phone} 失败`));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const callSip = (phone, originate) =>
|
||||
new Promise((resolve, reject) => {
|
||||
debug('拨打SIP: %s', phone);
|
||||
|
||||
('originate user/1000 &park');
|
||||
|
||||
conn.bgapi(
|
||||
`originate {origination_caller_id_number=${originate}}user/${phone} &park`,
|
||||
({ body }) => {
|
||||
let match = body.match(/([a-f\d]{8}(-[a-f\d]{4}){3}-[a-f\d]{12}?)/i);
|
||||
if (match) {
|
||||
resolve(match[1]);
|
||||
} else {
|
||||
reject(new Error(`呼叫SIP ${phone} 失败`));
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
const bridge = (luuid, ruuid) =>
|
||||
new Promise((resolve, reject) => {
|
||||
debug('桥接: %s <--> %s', luuid, ruuid);
|
||||
|
||||
conn.bgapi(`uuid_bridge ${luuid} ${ruuid}`, ({ body }) => {
|
||||
if (/OK/.test(body)) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error('桥接失败'));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const eavesdrop = (sip, uuid) =>
|
||||
new Promise((resolve, reject) => {
|
||||
debug('监听: %s <--> %s', sip, uuid);
|
||||
|
||||
conn.bgapi(`originate user/${sip} &eavesdrop(${uuid})`, ({ body }) => {
|
||||
if (/OK/.test(body)) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error('监听失败'));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
class FreeSwitch extends EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
this.tasks = [];
|
||||
this.wait = [];
|
||||
this.sips = [];
|
||||
}
|
||||
|
||||
async init() {
|
||||
debug('初始化Freeswich');
|
||||
await doConnect(() => this.regEvent());
|
||||
|
||||
setInterval(async () => {
|
||||
let sips = await showRegistrations();
|
||||
let toDel = _.chain(await control.getSips())
|
||||
.map(s => s.no)
|
||||
.filter(k => !_.find(sips, { reg_user: k }))
|
||||
.value();
|
||||
if (toDel.length > 0) {
|
||||
await control.removeSips(toDel);
|
||||
}
|
||||
|
||||
let channels = await showChannels();
|
||||
let updateSips = _.map(sips, ({ reg_user }) => {
|
||||
return {
|
||||
no: reg_user,
|
||||
state: _.find(channels, { dest: reg_user }) ? '通话' : '空闲',
|
||||
};
|
||||
});
|
||||
if (updateSips.length > 0) {
|
||||
await control.setSips(updateSips);
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
regEvent() {
|
||||
conn.events('plain CUSTOM sofia::register');
|
||||
|
||||
conn.on('esl::event::CUSTOM::*', ({ subclass, headers }) => {
|
||||
if (subclass == 'sofia::register') {
|
||||
let { username } = parseHeaders(headers);
|
||||
|
||||
debug('SIP %s 已上线', username);
|
||||
control.setSips([{ no: username, state: '空闲' }]);
|
||||
}
|
||||
});
|
||||
|
||||
conn.on('esl::event::CHANNEL_ANSWER::*', async ({ headers }) => {
|
||||
let variable = parseHeaders(headers);
|
||||
let { variable_uuid } = variable;
|
||||
debug('接通电话: %s', variable_uuid);
|
||||
|
||||
let call = _.find(this.tasks, { uuid: variable_uuid });
|
||||
if (call) {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
if (call.state == 'hangup') {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await call.linkSip();
|
||||
call.emit('channel_answer', variable_uuid);
|
||||
return;
|
||||
} catch (e) {
|
||||
debug('link sip error: %o', e);
|
||||
}
|
||||
}
|
||||
|
||||
// debug('link sip error: %o', err);
|
||||
} else if (variable.variable_sip_gateway_name == 'goipx') {
|
||||
let {
|
||||
variable_bridge_channel,
|
||||
variable_sip_to_user: sip_to_user,
|
||||
variable_call_uuid,
|
||||
} = variable;
|
||||
let match = variable_bridge_channel.match(/sofia\/internal\/(\d+)@/);
|
||||
if (match) {
|
||||
let sip_from_user = match[1];
|
||||
let call = new CallOut(
|
||||
variable_uuid,
|
||||
sip_to_user,
|
||||
variable_call_uuid,
|
||||
sip_from_user,
|
||||
);
|
||||
this.tasks.push(call);
|
||||
call.emit('channel_answer', variable_uuid);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
conn.on('esl::event::CHANNEL_HANGUP::*', ({ headers }) => {
|
||||
let event = parseHeaders(headers);
|
||||
|
||||
let { variable_uuid } = event;
|
||||
debug('挂断电话: %s', variable_uuid);
|
||||
|
||||
let call = _.find(this.tasks, { uuid: variable_uuid });
|
||||
if (call) {
|
||||
this.removeCall(call);
|
||||
call.state = 'hangup';
|
||||
call.emit('channel_hangup', variable_uuid);
|
||||
} else if (event.variable_sip_gateway_name == 'goipx') {
|
||||
let {
|
||||
variable_bridge_channel,
|
||||
variable_sip_to_user: sip_to_user,
|
||||
variable_call_uuid,
|
||||
} = event;
|
||||
let match =
|
||||
variable_bridge_channel &&
|
||||
variable_bridge_channel.match(/sofia\/internal\/(\d+)@/);
|
||||
if (match) {
|
||||
let sip_from_user = match[1];
|
||||
let call = new CallOut(
|
||||
variable_uuid,
|
||||
sip_to_user,
|
||||
null,
|
||||
sip_from_user,
|
||||
);
|
||||
|
||||
call.emit('channel_hangup', variable_uuid);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
_.filter(this.tasks, { state: 'call' }).length <=
|
||||
FREESWITCH_MAX_CHANNEL &&
|
||||
this.wait.length > 0
|
||||
) {
|
||||
debug('执行队列');
|
||||
let func = this.wait.shift();
|
||||
func();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
removeCall(call) {
|
||||
_.remove(this.tasks, call);
|
||||
}
|
||||
|
||||
call(phone) {
|
||||
let call = new Call(phone);
|
||||
this.tasks.push(call);
|
||||
|
||||
let doCall = async () => {
|
||||
try {
|
||||
await call.start();
|
||||
} catch (err) {
|
||||
this.removeCall(call);
|
||||
call.emit('error', err);
|
||||
}
|
||||
};
|
||||
|
||||
if (this.tasks.length <= FREESWITCH_MAX_CHANNEL) {
|
||||
doCall();
|
||||
} else {
|
||||
this.wait.push(doCall);
|
||||
}
|
||||
|
||||
return call;
|
||||
}
|
||||
|
||||
eavesdrop(sip, uuid) {
|
||||
return eavesdrop(sip, uuid);
|
||||
}
|
||||
}
|
||||
|
||||
class Call extends EventEmitter {
|
||||
constructor(phone) {
|
||||
super();
|
||||
|
||||
this.sips = [];
|
||||
this.phone = phone;
|
||||
this.try = 0;
|
||||
}
|
||||
|
||||
async start() {
|
||||
this.uuid = await callOut(this.phone);
|
||||
this.state = 'call';
|
||||
}
|
||||
|
||||
async linkSip() {
|
||||
debug('链接SIP电话');
|
||||
|
||||
let sips = _.chain(await control.getSips())
|
||||
.filter({ state: '空闲' })
|
||||
.map(s => s.no)
|
||||
.value();
|
||||
|
||||
this.sip = _.sample(_.intersection(sips, this.sips));
|
||||
if (!this.sip) {
|
||||
throw new Error('无可用SIP');
|
||||
}
|
||||
|
||||
await control.setSips([{ no: this.sip, state: '通话' }]);
|
||||
|
||||
this.sip_uuid = await callSip(this.sip, this.phone);
|
||||
await bridge(this.uuid, this.sip_uuid);
|
||||
await record(this.uuid);
|
||||
this.state = 'answer';
|
||||
}
|
||||
}
|
||||
|
||||
class CallOut extends EventEmitter {
|
||||
constructor(uuid, phone, sip_uuid, sip) {
|
||||
super();
|
||||
|
||||
this.uuid = uuid;
|
||||
this.phone = phone;
|
||||
this.sip_uuid = sip_uuid;
|
||||
this.sip = sip;
|
||||
|
||||
let message = {
|
||||
uuid,
|
||||
from: sip,
|
||||
to: phone,
|
||||
channel: config.PBX_CHANNEL_ID,
|
||||
type: 'callout',
|
||||
};
|
||||
|
||||
this.on('channel_answer', () => {
|
||||
debug('用户接听 %s -> %s', sip, phone);
|
||||
message.ops = 'answer';
|
||||
message.createtime = moment().valueOf();
|
||||
control.sendEvent(message);
|
||||
});
|
||||
|
||||
this.on('channel_hangup', () => {
|
||||
debug('用户挂机 %s -> %s', sip, phone);
|
||||
message.ops = 'hangup';
|
||||
message.createtime = moment().valueOf();
|
||||
|
||||
if (sip_uuid) {
|
||||
let file = `/usr/recordings/archive/${sip_uuid}.wav`;
|
||||
message.record = `${moment().format('YYYY-MM-DD')}/${uuid}.wav`;
|
||||
|
||||
access(file, err => {
|
||||
if (err) {
|
||||
debug('录音文件不存在: %s', uuid);
|
||||
} else {
|
||||
debug('上传录音: %s', uuid);
|
||||
|
||||
setTimeout(() => {
|
||||
minio.fPutObject('chatopera', message.record, file).catch(err => {
|
||||
debug('上传录音失败 %s error: %o', uuid, err);
|
||||
});
|
||||
}, 5000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
control.sendEvent(message);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = new FreeSwitch();
|
14
cc-switch/app/index.js
Normal file
14
cc-switch/app/index.js
Normal file
@ -0,0 +1,14 @@
|
||||
const debug = require('debug')('cc-switch');
|
||||
const Engine = require('./engine');
|
||||
|
||||
const engine = new Engine();
|
||||
|
||||
engine
|
||||
.init()
|
||||
.then(() => {
|
||||
console.log('cc-switch started');
|
||||
})
|
||||
.catch(err => {
|
||||
debug('cc-switch start error: %o', err);
|
||||
process.exit();
|
||||
});
|
12
cc-switch/app/minio.js
Normal file
12
cc-switch/app/minio.js
Normal file
@ -0,0 +1,12 @@
|
||||
const Minio = require('minio');
|
||||
const config = require('./config');
|
||||
|
||||
let client = new Minio.Client({
|
||||
endPoint: config.MINIO_END_POINT,
|
||||
accessKey: config.MINIO_ACCESS_KEY,
|
||||
secretKey: config.MINIO_SECRET_KEY,
|
||||
port: 9000,
|
||||
useSSL: false,
|
||||
});
|
||||
|
||||
module.exports = exports = client;
|
26
cc-switch/app/package.json
Normal file
26
cc-switch/app/package.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "cc-switch",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "node index.js",
|
||||
"dev:start": "nodemon index.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"debug": "^3.1.0",
|
||||
"dotenv": "^6.0.0",
|
||||
"ioredis": "^4.0.0",
|
||||
"lodash": ">=4.17.11",
|
||||
"minio": "^7.0.0",
|
||||
"modesl": "^1.2.0",
|
||||
"moment": "^2.22.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ava": "^0.25.0",
|
||||
"nodemon": "^1.18.3"
|
||||
}
|
||||
}
|
47
cc-switch/app/test/callout.answer.test.js
Normal file
47
cc-switch/app/test/callout.answer.test.js
Normal file
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Redis Test
|
||||
*/
|
||||
const test = require('ava');
|
||||
const debug = require('debug')('cc-switch:test:redis');
|
||||
const Redis = require('ioredis');
|
||||
const config = require('../config');
|
||||
const util = require('util');
|
||||
const moment = require('moment');
|
||||
|
||||
|
||||
const redis = new Redis({
|
||||
host: config.REDIS_HOST,
|
||||
port: config.REDIS_PORT,
|
||||
db: 2,
|
||||
});
|
||||
|
||||
const EVENT_TYPE_CALLOUT = "callout";
|
||||
const EVENT_QUEUE_QUEUE_CC_TO_FS = "cc:to:freeswitch";
|
||||
const CALLOUT_DIALPLAN_STATUS = "callout:dialplan:status";
|
||||
const CALLOUT_DIALPLAN_TARGET = "freeswitch:%s:callout";
|
||||
const CALLOUT_CC_FROM_FS = "pbx:bxzq:events";
|
||||
|
||||
|
||||
|
||||
test.only("Redis Test # 外呼接通", async(t) => {
|
||||
let now = moment();
|
||||
now.add(-3, "minutes");
|
||||
console.log("接通时间:", now);
|
||||
|
||||
let payload = {
|
||||
"uuid": "9a0cbc81-ccae-425e-8d3d-369b872a6481",
|
||||
"to": "13213213213",
|
||||
"from": "1003",
|
||||
"type": "callout",
|
||||
"channel": "bxzq",
|
||||
"dialplan": "4028827365b2acec0165b307afe405de",
|
||||
"createtime": now.valueOf(),
|
||||
"ops": "answer"
|
||||
}
|
||||
|
||||
redis.publish(CALLOUT_CC_FROM_FS, JSON.stringify(payload));
|
||||
t.pass();
|
||||
})
|
||||
|
||||
|
||||
|
41
cc-switch/app/test/callout.hangup.test.js
Normal file
41
cc-switch/app/test/callout.hangup.test.js
Normal file
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Redis Test
|
||||
*/
|
||||
const test = require('ava');
|
||||
const debug = require('debug')('cc-switch:test:redis');
|
||||
const Redis = require('ioredis');
|
||||
const config = require('../config');
|
||||
const util = require('util');
|
||||
const moment = require('moment');
|
||||
|
||||
const redis = new Redis({
|
||||
host: config.REDIS_HOST,
|
||||
port: config.REDIS_PORT,
|
||||
db: 2,
|
||||
});
|
||||
|
||||
const EVENT_TYPE_CALLOUT = "callout";
|
||||
const EVENT_QUEUE_QUEUE_CC_TO_FS = "cc:to:freeswitch";
|
||||
const CALLOUT_DIALPLAN_STATUS = "callout:dialplan:status";
|
||||
const CALLOUT_DIALPLAN_TARGET = "freeswitch:%s:callout";
|
||||
const CALLOUT_CC_FROM_FS = "pbx:bxzq:events";
|
||||
|
||||
test.only("Redis Test # 外呼挂断", async(t) => {
|
||||
let now = moment();
|
||||
now.add(-3, "minutes");
|
||||
console.log("挂断时间:", now);
|
||||
let payload = {
|
||||
"uuid": "9a0cbc81-ccae-425e-8d3d-369b872a6481",
|
||||
"to": "13213213213",
|
||||
"from": "1003",
|
||||
"type": "callout",
|
||||
"channel": "bxzq",
|
||||
"dialplan": "4028827365b2acec0165b307afe405de",
|
||||
"createtime": now.valueOf(),
|
||||
"ops": "hangup",
|
||||
"record":"chatopera/376bf70a-9449-46c8-ad3e-03ac41953946.wav"
|
||||
}
|
||||
|
||||
redis.publish(CALLOUT_CC_FROM_FS, JSON.stringify(payload));
|
||||
t.pass();
|
||||
})
|
5
contact-center/.dockerignore
Normal file
5
contact-center/.dockerignore
Normal file
@ -0,0 +1,5 @@
|
||||
app/target
|
||||
!app/target/contact-center-*.war.original
|
||||
logs/
|
||||
tmp/
|
||||
data/
|
23
contact-center/.gitignore
vendored
Normal file
23
contact-center/.gitignore
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
*.swp
|
||||
*.swo
|
||||
*.sublime-*
|
||||
*.pyc
|
||||
jmeter.log
|
||||
__pycache__
|
||||
tmp/
|
||||
node_modules/
|
||||
sftp-config.json
|
||||
.DS_Store
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
*.idea
|
||||
~$*.xls*
|
||||
~$*.ppt*
|
||||
~$*.doc*
|
||||
admin/localrc
|
||||
app/target/
|
||||
app/.classpath
|
||||
app/.project
|
||||
app/.settings/
|
||||
logPath_IS_UNDEFINED/
|
51
contact-center/Dockerfile
Normal file
51
contact-center/Dockerfile
Normal file
@ -0,0 +1,51 @@
|
||||
FROM ubuntu:18.04
|
||||
MAINTAINER Hai Liang Wang <hain@chatopera.com>
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
ARG VCS_REF
|
||||
|
||||
LABEL org.label-schema.vcs-ref=$VCS_REF \
|
||||
org.label-schema.vcs-url="https://github.com/chatopera/cosin"
|
||||
|
||||
# COPY $PWD/assets/aliyun.sources.list /etc/apt/sources.list
|
||||
# install amazon jdk corretto
|
||||
COPY $PWD/assets/install-corretto-8.sh /opt
|
||||
RUN chmod +x /opt/install-corretto-8.sh && /opt/install-corretto-8.sh
|
||||
|
||||
# install maven
|
||||
COPY $PWD/assets/install-maven.sh /opt
|
||||
RUN chmod +x /opt/install-maven.sh && /opt/install-maven.sh
|
||||
|
||||
# configure timezone
|
||||
RUN apt-get update && \
|
||||
apt-get install --no-install-recommends -y tzdata && \
|
||||
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
|
||||
DEBIAN_FRONTEND=noninteractive dpkg-reconfigure --frontend noninteractive tzdata && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Set the locale
|
||||
ENV LANG C.UTF-8
|
||||
ENV LANGUAGE en_US:en
|
||||
ENV LC_ALL C.UTF-8
|
||||
|
||||
# set ENVs
|
||||
ENV JAVA_HOME=/usr/lib/jvm/java-1.8.0-amazon-corretto
|
||||
ENV MAVEN_HOME=/opt/maven
|
||||
ENV PATH=$PATH:$JAVA_HOME/bin:$MAVEN_HOME/bin
|
||||
|
||||
# create dirs
|
||||
RUN /bin/bash -c "mkdir -p /{data,logs}"
|
||||
|
||||
# build WAR
|
||||
COPY app /app
|
||||
COPY config /config
|
||||
WORKDIR /app
|
||||
RUN mvn clean package && \
|
||||
mkdir -p /opt/chatopera && \
|
||||
mv target/contact-center-3.9.0.war /opt/chatopera && \
|
||||
rm -rf /app && rm -rf /config && \
|
||||
rm -rf /root/.m2
|
||||
|
||||
WORKDIR /opt/chatopera
|
||||
EXPOSE 8030-8050
|
||||
CMD ["java", "-jar", "contact-center-3.9.0.war"]
|
22
contact-center/README.md
Normal file
22
contact-center/README.md
Normal file
@ -0,0 +1,22 @@
|
||||
# 春松客服:智能客服系统
|
||||
|
||||
前三代呼叫中心均是以电话为主要的服务渠道。在2000年,伴随着互联网以及移动通信的发展与普及,将电子邮件、互联网、手机短信等渠道接入呼叫中心,成为第四代呼叫中心的标志。第四代呼叫中心也称为多媒体呼叫中心或联络中心(Contact Center)。它相对传统呼叫中心来说接入渠道丰富,同时引入了多渠道接入与多渠道统一排队等概念。
|
||||
|
||||
## 文档
|
||||
|
||||
### ukefu
|
||||
原始项目基于[ukefu](https://gitee.com/beimigame/ukefu)。
|
||||
|
||||
```
|
||||
链接: https://pan.baidu.com/s/1wEPZeieZm4qaaFSWUb2Szg
|
||||
密码: tdm2
|
||||
```
|
||||
|
||||
### wiki
|
||||
|
||||
<a href="https://github.com/Samurais/chatopera.io/wiki/13.-ContactCenter%EF%BC%9A%E5%A4%9A%E5%AA%92%E4%BD%93%E5%91%BC%E5%8F%AB%E4%B8%AD%E5%BF%83">ContactCenter:多媒体呼叫中心</a>
|
||||
|
||||
## 媒体
|
||||
|
||||
<a href="http://36kr.com/p/5144999.html" target="_blank">谷歌发布Contact Center AI,智能客服真能不再“智障”了吗?</a>
|
||||
|
19
contact-center/admin/build.sh
Executable file
19
contact-center/admin/build.sh
Executable file
@ -0,0 +1,19 @@
|
||||
#! /bin/bash
|
||||
###########################################
|
||||
#
|
||||
###########################################
|
||||
|
||||
# constants
|
||||
baseDir=$(cd `dirname "$0"`;pwd)
|
||||
appHome=$baseDir/..
|
||||
imagename=chatopera/contact-center
|
||||
PACKAGE_VERSION=1.0.0
|
||||
# functions
|
||||
|
||||
# main
|
||||
[ -z "${BASH_SOURCE[0]}" -o "${BASH_SOURCE[0]}" = "$0" ] || return
|
||||
# build
|
||||
cd $appHome
|
||||
set -x
|
||||
docker build --build-arg VCS_REF=`git rev-parse --short HEAD` --force-rm=true --tag $imagename:$PACKAGE_VERSION .
|
||||
docker tag $imagename:$PACKAGE_VERSION $imagename:develop
|
13
contact-center/admin/compile.sh
Executable file
13
contact-center/admin/compile.sh
Executable file
@ -0,0 +1,13 @@
|
||||
#! /bin/bash
|
||||
###########################################
|
||||
#
|
||||
###########################################
|
||||
|
||||
# constants
|
||||
baseDir=$(cd `dirname "$0"`;pwd)
|
||||
# functions
|
||||
|
||||
# main
|
||||
[ -z "${BASH_SOURCE[0]}" -o "${BASH_SOURCE[0]}" = "$0" ] || return
|
||||
cd $baseDir/../app
|
||||
mvn clean compile
|
30
contact-center/admin/dev.sh
Executable file
30
contact-center/admin/dev.sh
Executable file
@ -0,0 +1,30 @@
|
||||
#! /bin/bash
|
||||
###########################################
|
||||
#
|
||||
###########################################
|
||||
|
||||
# constants
|
||||
baseDir=$(cd `dirname "$0"`;pwd)
|
||||
|
||||
if [ -f $baseDir/localrc ]; then
|
||||
echo "Load localrc for environment variables ..."
|
||||
set -x
|
||||
source $baseDir/localrc
|
||||
else
|
||||
echo $baseDir/localrc "not exist."
|
||||
echo "First, copy and modify the rc template."
|
||||
echo "cp " $baseDir/localrc.sample $baseDir/localrc
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
# functions
|
||||
function start(){
|
||||
cd $baseDir/../app
|
||||
mvn spring-boot:run
|
||||
}
|
||||
|
||||
|
||||
# main
|
||||
[ -z "${BASH_SOURCE[0]}" -o "${BASH_SOURCE[0]}" = "$0" ] || return
|
||||
start
|
13
contact-center/admin/gen-eclipse.sh
Executable file
13
contact-center/admin/gen-eclipse.sh
Executable file
@ -0,0 +1,13 @@
|
||||
#! /bin/bash
|
||||
###########################################
|
||||
#
|
||||
###########################################
|
||||
|
||||
# constants
|
||||
baseDir=$(cd `dirname "$0"`;pwd)
|
||||
# functions
|
||||
|
||||
# main
|
||||
[ -z "${BASH_SOURCE[0]}" -o "${BASH_SOURCE[0]}" = "$0" ] || return
|
||||
cd $baseDir/../app
|
||||
mvn eclipse:eclipse
|
13
contact-center/admin/gen-idea.sh
Executable file
13
contact-center/admin/gen-idea.sh
Executable file
@ -0,0 +1,13 @@
|
||||
#! /bin/bash
|
||||
###########################################
|
||||
#
|
||||
###########################################
|
||||
|
||||
# constants
|
||||
baseDir=$(cd `dirname "$0"`;pwd)
|
||||
# functions
|
||||
|
||||
# main
|
||||
[ -z "${BASH_SOURCE[0]}" -o "${BASH_SOURCE[0]}" = "$0" ] || return
|
||||
cd $baseDir/../app
|
||||
mvn idea:idea
|
19
contact-center/admin/localrc.sample
Normal file
19
contact-center/admin/localrc.sample
Normal file
@ -0,0 +1,19 @@
|
||||
export SERVER_PORT=8035
|
||||
export SERVER_LOG_PATH=$baseDir/../logs
|
||||
# Log Level: INFO, DEBUG, ERROR, STDOUT
|
||||
export SERVER_LOG_LEVEL=INFO
|
||||
export WEB_UPLOAD_PATH=$baseDir/../data
|
||||
export SPRING_FREEMARKER_CACHE=false
|
||||
export SPRING_DATA_ELASTICSEARCH_PROPERTIES_PATH_DATA=$baseDir/../data
|
||||
export UK_IM_SERVER_PORT=8036
|
||||
export UK_IM_SERVER_HOST=localhost
|
||||
export UK_IM_SERVER_THREADS=10
|
||||
export SPRING_DATASOURCE_TYPE=com.alibaba.druid.pool.DruidDataSource
|
||||
export SPRING_DATASOURCE_DRIVER_CLASS_NAME=com.mysql.jdbc.Driver
|
||||
export SPRING_DATASOURCE_URL=jdbc:mysql://localhost:8889/ukefu-test?useUnicode=true&characterEncoding=UTF-8
|
||||
export SPRING_DATASOURCE_USERNAME=root
|
||||
export SPRING_DATASOURCE_PASSWORD=root
|
||||
export MANAGEMENT_SECURITY_ENABLED=false
|
||||
export SPRING_REDIS_DATABASE=2
|
||||
export SPRING_REDIS_HOST=redis
|
||||
export SPRING_REDIS_PORT=6379
|
13
contact-center/admin/package.sh
Executable file
13
contact-center/admin/package.sh
Executable file
@ -0,0 +1,13 @@
|
||||
#! /bin/bash
|
||||
###########################################
|
||||
#
|
||||
###########################################
|
||||
|
||||
# constants
|
||||
baseDir=$(cd `dirname "$0"`;pwd)
|
||||
# functions
|
||||
|
||||
# main
|
||||
[ -z "${BASH_SOURCE[0]}" -o "${BASH_SOURCE[0]}" = "$0" ] || return
|
||||
cd $baseDir/../app
|
||||
mvn clean package
|
16
contact-center/admin/push.sh
Executable file
16
contact-center/admin/push.sh
Executable file
@ -0,0 +1,16 @@
|
||||
#! /bin/bash
|
||||
###########################################
|
||||
#
|
||||
###########################################
|
||||
|
||||
# constants
|
||||
baseDir=$(cd `dirname "$0"`;pwd)
|
||||
imagename=chatopera/contact-center
|
||||
PACKAGE_VERSION=1.0.0
|
||||
|
||||
# functions
|
||||
|
||||
# main
|
||||
[ -z "${BASH_SOURCE[0]}" -o "${BASH_SOURCE[0]}" = "$0" ] || return
|
||||
docker push $imagename:$PACKAGE_VERSION
|
||||
docker push $imagename:develop
|
38
contact-center/admin/run.sh
Executable file
38
contact-center/admin/run.sh
Executable file
@ -0,0 +1,38 @@
|
||||
#! /bin/bash
|
||||
###########################################
|
||||
#
|
||||
###########################################
|
||||
|
||||
# constants
|
||||
baseDir=$(cd `dirname "$0"`;pwd)
|
||||
# functions
|
||||
|
||||
# main
|
||||
[ -z "${BASH_SOURCE[0]}" -o "${BASH_SOURCE[0]}" = "$0" ] || return
|
||||
cd $baseDir/
|
||||
docker run -it --rm \
|
||||
-p 9035:8035 \
|
||||
-p 9036:8036 \
|
||||
-v $PWD/data:/data \
|
||||
-v $PWD/logs:/logs \
|
||||
-e "JAVA_OPTS=-Xmx12288m -Xms2048m -XX:PermSize=256m -XX:MaxPermSize=1024m -Djava.net.preferIPv4Stack=true" \
|
||||
-e SERVER_PORT=8035 \
|
||||
-e SERVER_LOG_PATH=/logs \
|
||||
-e SERVER_LOG_LEVEL=INFO \
|
||||
-e WEB_UPLOAD_PATH=/data \
|
||||
-e SPRING_FREEMARKER_CACHE=true \
|
||||
-e SPRING_DATA_ELASTICSEARCH_PROPERTIES_PATH_DATA=/data \
|
||||
-e SPRING_DATASOURCE_DRIVER_CLASS_NAME=com.mysql.jdbc.Driver \
|
||||
-e "SPRING_DATASOURCE_URL=jdbc:mysql://mysql:8037/contactcenter?useUnicode=true&characterEncoding=UTF-8" \
|
||||
-e SPRING_DATASOURCE_USERNAME=root \
|
||||
-e SPRING_DATASOURCE_PASSWORD=123456 \
|
||||
-e MANAGEMENT_SECURITY_ENABLED=false \
|
||||
-e SPRING_REDIS_DATABASE=2 \
|
||||
-e SPRING_REDIS_HOST=redis \
|
||||
-e SPRING_REDIS_PORT=8041 \
|
||||
-e CSKEFU_CALLOUT_WATCH_INTERVAL=60000 \
|
||||
-e SPRING_DATA_ELASTICSEARCH_CLUSTER_NAME=elasticsearch \
|
||||
-e SPRING_DATA_ELASTICSEARCH_CLUSTER_NODES=elasticsearch:8040 \
|
||||
-e SPRING_DATA_ELASTICSEARCH_LOCAL=false \
|
||||
-e SPRING_DATA_ELASTICSEARCH_REPOSITORIES_ENABLED=true \
|
||||
chatopera/contact-center:develop
|
419
contact-center/app/pom.xml
Normal file
419
contact-center/app/pom.xml
Normal file
@ -0,0 +1,419 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.chatopera.cc</groupId>
|
||||
<artifactId>contact-center</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<packaging>war</packaging>
|
||||
<name>contact-center</name>
|
||||
<description>Chatopera Contact Center,多媒体呼叫中心,下一代呼叫中心</description>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>1.5.6.RELEASE</version>
|
||||
<relativePath/>
|
||||
<!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<java.version>1.8</java.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-freemarker</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.freemarker</groupId>
|
||||
<artifactId>freemarker</artifactId>
|
||||
<version>2.3.25-incubating</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>5.1.21</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.java.dev.jna</groupId>
|
||||
<artifactId>jna</artifactId>
|
||||
<version>4.1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.activation</groupId>
|
||||
<artifactId>activation</artifactId>
|
||||
<version>1.1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.mail</groupId>
|
||||
<artifactId>javax.mail-api</artifactId>
|
||||
<version>1.5.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.mail</groupId>
|
||||
<artifactId>javax.mail</artifactId>
|
||||
<version>1.6.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-elasticsearch</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger2</artifactId>
|
||||
<version>2.2.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger-ui</artifactId>
|
||||
<version>2.2.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jasypt</groupId>
|
||||
<artifactId>jasypt</artifactId>
|
||||
<version>1.9.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org</groupId>
|
||||
<artifactId>jaudiotagger</artifactId>
|
||||
<version>2.0.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>lt.jave</groupId>
|
||||
<artifactId>jave</artifactId>
|
||||
<version>1.0.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<version>1.10</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-lang</groupId>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
<version>2.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-beanutils</groupId>
|
||||
<artifactId>commons-beanutils</artifactId>
|
||||
<version>1.8.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.corundumstudio.socketio</groupId>
|
||||
<artifactId>netty-socketio</artifactId>
|
||||
<version>1.7.12</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context-support</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.hazelcast</groupId>
|
||||
<artifactId>hazelcast-all</artifactId>
|
||||
<version>3.10.5</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.quartz-scheduler</groupId>
|
||||
<artifactId>quartz</artifactId>
|
||||
<version>2.3.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-xml</artifactId>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid</artifactId>
|
||||
<version>1.0.27</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.lionsoul</groupId>
|
||||
<artifactId>ip2region</artifactId>
|
||||
<version>1.7.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.lionsoul</groupId>
|
||||
<artifactId>jcseg-core</artifactId>
|
||||
<version>2.2.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lmax</groupId>
|
||||
<artifactId>disruptor</artifactId>
|
||||
<version>3.3.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi</artifactId>
|
||||
<version>3.15</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
<version>3.15</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.binarywang</groupId>
|
||||
<artifactId>weixin-java-mp</artifactId>
|
||||
<version>2.6.0</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.8.1</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
<version>1.10.2</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/commons-dbutils/commons-dbutils -->
|
||||
<dependency>
|
||||
<groupId>commons-dbutils</groupId>
|
||||
<artifactId>commons-dbutils</artifactId>
|
||||
<version>1.6</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/joda-time/joda-time -->
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
<artifactId>joda-time</artifactId>
|
||||
<version>2.9.9</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/de.odysseus.juel/juel-impl -->
|
||||
<dependency>
|
||||
<groupId>de.odysseus.juel</groupId>
|
||||
<artifactId>juel-impl</artifactId>
|
||||
<version>2.2.7</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>2.8.8</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
|
||||
<dependency>
|
||||
<groupId>cglib</groupId>
|
||||
<artifactId>cglib</artifactId>
|
||||
<version>3.2.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-dbcp</groupId>
|
||||
<artifactId>commons-dbcp</artifactId>
|
||||
<version>1.3</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.oracle</groupId>
|
||||
<artifactId>ojdbc6</artifactId>
|
||||
<version>12.1.0.1-atlassian-hosted</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.belerweb</groupId>
|
||||
<artifactId>pinyin4j</artifactId>
|
||||
<version>2.5.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-collections</groupId>
|
||||
<artifactId>commons-collections</artifactId>
|
||||
<version>3.2.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.olap4j</groupId>
|
||||
<artifactId>olap4j</artifactId>
|
||||
<version>1.2.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mondrian</groupId>
|
||||
<artifactId>mondrian</artifactId>
|
||||
<version>3.7.0</version>
|
||||
</dependency>
|
||||
<!-- Distribute Storage Service https://github.com/minio/minio-java -->
|
||||
<dependency>
|
||||
<groupId>io.minio</groupId>
|
||||
<artifactId>minio</artifactId>
|
||||
<version>5.0.0</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/com.googlecode.aviator/aviator -->
|
||||
<dependency>
|
||||
<groupId>com.googlecode.aviator</groupId>
|
||||
<artifactId>aviator</artifactId>
|
||||
<version>3.3.0</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/com.aliyun/aliyun-java-sdk-core -->
|
||||
<dependency>
|
||||
<groupId>com.aliyun</groupId>
|
||||
<artifactId>aliyun-java-sdk-core</artifactId>
|
||||
<version>3.3.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.aliyun</groupId>
|
||||
<artifactId>aliyun-java-sdk-dysmsapi</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.3.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpasyncclient</artifactId>
|
||||
<version>4.0.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpmime</artifactId>
|
||||
<version>4.3.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
<version>20140107</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mashape.unirest</groupId>
|
||||
<artifactId>unirest-java</artifactId>
|
||||
<version>1.4.9</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.chatopera.bot</groupId>
|
||||
<artifactId>sdk</artifactId>
|
||||
<version>1.0.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<fork>true</fork>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>springloaded</artifactId>
|
||||
<version>1.2.8.RELEASE</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
<encoding>UTF-8</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<configuration>
|
||||
<attachClasses>true</attachClasses>
|
||||
<warSourceExcludes>**/WEB-INF</warSourceExcludes>
|
||||
<packagingExcludes>**/WEB-INF,**/resources</packagingExcludes>
|
||||
<webResources>
|
||||
<resource>
|
||||
<directory>../config/sql/</directory>
|
||||
<includes>
|
||||
<include>cskefu-MySQL-slim.sql</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</webResources>
|
||||
</configuration>
|
||||
<version>2.1.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>8</source>
|
||||
<target>8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<defaultGoal>compile</defaultGoal>
|
||||
</build>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>chatopera</id>
|
||||
<name>Chatopera Inc.</name>
|
||||
<url>https://nexus.chatopera.com/repository/maven-public</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>hain</id>
|
||||
<name>Hai Liang Wang</name>
|
||||
<email>hailiang.hl.wang@gmail.com</email>
|
||||
<url>https://github.com/Samurais</url>
|
||||
<organization>Chatopera Inc.</organization>
|
||||
<organizationUrl>https://www.chatopera.com</organizationUrl>
|
||||
<roles>
|
||||
<role>architect</role>
|
||||
<role>developer</role>
|
||||
</roles>
|
||||
<timezone>Asia/Shanghai</timezone>
|
||||
</developer>
|
||||
</developers>
|
||||
</project>
|
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.aggregation;
|
||||
|
||||
import com.chatopera.cc.exception.CallOutRecordException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
|
||||
public class CallOutHangupAggsResult {
|
||||
private final static Logger logger = LoggerFactory.getLogger(CallOutHangupAggsResult.class);
|
||||
private String dialplan;
|
||||
private String datestr;
|
||||
private int total;
|
||||
private int fails;
|
||||
private int duration;
|
||||
|
||||
private CallOutHangupAggsResult() {
|
||||
|
||||
}
|
||||
|
||||
public CallOutHangupAggsResult(final String dialplan, // 呼叫计划
|
||||
final String datestr, // 目标日期
|
||||
final int total, // 总呼叫
|
||||
final int fails, // 失败通话
|
||||
final int duration) { // 总通话事件,秒
|
||||
this.dialplan = dialplan;
|
||||
this.datestr = datestr;
|
||||
this.total = total;
|
||||
this.fails = fails;
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
public static CallOutHangupAggsResult cast(Object[] x) throws CallOutRecordException {
|
||||
CallOutHangupAggsResult y = new CallOutHangupAggsResult();
|
||||
try {
|
||||
y.setDialplan((String) x[0]);
|
||||
y.setDatestr((String) x[1]);
|
||||
y.setTotal(((BigInteger) x[2]).intValue());
|
||||
y.setFails(((BigInteger) x[3]).intValue());
|
||||
y.setDuration(((BigDecimal) x[4]).intValue());
|
||||
} catch (Exception e) {
|
||||
logger.error("[callout agg] cast error", e);
|
||||
throw new CallOutRecordException("[Ljava.lang.Object; cannot be cast to " + CallOutHangupAggsResult.class.getSimpleName());
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
public String getDialplan() {
|
||||
return dialplan;
|
||||
}
|
||||
|
||||
public void setDialplan(String dialplan) {
|
||||
this.dialplan = dialplan;
|
||||
}
|
||||
|
||||
public String getDatestr() {
|
||||
return datestr;
|
||||
}
|
||||
|
||||
public void setDatestr(String datestr) {
|
||||
this.datestr = datestr;
|
||||
}
|
||||
|
||||
public int getTotal() {
|
||||
return total;
|
||||
}
|
||||
|
||||
public void setTotal(int total) {
|
||||
this.total = total;
|
||||
}
|
||||
|
||||
public int getFails() {
|
||||
return fails;
|
||||
}
|
||||
|
||||
public void setFails(int fails) {
|
||||
this.fails = fails;
|
||||
}
|
||||
|
||||
public int getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
public void setDuration(int duration) {
|
||||
this.duration = duration;
|
||||
}
|
||||
}
|
@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.aggregation;
|
||||
|
||||
import com.chatopera.cc.exception.CallOutRecordException;
|
||||
import com.google.gson.JsonObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
|
||||
public class CallOutHangupAuditResult {
|
||||
private final static Logger logger = LoggerFactory.getLogger(CallOutHangupAuditResult.class);
|
||||
private final static String DIRECTION_ALL = "呼出和呼入";
|
||||
private String agentId; // 坐席ID
|
||||
private String agentName; // 坐席名字
|
||||
private String direction; // 呼叫方向 ['呼入', '呼出']
|
||||
private int dialplan; // 自动呼叫个数
|
||||
private int total; // 总数
|
||||
private int seconds; // 总时长,单位:秒
|
||||
private int fails; // 失败数
|
||||
private int gt60; // 长于1分钟个数
|
||||
private int maxduration; // 最长通话时间,单位:秒
|
||||
private int avgduration; // 平均时长,单位:秒
|
||||
|
||||
private CallOutHangupAuditResult() {
|
||||
|
||||
}
|
||||
|
||||
public CallOutHangupAuditResult(String agentId,
|
||||
String direction,
|
||||
int dialplan,
|
||||
int total,
|
||||
int seconds,
|
||||
int fails,
|
||||
int gt60,
|
||||
int maxduration) {
|
||||
this.agentId = agentId;
|
||||
this.direction = direction;
|
||||
this.dialplan = dialplan;
|
||||
this.total = total;
|
||||
this.seconds = seconds;
|
||||
this.fails = fails;
|
||||
this.gt60 = gt60;
|
||||
this.maxduration = maxduration;
|
||||
|
||||
}
|
||||
|
||||
public static CallOutHangupAuditResult cast(Object[] x) throws CallOutRecordException {
|
||||
CallOutHangupAuditResult y = new CallOutHangupAuditResult();
|
||||
try {
|
||||
y.setAgentId((String) x[0]);
|
||||
y.setDirection((String) x[1]);
|
||||
y.setDialplan(((BigInteger) x[2]).intValue());
|
||||
y.setTotal(((BigInteger) x[3]).intValue());
|
||||
y.setSeconds(((BigDecimal) x[4]).intValue());
|
||||
y.setFails(((BigInteger) x[5]).intValue());
|
||||
y.setGt60(((BigInteger) x[6]).intValue());
|
||||
y.setMaxduration((int) x[7]);
|
||||
y.setAvgduration(((BigDecimal) x[8]).intValue());
|
||||
y.setAgentName((String) x[9]);
|
||||
} catch (Exception e) {
|
||||
logger.error("[callout audit] cast error ", e);
|
||||
throw new CallOutRecordException("[Ljava.lang.Object; cannot be cast to " + CallOutHangupAuditResult.class.getSimpleName());
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并同一个Agent的两个不同direction的数据
|
||||
*/
|
||||
public static CallOutHangupAuditResult mix(final CallOutHangupAuditResult x, final CallOutHangupAuditResult y) throws CallOutRecordException {
|
||||
if (x == null)
|
||||
return y;
|
||||
if (y == null)
|
||||
return x;
|
||||
if (x.getDirection() == y.getDirection())
|
||||
throw new CallOutRecordException("CallOutHangupAuditResult.mix 呼叫方向不能相同。");
|
||||
|
||||
if (!x.getAgentId().equals(y.getAgentId()))
|
||||
throw new CallOutRecordException("CallOutHangupAuditResult.mix 坐席ID必须相同。");
|
||||
|
||||
CallOutHangupAuditResult z = new CallOutHangupAuditResult();
|
||||
z.setDirection(DIRECTION_ALL);
|
||||
z.setAgentId(x.getAgentId());
|
||||
z.setAgentName(x.getAgentName());
|
||||
z.setMaxduration(x.getMaxduration() > y.getMaxduration() ? x.getMaxduration() : y.getMaxduration());
|
||||
z.setSeconds(x.getSeconds() + y.getSeconds());
|
||||
z.setGt60(x.getGt60() + y.getGt60());
|
||||
z.setTotal(x.getTotal() + y.getTotal());
|
||||
z.setFails(x.getFails() + y.getFails());
|
||||
z.setDialplan(x.getDialplan() + y.getDialplan());
|
||||
z.setAvgduration((int) ((x.getAvgduration() + y.getAvgduration()) / 2));
|
||||
return z;
|
||||
}
|
||||
|
||||
|
||||
public JsonObject toJson(boolean id, boolean name, boolean direction) {
|
||||
JsonObject j = new JsonObject();
|
||||
|
||||
if (direction)
|
||||
j.addProperty("direction", this.getDirection());
|
||||
|
||||
if (id)
|
||||
j.addProperty("agentId", this.agentId);
|
||||
|
||||
if (name)
|
||||
j.addProperty("agentName", this.agentName);
|
||||
|
||||
int succ = this.getTotal() - this.getFails();
|
||||
j.addProperty("total", this.getTotal());
|
||||
j.addProperty("answer", succ);
|
||||
j.addProperty("rate", MathHelper.float_percentage_formatter(succ, this.getTotal()));
|
||||
j.addProperty("dur", MathHelper.formatSeconds(this.getSeconds()));
|
||||
j.addProperty("avg", MathHelper.formatSeconds(this.avgduration));
|
||||
j.addProperty("max", MathHelper.formatSeconds(this.getMaxduration()));
|
||||
j.addProperty("gt60", this.getGt60());
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
public String getAgentId() {
|
||||
return agentId;
|
||||
}
|
||||
|
||||
public void setAgentId(String agentId) {
|
||||
this.agentId = agentId;
|
||||
}
|
||||
|
||||
public String getDirection() {
|
||||
return direction;
|
||||
}
|
||||
|
||||
public void setDirection(String direction) {
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
public int getDialplan() {
|
||||
return dialplan;
|
||||
}
|
||||
|
||||
public void setDialplan(int dialplan) {
|
||||
this.dialplan = dialplan;
|
||||
}
|
||||
|
||||
public int getTotal() {
|
||||
return total;
|
||||
}
|
||||
|
||||
public void setTotal(int total) {
|
||||
this.total = total;
|
||||
}
|
||||
|
||||
public int getSeconds() {
|
||||
return seconds;
|
||||
}
|
||||
|
||||
public void setSeconds(int seconds) {
|
||||
this.seconds = seconds;
|
||||
}
|
||||
|
||||
public int getFails() {
|
||||
return fails;
|
||||
}
|
||||
|
||||
public void setFails(int fails) {
|
||||
this.fails = fails;
|
||||
}
|
||||
|
||||
public int getGt60() {
|
||||
return gt60;
|
||||
}
|
||||
|
||||
public void setGt60(int gt60) {
|
||||
this.gt60 = gt60;
|
||||
}
|
||||
|
||||
public int getMaxduration() {
|
||||
return maxduration;
|
||||
}
|
||||
|
||||
public void setMaxduration(int maxduration) {
|
||||
this.maxduration = maxduration;
|
||||
}
|
||||
|
||||
public int getAvgduration() {
|
||||
return avgduration;
|
||||
}
|
||||
|
||||
public void setAvgduration(int avgduration) {
|
||||
this.avgduration = avgduration;
|
||||
}
|
||||
|
||||
public String getAgentName() {
|
||||
return agentName;
|
||||
}
|
||||
|
||||
public void setAgentName(String agentName) {
|
||||
this.agentName = agentName;
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.aggregation;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class MathHelper {
|
||||
|
||||
public static final String FLOAT_PERCENTAGE_FORMATTER = "%.2f%%";
|
||||
public static final String FLOAT_PERCENTAGE_INVALID = "NaN%";
|
||||
public static final String FLOAT_PERCENTAGE_ZERO = "0.0%";
|
||||
|
||||
/**
|
||||
* 计算两个int类型的数字的百分比字符串
|
||||
*
|
||||
* @param molecule
|
||||
* @param denominator
|
||||
* @return
|
||||
*/
|
||||
public static String float_percentage_formatter(final int molecule, final int denominator) {
|
||||
String r = String.format(FLOAT_PERCENTAGE_FORMATTER, 100 * ((float) molecule / denominator));
|
||||
if (FLOAT_PERCENTAGE_INVALID.equals(r))
|
||||
r = FLOAT_PERCENTAGE_ZERO;
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
public static String formatSecondsBetweenTwoDates(Date pre, Date d){
|
||||
if(d == null)
|
||||
d = new Date();
|
||||
return MathHelper.formatSeconds(((d.getTime() - pre.getTime()) / 1000));
|
||||
}
|
||||
|
||||
public static String formatSeconds(Long timeInLong){
|
||||
Long l = new Long(timeInLong);
|
||||
return MathHelper.formatSeconds(l.intValue());
|
||||
}
|
||||
|
||||
public static String formatSeconds(int timeInSeconds)
|
||||
{
|
||||
int hours = timeInSeconds / 3600;
|
||||
int secondsLeft = timeInSeconds - hours * 3600;
|
||||
int minutes = secondsLeft / 60;
|
||||
int seconds = secondsLeft - minutes * 60;
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
if (hours < 10)
|
||||
sb.append(0);
|
||||
sb.append(hours);
|
||||
sb.append(":");
|
||||
|
||||
if (minutes < 10)
|
||||
sb.append(0);
|
||||
sb.append(minutes);
|
||||
sb.append(":");
|
||||
|
||||
if (seconds < 10)
|
||||
sb.append(0);
|
||||
sb.append(seconds);
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统
|
||||
* Modifications copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.aggregation.filter;
|
||||
|
||||
import com.chatopera.cc.app.cache.CacheHelper;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import com.hazelcast.mapreduce.KeyPredicate;
|
||||
import com.chatopera.cc.app.basic.MainContext;
|
||||
import com.chatopera.cc.app.model.UKefuCallOutNames;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class AgentCallOutFilter implements KeyPredicate<String>{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1236581634096258855L;
|
||||
private String orgi ;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public AgentCallOutFilter(String orgi){
|
||||
this.orgi = orgi ;
|
||||
}
|
||||
public boolean evaluate(String key) {
|
||||
UKefuCallOutNames ukefuCallOutNames = (UKefuCallOutNames) CacheHelper.getCallOutCacheBean().getCacheObject(key, orgi);
|
||||
return ukefuCallOutNames !=null && !StringUtils.isBlank(orgi) && orgi.equals(ukefuCallOutNames.getOrgi()) && MainContext.CallOutType.AGENT.toString().equals(ukefuCallOutNames.getCalltype());
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统
|
||||
* Modifications copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.aggregation.filter;
|
||||
|
||||
import com.chatopera.cc.app.cache.CacheHelper;
|
||||
import com.chatopera.cc.app.model.AgentStatus;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import com.hazelcast.mapreduce.KeyPredicate;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class AgentStatusBusyOrgiFilter implements KeyPredicate<String>{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1236581634096258855L;
|
||||
private String orgi ;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public AgentStatusBusyOrgiFilter(String orgi){
|
||||
this.orgi = orgi ;
|
||||
}
|
||||
public boolean evaluate(String key) {
|
||||
AgentStatus agent = (AgentStatus) CacheHelper.getAgentStatusCacheBean().getCacheObject(key, orgi);
|
||||
return agent!=null && !StringUtils.isBlank(orgi) && orgi.equals(agent.getOrgi()) && agent.isBusy();
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统
|
||||
* Modifications copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.aggregation.filter;
|
||||
|
||||
import com.chatopera.cc.app.cache.CacheHelper;
|
||||
import com.chatopera.cc.app.model.AgentStatus;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import com.hazelcast.mapreduce.KeyPredicate;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class AgentStatusOrgiFilter implements KeyPredicate<String>{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1236581634096258855L;
|
||||
private String orgi ;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public AgentStatusOrgiFilter(String orgi){
|
||||
this.orgi = orgi ;
|
||||
}
|
||||
public boolean evaluate(String key) {
|
||||
AgentStatus agent = (AgentStatus) CacheHelper.getAgentStatusCacheBean().getCacheObject(key, orgi);
|
||||
return agent!=null && !StringUtils.isBlank(orgi) && orgi.equals(agent.getOrgi());
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统
|
||||
* Modifications copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.aggregation.filter;
|
||||
|
||||
import com.chatopera.cc.app.cache.CacheHelper;
|
||||
import com.chatopera.cc.app.model.AgentUser;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import com.hazelcast.mapreduce.KeyPredicate;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class AgentUserOrgiFilter implements KeyPredicate<String>{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1236581634096258855L;
|
||||
private String orgi ;
|
||||
private String status ;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public AgentUserOrgiFilter(String orgi , String status){
|
||||
this.orgi = orgi ;
|
||||
this.status = status ;
|
||||
}
|
||||
public boolean evaluate(String key) {
|
||||
AgentUser user = (AgentUser) CacheHelper
|
||||
.getAgentUserCacheBean().getCacheObject(key , orgi);
|
||||
return user!=null && user.getStatus()!=null && !StringUtils.isBlank(orgi) && orgi.equals(user.getOrgi()) && user.getStatus()!=null && user.getStatus().equals(status);
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统
|
||||
* Modifications copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.aggregation.filter;
|
||||
|
||||
import com.chatopera.cc.app.basic.MainContext;
|
||||
import com.chatopera.cc.app.cache.CacheHelper;
|
||||
import com.chatopera.cc.app.model.UKefuCallOutNames;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import com.hazelcast.mapreduce.KeyPredicate;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class AiCallOutFilter implements KeyPredicate<String>{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1236581634096258855L;
|
||||
private String orgi ;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public AiCallOutFilter(String orgi){
|
||||
this.orgi = orgi ;
|
||||
}
|
||||
public boolean evaluate(String key) {
|
||||
UKefuCallOutNames ukefuCallOutNames = (UKefuCallOutNames) CacheHelper.getCallOutCacheBean().getCacheObject(key, orgi);
|
||||
return ukefuCallOutNames !=null && !StringUtils.isBlank(orgi) && orgi.equals(ukefuCallOutNames.getOrgi()) && MainContext.CallOutType.AI.toString().equals(ukefuCallOutNames.getCalltype());
|
||||
}
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统
|
||||
* Modifications copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.app;
|
||||
|
||||
import com.chatopera.cc.app.basic.MainContext;
|
||||
import com.chatopera.cc.app.config.StartedEventListener;
|
||||
import com.chatopera.cc.util.Constants;
|
||||
import com.chatopera.cc.util.SystemEnvHelper;
|
||||
import com.chatopera.cc.util.mobile.MobileNumberUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.Banner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
|
||||
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
|
||||
import org.springframework.boot.web.servlet.ErrorPage;
|
||||
import org.springframework.boot.web.servlet.MultipartConfigFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
import javax.servlet.MultipartConfigElement;
|
||||
import java.io.IOException;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableJpaRepositories("com.chatopera.cc.app.persistence.repository")
|
||||
@EnableElasticsearchRepositories("com.chatopera.cc.app.persistence.es")
|
||||
@EnableAsync
|
||||
@EnableTransactionManagement
|
||||
public class Application {
|
||||
|
||||
@Value("${web.upload-path}")
|
||||
private String uploaddir;
|
||||
|
||||
@Value("${spring.servlet.multipart.max-file-size}")
|
||||
private String multipartMaxUpload;
|
||||
|
||||
@Value("${spring.servlet.multipart.max-request-size}")
|
||||
private String multipartMaxRequest;
|
||||
|
||||
/**
|
||||
* 记载模块
|
||||
*/
|
||||
// 外呼模块
|
||||
private final static boolean isCalloutModule = SystemEnvHelper.parseModuleFlag("CSKEFU_MODULE_CALLOUT");
|
||||
// CRM模块
|
||||
private final static boolean isContactsModule = SystemEnvHelper.parseModuleFlag("CSKEFU_MODULE_CONTACTS");
|
||||
// 聊天机器人模块
|
||||
private final static boolean isChatbotModule = SystemEnvHelper.parseModuleFlag("CSKEFU_MODULE_CHATBOT");
|
||||
|
||||
static {
|
||||
// 外呼模块
|
||||
if (isCalloutModule) {
|
||||
MainContext.model.put(Constants.CSKEFU_MODULE_CALLOUT, true);
|
||||
}
|
||||
// CRM模块
|
||||
if (isContactsModule) {
|
||||
MainContext.model.put(Constants.CSKEFU_MODULE_CONTACTS, true);
|
||||
}
|
||||
// 聊天机器人模块
|
||||
if (isChatbotModule) {
|
||||
MainContext.model.put(Constants.CSKEFU_MODULE_CHATBOT, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init local resources
|
||||
*/
|
||||
protected static void init() {
|
||||
try {
|
||||
System.out.println("init mobile number utils ...");
|
||||
MobileNumberUtils.init();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MultipartConfigElement multipartConfigElement() {
|
||||
MultipartConfigFactory factory = new MultipartConfigFactory();
|
||||
factory.setMaxFileSize(multipartMaxUpload); //KB,MB
|
||||
factory.setMaxRequestSize(multipartMaxRequest);
|
||||
factory.setLocation(uploaddir);
|
||||
return factory.createMultipartConfig();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EmbeddedServletContainerCustomizer containerCustomizer() {
|
||||
|
||||
return new EmbeddedServletContainerCustomizer() {
|
||||
@Override
|
||||
public void customize(ConfigurableEmbeddedServletContainer container) {
|
||||
ErrorPage error = new ErrorPage("/error.html");
|
||||
container.addErrorPages(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Application.init();
|
||||
SpringApplication app = new SpringApplication(Application.class);
|
||||
app.setBannerMode(Banner.Mode.CONSOLE);
|
||||
app.setAddCommandLineProperties(false);
|
||||
app.addListeners(new StartedEventListener());
|
||||
MainContext.setApplicationContext(app.run(args));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统
|
||||
* Modifications copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.app;
|
||||
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.web.support.SpringBootServletInitializer;
|
||||
|
||||
public class ServletInitializer extends SpringBootServletInitializer{
|
||||
|
||||
@Override
|
||||
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
|
||||
return application.sources(Application.class);
|
||||
}
|
||||
}
|
@ -0,0 +1,837 @@
|
||||
/*
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统
|
||||
* Modifications copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.app.algorithm;
|
||||
|
||||
import com.chatopera.cc.aggregation.filter.AgentStatusBusyOrgiFilter;
|
||||
import com.chatopera.cc.aggregation.filter.AgentStatusOrgiFilter;
|
||||
import com.chatopera.cc.aggregation.filter.AgentUserOrgiFilter;
|
||||
import com.chatopera.cc.app.basic.MainContext;
|
||||
import com.chatopera.cc.app.basic.MainUtils;
|
||||
import com.chatopera.cc.app.cache.CacheHelper;
|
||||
import com.chatopera.cc.app.im.client.NettyClients;
|
||||
import com.chatopera.cc.app.im.router.OutMessageRouter;
|
||||
import com.chatopera.cc.app.model.*;
|
||||
import com.chatopera.cc.app.persistence.repository.*;
|
||||
import com.chatopera.cc.util.WebIMReport;
|
||||
import com.corundumstudio.socketio.SocketIONamespace;
|
||||
import com.hazelcast.core.IMap;
|
||||
import com.hazelcast.mapreduce.aggregation.Aggregations;
|
||||
import com.hazelcast.mapreduce.aggregation.Supplier;
|
||||
import com.hazelcast.query.PagingPredicate;
|
||||
import com.hazelcast.query.SqlPredicate;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
/**
|
||||
* Automatic Call Distribution
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class AutomaticServiceDist {
|
||||
private final static Logger logger = LoggerFactory.getLogger(AutomaticServiceDist.class);
|
||||
|
||||
/**
|
||||
* 载入坐席 ACD策略配置
|
||||
*
|
||||
* @param orgi
|
||||
* @return
|
||||
*/
|
||||
public static SessionConfig initSessionConfig(String orgi) {
|
||||
SessionConfig sessionConfig = null;
|
||||
if (MainContext.getContext() != null && (sessionConfig = (SessionConfig) CacheHelper.getSystemCacheBean().getCacheObject(MainContext.SYSTEM_CACHE_SESSION_CONFIG + "_" + orgi, orgi)) == null) {
|
||||
SessionConfigRepository sessionConfigRes = MainContext.getContext().getBean(SessionConfigRepository.class);
|
||||
sessionConfig = sessionConfigRes.findByOrgi(orgi);
|
||||
if (sessionConfig == null) {
|
||||
sessionConfig = new SessionConfig();
|
||||
} else {
|
||||
CacheHelper.getSystemCacheBean().put(MainContext.SYSTEM_CACHE_SESSION_CONFIG + "_" + orgi, sessionConfig, orgi);
|
||||
}
|
||||
}
|
||||
return sessionConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* 载入坐席 ACD策略配置
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static List<SessionConfig> initSessionConfigList() {
|
||||
List<SessionConfig> sessionConfigList = null;
|
||||
if (MainContext.getContext() != null && (sessionConfigList = (List<SessionConfig>) CacheHelper.getSystemCacheBean().getCacheObject(MainContext.SYSTEM_CACHE_SESSION_CONFIG_LIST, MainContext.SYSTEM_ORGI)) == null) {
|
||||
SessionConfigRepository sessionConfigRes = MainContext.getContext().getBean(SessionConfigRepository.class);
|
||||
sessionConfigList = sessionConfigRes.findAll();
|
||||
if (sessionConfigList != null && sessionConfigList.size() > 0) {
|
||||
CacheHelper.getSystemCacheBean().put(MainContext.SYSTEM_CACHE_SESSION_CONFIG_LIST, sessionConfigList, MainContext.SYSTEM_ORGI);
|
||||
}
|
||||
}
|
||||
return sessionConfigList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得 当前服务状态
|
||||
*
|
||||
* @param orgi
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public static AgentReport getAgentReport(String orgi) {
|
||||
/**
|
||||
* 统计当前在线的坐席数量
|
||||
*/
|
||||
AgentReport report = new AgentReport();
|
||||
IMap agentStatusMap = (IMap<String, Object>) CacheHelper.getAgentStatusCacheBean().getCache();
|
||||
AgentStatusOrgiFilter filter = new AgentStatusOrgiFilter(orgi);
|
||||
Long agents = (Long) agentStatusMap.aggregate(Supplier.fromKeyPredicate(filter), Aggregations.count());
|
||||
report.setAgents(agents.intValue());
|
||||
|
||||
Long busyAgent = (Long) agentStatusMap.aggregate(Supplier.fromKeyPredicate(new AgentStatusBusyOrgiFilter(orgi)), Aggregations.count());
|
||||
report.setBusy(busyAgent.intValue());
|
||||
report.setOrgi(orgi);
|
||||
|
||||
/**
|
||||
* 统计当前服务中的用户数量
|
||||
*/
|
||||
IMap agentUserMap = (IMap<String, Object>) CacheHelper.getAgentUserCacheBean().getCache();
|
||||
Long users = (Long) agentUserMap.aggregate(Supplier.fromKeyPredicate(new AgentUserOrgiFilter(orgi, MainContext.AgentUserStatusEnum.INSERVICE.toString())), Aggregations.count());
|
||||
report.setUsers(users.intValue());
|
||||
|
||||
Long queneUsers = (Long) agentUserMap.aggregate(Supplier.fromKeyPredicate(new AgentUserOrgiFilter(orgi, MainContext.AgentUserStatusEnum.INQUENE.toString())), Aggregations.count());
|
||||
report.setInquene(queneUsers.intValue());
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
public static int getQueneIndex(String userid, String orgi, long ordertime) {
|
||||
|
||||
// IList<AgentUser> queneUserList = (IList<AgentUser>) CacheHelper.getQueneUserCacheBean().getCache() ;
|
||||
int queneUsers = 0;
|
||||
// for(AgentUser agentUser : queneUserList){
|
||||
// if(agentUser.getOrgi().equals(orgi) && agentUser.getUserid().equals(userid)){
|
||||
// queneUsers ++ ;
|
||||
// }
|
||||
// }
|
||||
|
||||
return queneUsers;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static int getQueneIndex(String agent, String orgi, String skill) {
|
||||
|
||||
int queneUsers = 0;
|
||||
|
||||
PagingPredicate<String, AgentUser> pagingPredicate = null;
|
||||
if (StringUtils.isNotBlank(skill)) {
|
||||
pagingPredicate = new PagingPredicate<String, AgentUser>(new SqlPredicate("status = 'inquene' AND skill = '" + skill + "' AND orgi = '" + orgi + "'"), 100);
|
||||
} else if (StringUtils.isNotBlank(agent)) {
|
||||
pagingPredicate = new PagingPredicate<String, AgentUser>(new SqlPredicate("status = 'inquene' AND agent = '" + agent + "' AND orgi = '" + orgi + "'"), 100);
|
||||
} else {
|
||||
pagingPredicate = new PagingPredicate<String, AgentUser>(new SqlPredicate("status = 'inquene' AND orgi = '" + orgi + "'"), 100);
|
||||
}
|
||||
queneUsers = ((IMap<String, AgentUser>) CacheHelper.getAgentUserCacheBean().getCache()).values(pagingPredicate).size();
|
||||
return queneUsers;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static int getAgentUsers(String agent, String orgi) {
|
||||
/**
|
||||
* agentno自动是 服务的坐席, agent 是请求的坐席
|
||||
*/
|
||||
PagingPredicate<String, AgentUser> pagingPredicate = new PagingPredicate<String, AgentUser>(new SqlPredicate("status = 'inservice' AND agentno = '" + agent + "' AND orgi = '" + orgi + "'"), 100);
|
||||
List<AgentUser> agentUserList = new ArrayList<AgentUser>();
|
||||
agentUserList.addAll(((IMap<String, AgentUser>) CacheHelper.getAgentUserCacheBean().getCache()).values(pagingPredicate));
|
||||
return agentUserList.size();
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static List<AgentStatus> getAgentStatus(String skill, String orgi) {
|
||||
PagingPredicate<String, AgentStatus> pagingPredicate = new PagingPredicate<String, AgentStatus>(new SqlPredicate("orgi = '" + orgi + "'"), 100);
|
||||
|
||||
if (StringUtils.isNotBlank(skill)) {
|
||||
pagingPredicate = new PagingPredicate<String, AgentStatus>(new SqlPredicate("skill = '" + skill + "' AND orgi = '" + orgi + "'"), 100);
|
||||
}
|
||||
List<AgentStatus> agentList = new ArrayList<AgentStatus>();
|
||||
agentList.addAll(((IMap<String, AgentStatus>) CacheHelper.getAgentStatusCacheBean().getCache()).values(pagingPredicate));
|
||||
return agentList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 为坐席批量分配用户
|
||||
*
|
||||
* @param agentStatus
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void allotAgent(String agentno, String orgi) {
|
||||
AgentStatus agentStatus = (AgentStatus) CacheHelper.getAgentStatusCacheBean().getCacheObject(agentno, orgi);
|
||||
List<AgentUser> agentStatusList = new ArrayList<AgentUser>();
|
||||
PagingPredicate<String, AgentUser> pagingPredicate = null;
|
||||
if (agentStatus != null && StringUtils.isNotBlank(agentStatus.getSkill())) {
|
||||
pagingPredicate = new PagingPredicate<String, AgentUser>(new SqlPredicate("status = 'inquene' AND ((agent = null AND skill = null) OR (skill = '" + agentStatus.getSkill() + "' AND agent = null) OR agent = '" + agentno + "') AND orgi = '" + orgi + "'"), 10);
|
||||
} else {
|
||||
pagingPredicate = new PagingPredicate<String, AgentUser>(new SqlPredicate("status = 'inquene' AND ((agent = null AND skill = null) OR agent = '" + agentno + "') AND orgi = '" + orgi + "'"), 10);
|
||||
}
|
||||
agentStatusList.addAll(((IMap<String, AgentUser>) CacheHelper.getAgentUserCacheBean().getCache()).values(pagingPredicate));
|
||||
for (AgentUser agentUser : agentStatusList) {
|
||||
SessionConfig sessionConfig = AutomaticServiceDist.initSessionConfig(orgi);
|
||||
long maxusers = sessionConfig != null ? sessionConfig.getMaxuser() : MainContext.AGENT_STATUS_MAX_USER;
|
||||
if (agentStatus != null && agentStatus.getUsers() < maxusers) { //坐席未达到最大咨询访客数量
|
||||
CacheHelper.getAgentUserCacheBean().delete(agentUser.getUserid(), orgi); //从队列移除,进入正在处理的队列, 避免使用 分布式锁
|
||||
try {
|
||||
AgentService agentService = processAgentService(agentStatus, agentUser, orgi);
|
||||
|
||||
MessageOutContent outMessage = new MessageOutContent();
|
||||
outMessage.setMessage(AutomaticServiceDist.getSuccessMessage(agentService, agentUser.getChannel(), orgi));
|
||||
outMessage.setMessageType(MainContext.MediaTypeEnum.TEXT.toString());
|
||||
outMessage.setCalltype(MainContext.CallTypeEnum.IN.toString());
|
||||
outMessage.setNickName(agentStatus.getUsername());
|
||||
outMessage.setCreatetime(MainUtils.dateFormate.format(new Date()));
|
||||
|
||||
if (StringUtils.isNotBlank(agentUser.getUserid())) {
|
||||
OutMessageRouter router = null;
|
||||
router = (OutMessageRouter) MainContext.getContext().getBean(agentUser.getChannel());
|
||||
if (router != null) {
|
||||
router.handler(agentUser.getUserid(), MainContext.MessageTypeEnum.MESSAGE.toString(), agentUser.getAppid(), outMessage);
|
||||
}
|
||||
}
|
||||
// TODO #111 为坐席分配访客
|
||||
NettyClients.getInstance().publishAgentEventMessage(agentService.getAgentno(), MainContext.MessageTypeEnum.NEW.toString(), agentUser);
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
publishMessage(orgi, "agent", "success", agentno);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为坐席批量分配用户
|
||||
*
|
||||
* @param agentStatus
|
||||
* @throws Exception
|
||||
*/
|
||||
public static void serviceFinish(AgentUser agentUser, String orgi) throws Exception {
|
||||
if (agentUser != null) {
|
||||
AgentStatus agentStatus = null;
|
||||
if (MainContext.AgentUserStatusEnum.INSERVICE.toString().equals(agentUser.getStatus()) && agentUser.getAgentno() != null) {
|
||||
agentStatus = (AgentStatus) CacheHelper.getAgentStatusCacheBean().getCacheObject(agentUser.getAgentno(), orgi);
|
||||
}
|
||||
CacheHelper.getAgentUserCacheBean().delete(agentUser.getUserid(), orgi);
|
||||
|
||||
AgentUserRepository agentUserRepository = MainContext.getContext().getBean(AgentUserRepository.class);
|
||||
|
||||
AgentUser agentUseDataBean = agentUserRepository.findByIdAndOrgi(agentUser.getId(), agentUser.getOrgi());
|
||||
SessionConfig sessionConfig = AutomaticServiceDist.initSessionConfig(orgi);
|
||||
if (agentUseDataBean != null) {
|
||||
agentUseDataBean.setStatus(MainContext.AgentUserStatusEnum.END.toString());
|
||||
if (agentUser.getServicetime() != null) {
|
||||
agentUseDataBean.setSessiontimes(System.currentTimeMillis() - agentUser.getServicetime().getTime());
|
||||
}
|
||||
|
||||
agentUserRepository.save(agentUseDataBean);
|
||||
|
||||
/**
|
||||
* 更新OnlineUser对象,变更为服务中,不可邀请 , WebIM渠道专用
|
||||
*/
|
||||
if (MainContext.ChannelTypeEnum.WEBIM.toString().equals(agentUser.getChannel())) {
|
||||
OnlineUserRepository onlineUserRes = MainContext.getContext().getBean(OnlineUserRepository.class);
|
||||
List<OnlineUser> onlineUserList = onlineUserRes.findByUseridAndOrgi(agentUser.getUserid(), agentUser.getOrgi());
|
||||
if (onlineUserList.size() > 0) {
|
||||
OnlineUser onlineUser = onlineUserList.get(0);
|
||||
onlineUser.setInvitestatus(MainContext.OnlineUserInviteStatus.DEFAULT.toString());
|
||||
onlineUserRes.save(onlineUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final boolean isPhone = MainContext.ChannelTypeEnum.PHONE.toString().equals(agentUser.getChannel());
|
||||
AgentServiceRepository agentServiceRes = MainContext.getContext().getBean(AgentServiceRepository.class);
|
||||
AgentService service = null;
|
||||
if (StringUtils.isNotBlank(agentUser.getAgentserviceid())) {
|
||||
service = agentServiceRes.findByIdAndOrgi(agentUser.getAgentserviceid(), agentUser.getOrgi());
|
||||
}
|
||||
if (service == null) {//当做留言处理
|
||||
service = processAgentService(agentStatus, agentUser, orgi, true);
|
||||
}
|
||||
if (service != null) {
|
||||
service.setStatus(MainContext.AgentUserStatusEnum.END.toString());
|
||||
service.setEndtime(new Date());
|
||||
if (service.getServicetime() != null) {
|
||||
service.setSessiontimes(System.currentTimeMillis() - service.getServicetime().getTime());
|
||||
}
|
||||
|
||||
AgentUserTaskRepository agentUserTaskRes = MainContext.getContext().getBean(AgentUserTaskRepository.class);
|
||||
List<AgentUserTask> agentUserTaskList = agentUserTaskRes.findByIdAndOrgi(agentUser.getId(), agentUser.getOrgi());
|
||||
if (agentUserTaskList.size() > 0) {
|
||||
AgentUserTask agentUserTask = agentUserTaskList.get(0);
|
||||
service.setAgentreplyinterval(agentUserTask.getAgentreplyinterval());
|
||||
service.setAgentreplytime(agentUserTask.getAgentreplytime());
|
||||
service.setAvgreplyinterval(agentUserTask.getAvgreplyinterval());
|
||||
service.setAvgreplytime(agentUserTask.getAvgreplytime());
|
||||
|
||||
service.setUserasks(agentUserTask.getUserasks());
|
||||
service.setAgentreplys(agentUserTask.getAgentreplys());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 启用了质检任务,开启质检
|
||||
*/
|
||||
if (sessionConfig.isQuality() && service.getUserasks() > 0) { //开启了质检,并且是有效对话
|
||||
service.setQualitystatus(MainContext.QualityStatus.NODIS.toString()); //未分配质检任务
|
||||
} else {
|
||||
service.setQualitystatus(MainContext.QualityStatus.NO.toString()); //未开启质检 或无效对话无需质检
|
||||
}
|
||||
agentServiceRes.save(service);
|
||||
}
|
||||
|
||||
if (isPhone) { // 语音渠道,强制发送
|
||||
NettyClients.getInstance().sendCalloutEventMessage(agentUser.getAgentno(), MainContext.MessageTypeEnum.END.toString(), agentUser);
|
||||
} else {
|
||||
if (agentStatus != null) // WebIM 查看用户状态
|
||||
// TODO #111 结束会话
|
||||
NettyClients.getInstance().publishAgentEventMessage(agentUser.getAgentno(), MainContext.MessageTypeEnum.END.toString(), agentUser);
|
||||
OutMessageRouter router = null;
|
||||
router = (OutMessageRouter) MainContext.getContext().getBean(agentUser.getChannel());
|
||||
if (router != null) {
|
||||
MessageOutContent outMessage = new MessageOutContent();
|
||||
outMessage.setMessage(AutomaticServiceDist.getServiceFinishMessage(agentUser.getChannel(), orgi));
|
||||
outMessage.setMessageType(MainContext.AgentUserStatusEnum.END.toString());
|
||||
outMessage.setCalltype(MainContext.CallTypeEnum.IN.toString());
|
||||
if (agentStatus != null) {
|
||||
outMessage.setNickName(agentStatus.getUsername());
|
||||
} else {
|
||||
outMessage.setNickName(agentUser.getUsername());
|
||||
}
|
||||
outMessage.setCreatetime(MainUtils.dateFormate.format(new Date()));
|
||||
outMessage.setAgentserviceid(agentUser.getAgentserviceid());
|
||||
|
||||
router.handler(agentUser.getUserid(), MainContext.MessageTypeEnum.STATUS.toString(), agentUser.getAppid(), outMessage);
|
||||
}
|
||||
}
|
||||
|
||||
if (agentStatus != null) {
|
||||
updateAgentStatus(agentStatus, agentUser, orgi, false);
|
||||
|
||||
long maxusers = sessionConfig != null ? sessionConfig.getMaxuser() : MainContext.AGENT_STATUS_MAX_USER;
|
||||
if (agentStatus.getUsers() < maxusers) {
|
||||
allotAgent(agentStatus.getAgentno(), orgi);
|
||||
}
|
||||
}
|
||||
publishMessage(orgi, "end", "success", agentUser != null ? agentUser.getId() : null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新坐席当前服务中的用户状态,需要分布式锁
|
||||
*
|
||||
* @param agentStatus
|
||||
* @param agentUser
|
||||
* @param orgi
|
||||
*/
|
||||
public synchronized static void updateAgentStatus(AgentStatus agentStatus, AgentUser agentUser, String orgi, boolean in) {
|
||||
int users = getAgentUsers(agentStatus.getAgentno(), orgi);
|
||||
Lock lock = CacheHelper.getAgentStatusCacheBean().getLock("LOCK", orgi);
|
||||
lock.lock();
|
||||
try {
|
||||
agentStatus.setUsers(users);
|
||||
agentStatus.setUpdatetime(new Date());
|
||||
CacheHelper.getAgentStatusCacheBean().put(agentStatus.getAgentno(), agentStatus, orgi);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public static void publishMessage(String orgi, String worktype, String workresult, String dataid) {
|
||||
/**
|
||||
* 坐席状态改变,通知监测服务
|
||||
*/
|
||||
AgentReport agentReport = AutomaticServiceDist.getAgentReport(orgi);
|
||||
AgentReportRepository agentReportRes = MainContext.getContext().getBean(AgentReportRepository.class);
|
||||
if (agentReportRes != null) {
|
||||
agentReport.setOrgi(orgi);
|
||||
agentReport.setWorktype(worktype);
|
||||
agentReport.setWorkresult(workresult);
|
||||
agentReport.setDataid(dataid);
|
||||
|
||||
agentReportRes.save(agentReport);
|
||||
}
|
||||
MainContext.getContext().getBean("agentNamespace", SocketIONamespace.class).getBroadcastOperations().sendEvent("status", agentReport);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param agent 坐席
|
||||
* @param skill 技能组
|
||||
* @param userid 用户ID
|
||||
* @param status 工作状态
|
||||
* @param worktype 类型 : 语音OR 文本
|
||||
* @param orgi
|
||||
* @param lasttime
|
||||
*/
|
||||
public static void recordAgentStatus(String agent, String username, String extno, String skill, boolean admin, String userid, String status, String current, String worktype, String orgi, Date lasttime) {
|
||||
WorkMonitorRepository workMonitorRes = MainContext.getContext().getBean(WorkMonitorRepository.class);
|
||||
WorkMonitor workMonitor = new WorkMonitor();
|
||||
if (StringUtils.isNotBlank(agent) && StringUtils.isNotBlank(status)) {
|
||||
workMonitor.setAgent(agent);
|
||||
workMonitor.setAgentno(agent);
|
||||
workMonitor.setStatus(status);
|
||||
workMonitor.setAdmin(admin);
|
||||
workMonitor.setUsername(username);
|
||||
workMonitor.setExtno(extno);
|
||||
workMonitor.setWorktype(worktype);
|
||||
if (lasttime != null) {
|
||||
workMonitor.setDuration((int) (System.currentTimeMillis() - lasttime.getTime()) / 1000);
|
||||
}
|
||||
if (status.equals(MainContext.AgentStatusEnum.BUSY.toString())) {
|
||||
workMonitor.setBusy(true);
|
||||
}
|
||||
if (status.equals(MainContext.AgentStatusEnum.READY.toString())) {
|
||||
int count = workMonitorRes.countByAgentAndDatestrAndStatusAndOrgi(agent, MainUtils.simpleDateFormat.format(new Date()), MainContext.AgentStatusEnum.READY.toString(), orgi);
|
||||
if (count == 0) {
|
||||
workMonitor.setFirsttime(true);
|
||||
}
|
||||
}
|
||||
if (current.equals(MainContext.AgentStatusEnum.NOTREADY.toString())) {
|
||||
List<WorkMonitor> workMonitorList = workMonitorRes.findByOrgiAndAgentAndDatestrAndFirsttime(orgi, agent, MainUtils.simpleDateFormat.format(new Date()), true);
|
||||
if (workMonitorList.size() > 0) {
|
||||
WorkMonitor firstWorkMonitor = workMonitorList.get(0);
|
||||
if (firstWorkMonitor.getFirsttimes() == 0) {
|
||||
firstWorkMonitor.setFirsttimes((int) (System.currentTimeMillis() - firstWorkMonitor.getCreatetime().getTime()));
|
||||
workMonitorRes.save(firstWorkMonitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
workMonitor.setCreatetime(new Date());
|
||||
workMonitor.setDatestr(MainUtils.simpleDateFormat.format(new Date()));
|
||||
|
||||
workMonitor.setName(agent);
|
||||
workMonitor.setOrgi(orgi);
|
||||
workMonitor.setSkill(skill);
|
||||
workMonitor.setUserid(userid);
|
||||
|
||||
workMonitorRes.save(workMonitor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 为用户分配坐席
|
||||
*
|
||||
* @param agentUser
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static AgentService allotAgent(AgentUser agentUser, String orgi) {
|
||||
/**
|
||||
* 查询条件,当前在线的 坐席,并且 未达到最大 服务人数的坐席
|
||||
*/
|
||||
|
||||
List<AgentStatus> agentStatusList = new ArrayList<AgentStatus>();
|
||||
PagingPredicate<String, AgentStatus> pagingPredicate = null;
|
||||
/**
|
||||
* 处理ACD 的 技能组请求和 坐席请求
|
||||
*/
|
||||
if (StringUtils.isNotBlank(agentUser.getAgent())) {
|
||||
pagingPredicate = new PagingPredicate<String, AgentStatus>(new SqlPredicate(" busy = false AND agentno = '" + agentUser.getAgent() + "' AND orgi = '" + orgi + "'"), 1);
|
||||
} else if (StringUtils.isNotBlank(agentUser.getSkill())) {
|
||||
pagingPredicate = new PagingPredicate<String, AgentStatus>(new SqlPredicate(" busy = false AND skill = '" + agentUser.getSkill() + "' AND orgi = '" + orgi + "'"), 1);
|
||||
} else {
|
||||
pagingPredicate = new PagingPredicate<String, AgentStatus>(new SqlPredicate(" busy = false AND orgi = '" + orgi + "'"), 1);
|
||||
}
|
||||
|
||||
agentStatusList.addAll(((IMap<String, AgentStatus>) CacheHelper.getAgentStatusCacheBean().getCache()).values(pagingPredicate));
|
||||
AgentStatus agentStatus = null;
|
||||
AgentService agentService = null; //放入缓存的对象
|
||||
if (agentStatusList.size() > 0) {
|
||||
agentStatus = agentStatusList.get(0);
|
||||
if (agentStatus.getUsers() >= initSessionConfig(orgi).getMaxuser()) {
|
||||
agentStatus = null;
|
||||
/**
|
||||
* 判断当前有多少人排队中 , 分三种情况:1、请求技能组的,2、请求坐席的,3,默认请求的
|
||||
*
|
||||
*/
|
||||
|
||||
}
|
||||
}
|
||||
try {
|
||||
agentService = processAgentService(agentStatus, agentUser, orgi);
|
||||
if (agentService.getStatus().equals(MainContext.AgentUserStatusEnum.INQUENE.toString())) {
|
||||
agentService.setQueneindex(getQueneIndex(agentUser.getAgent(), orgi, agentUser.getSkill()));
|
||||
}
|
||||
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
publishMessage(orgi, "user", agentService != null && agentService.getStatus().equals(MainContext.AgentUserStatusEnum.INSERVICE.toString()) ? "inservice" : "inquene", agentUser.getId());
|
||||
return agentService;
|
||||
}
|
||||
|
||||
/**
|
||||
* 邀请访客进入当前对话,如果当前操作的 坐席是已就绪状态,则直接加入到当前坐席的 对话列表中,如果未登录,则分配给其他坐席
|
||||
*
|
||||
* @param agentno
|
||||
* @param agentUser
|
||||
* @param orgi
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static AgentService allotAgentForInvite(String agentno, AgentUser agentUser, String orgi) throws Exception {
|
||||
AgentStatus agentStatus = (AgentStatus) CacheHelper.getAgentStatusCacheBean().getCacheObject(agentno, orgi);
|
||||
AgentService agentService = null;
|
||||
if (agentStatus != null) {
|
||||
agentService = processAgentService(agentStatus, agentUser, orgi);
|
||||
publishMessage(orgi, "invite", "success", agentno);
|
||||
// TODO #111 为坐席分配邀请的访客
|
||||
NettyClients.getInstance().publishAgentEventMessage(agentService.getAgentno(), MainContext.MessageTypeEnum.NEW.toString(), agentUser);
|
||||
} else {
|
||||
agentService = allotAgent(agentUser, orgi);
|
||||
}
|
||||
return agentService;
|
||||
}
|
||||
|
||||
/**
|
||||
* 为访客 分配坐席, ACD策略,此处 AgentStatus 是建议 的 坐席, 如果启用了 历史服务坐席 优先策略, 则会默认检查历史坐席是否空闲,如果空闲,则分配,如果不空闲,则 分配当前建议的坐席
|
||||
*
|
||||
* @param agentStatus
|
||||
* @param agentUser
|
||||
* @param orgi
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
private static AgentService processAgentService(AgentStatus agentStatus, AgentUser agentUser, String orgi) throws Exception {
|
||||
return processAgentService(agentStatus, agentUser, orgi, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为访客 分配坐席, ACD策略,此处 AgentStatus 是建议 的 坐席, 如果启用了 历史服务坐席 优先策略, 则会默认检查历史坐席是否空闲,如果空闲,则分配,如果不空闲,则 分配当前建议的坐席
|
||||
*
|
||||
* @param agentUser
|
||||
* @param orgi
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static AgentService processChatbotService(final String botName, final AgentUser agentUser, final String orgi) {
|
||||
AgentService agentService = new AgentService(); //放入缓存的对象
|
||||
AgentServiceRepository agentServiceRes = MainContext.getContext().getBean(AgentServiceRepository.class);
|
||||
Date now = new Date();
|
||||
if (StringUtils.isNotBlank(agentUser.getAgentserviceid())) {
|
||||
agentService = agentServiceRes.findByIdAndOrgi(agentUser.getAgentserviceid(), orgi);
|
||||
agentService.setEndtime(now);
|
||||
if (agentService.getServicetime() != null) {
|
||||
agentService.setSessiontimes(System.currentTimeMillis() - agentService.getServicetime().getTime());
|
||||
}
|
||||
agentService.setStatus(MainContext.AgentUserStatusEnum.END.toString());
|
||||
} else {
|
||||
agentService.setServicetime(now);
|
||||
agentService.setLogindate(now);
|
||||
agentService.setOrgi(orgi);
|
||||
agentService.setOwner(agentUser.getContextid());
|
||||
agentService.setSessionid(agentUser.getSessionid());
|
||||
agentService.setRegion(agentUser.getRegion());
|
||||
agentService.setUsername(agentUser.getUsername());
|
||||
agentService.setChannel(agentUser.getChannel());
|
||||
if (botName != null)
|
||||
agentService.setAgentusername(botName);
|
||||
|
||||
if (StringUtils.isNotBlank(agentUser.getContextid())) {
|
||||
agentService.setContextid(agentUser.getContextid());
|
||||
} else {
|
||||
agentService.setContextid(agentUser.getSessionid());
|
||||
}
|
||||
|
||||
agentService.setUserid(agentUser.getUserid());
|
||||
agentService.setAiid(agentUser.getAgentno());
|
||||
agentService.setAiservice(true);
|
||||
agentService.setStatus(MainContext.AgentUserStatusEnum.INSERVICE.toString());
|
||||
|
||||
agentService.setAppid(agentUser.getAppid());
|
||||
agentService.setLeavemsg(false);
|
||||
}
|
||||
|
||||
agentServiceRes.save(agentService);
|
||||
return agentService;
|
||||
}
|
||||
|
||||
/**
|
||||
* 为访客 分配坐席, ACD策略,此处 AgentStatus 是建议 的 坐席, 如果启用了 历史服务坐席 优先策略, 则会默认检查历史坐席是否空闲,如果空闲,则分配,如果不空闲,则 分配当前建议的坐席
|
||||
*
|
||||
* @param agentStatus
|
||||
* @param agentUser
|
||||
* @param orgi
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
private static AgentService processAgentService(AgentStatus agentStatus, AgentUser agentUser, String orgi, boolean finished) throws Exception {
|
||||
AgentService agentService = new AgentService(); //放入缓存的对象
|
||||
if (StringUtils.isNotBlank(agentUser.getAgentserviceid())) {
|
||||
agentService.setId(agentUser.getAgentserviceid());
|
||||
}
|
||||
agentService.setOrgi(orgi);
|
||||
|
||||
MainUtils.copyProperties(agentUser, agentService); //复制属性
|
||||
|
||||
agentService.setChannel(agentUser.getChannel());
|
||||
|
||||
agentService.setSessionid(agentUser.getSessionid());
|
||||
OnlineUserRepository onlineUserRes = MainContext.getContext().getBean(OnlineUserRepository.class);
|
||||
agentUser.setLogindate(new Date());
|
||||
List<OnlineUser> onlineUserList = onlineUserRes.findByUseridAndOrgi(agentUser.getUserid(), agentUser.getOrgi());
|
||||
OnlineUser onlineUser = null;
|
||||
if (onlineUserList.size() > 0) {
|
||||
onlineUser = onlineUserList.get(0);
|
||||
}
|
||||
|
||||
if (agentStatus != null) {
|
||||
SessionConfig sessionConfig = initSessionConfig(orgi);
|
||||
|
||||
agentService.setAgent(agentStatus.getAgentno());
|
||||
agentService.setSkill(agentUser.getSkill());
|
||||
|
||||
if (sessionConfig.isLastagent()) { //启用了历史坐席优先 , 查找 历史服务坐席
|
||||
List<WebIMReport> webIMaggList = MainUtils.getWebIMDataAgg(onlineUserRes.findByOrgiForDistinctAgent(orgi, agentUser.getUserid()));
|
||||
if (webIMaggList.size() > 0) {
|
||||
for (WebIMReport report : webIMaggList) {
|
||||
if (report.getData().equals(agentStatus.getAgentno())) {
|
||||
break;
|
||||
} else {
|
||||
AgentStatus hisAgentStatus = (AgentStatus) CacheHelper.getAgentStatusCacheBean().getCacheObject(report.getData(), orgi);
|
||||
if (hisAgentStatus != null && hisAgentStatus.getUsers() < hisAgentStatus.getMaxusers()) {
|
||||
agentStatus = hisAgentStatus; //变更为 历史服务坐席
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
agentUser.setStatus(MainContext.AgentUserStatusEnum.INSERVICE.toString());
|
||||
agentService.setStatus(MainContext.AgentUserStatusEnum.INSERVICE.toString());
|
||||
|
||||
agentService.setSessiontype(MainContext.AgentUserStatusEnum.INSERVICE.toString());
|
||||
|
||||
agentService.setAgentno(agentStatus.getUserid());
|
||||
agentService.setAgentusername(agentStatus.getUsername()); //agent
|
||||
} else {
|
||||
if (finished == true) {
|
||||
agentUser.setStatus(MainContext.AgentUserStatusEnum.END.toString());
|
||||
agentService.setStatus(MainContext.AgentUserStatusEnum.END.toString());
|
||||
agentService.setSessiontype(MainContext.AgentUserStatusEnum.END.toString());
|
||||
if (agentStatus == null) {
|
||||
agentService.setLeavemsg(true); //是留言
|
||||
agentService.setLeavemsgstatus(MainContext.LeaveMsgStatus.NOTPROCESS.toString()); //未处理的留言
|
||||
}
|
||||
} else {
|
||||
agentUser.setStatus(MainContext.AgentUserStatusEnum.INQUENE.toString());
|
||||
agentService.setStatus(MainContext.AgentUserStatusEnum.INQUENE.toString());
|
||||
|
||||
agentService.setSessiontype(MainContext.AgentUserStatusEnum.INQUENE.toString());
|
||||
}
|
||||
}
|
||||
if (finished || agentStatus != null) {
|
||||
// agentService.setId(null);
|
||||
|
||||
agentService.setAgentuserid(agentUser.getId());
|
||||
|
||||
agentService.setInitiator(MainContext.ChatInitiatorType.USER.toString());
|
||||
|
||||
long waittingtime = 0;
|
||||
if (agentUser.getWaittingtimestart() != null) {
|
||||
waittingtime = System.currentTimeMillis() - agentUser.getWaittingtimestart().getTime();
|
||||
} else if (agentUser.getCreatetime() != null) {
|
||||
waittingtime = System.currentTimeMillis() - agentUser.getCreatetime().getTime();
|
||||
}
|
||||
agentUser.setWaittingtime((int) waittingtime);
|
||||
|
||||
agentUser.setServicetime(new Date());
|
||||
|
||||
agentService.setOwner(agentUser.getOwner());
|
||||
|
||||
agentService.setTimes(0);
|
||||
agentUser.setAgentno(agentService.getAgentno());
|
||||
|
||||
AgentServiceRepository agentServiceRes = MainContext.getContext().getBean(AgentServiceRepository.class);
|
||||
|
||||
if (StringUtils.isNotBlank(agentUser.getName())) {
|
||||
agentService.setName(agentUser.getName());
|
||||
}
|
||||
if (StringUtils.isNotBlank(agentUser.getPhone())) {
|
||||
agentService.setPhone(agentUser.getPhone());
|
||||
}
|
||||
if (StringUtils.isNotBlank(agentUser.getEmail())) {
|
||||
agentService.setEmail(agentUser.getEmail());
|
||||
}
|
||||
if (StringUtils.isNotBlank(agentUser.getResion())) {
|
||||
agentService.setResion(agentUser.getResion());
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(agentUser.getSkill())) {
|
||||
agentService.setAgentskill(agentUser.getSkill());
|
||||
} else if (agentStatus != null) {
|
||||
agentService.setAgentskill(agentStatus.getSkill());
|
||||
}
|
||||
|
||||
agentService.setServicetime(new Date());
|
||||
if (agentUser.getCreatetime() != null) {
|
||||
agentService.setWaittingtime((int) (System.currentTimeMillis() - agentUser.getCreatetime().getTime()));
|
||||
agentUser.setWaittingtime(agentService.getWaittingtime());
|
||||
}
|
||||
if (onlineUser != null) {
|
||||
agentService.setOsname(onlineUser.getOpersystem());
|
||||
agentService.setBrowser(onlineUser.getBrowser());
|
||||
agentService.setDataid(onlineUser.getId()); //记录 onlineuser 的id
|
||||
}
|
||||
agentService.setLogindate(agentUser.getCreatetime());
|
||||
agentServiceRes.save(agentService);
|
||||
agentUser.setAgentserviceid(agentService.getId());
|
||||
agentUser.setLastgetmessage(new Date());
|
||||
agentUser.setLastmessage(new Date());
|
||||
}
|
||||
|
||||
agentService.setDataid(agentUser.getId());
|
||||
/**
|
||||
* 分配成功以后, 将用户 和坐席的对应关系放入到 缓存
|
||||
*/
|
||||
/**
|
||||
* 将 AgentUser 放入到 当前坐席的 服务队列
|
||||
*/
|
||||
AgentUserRepository agentUserRepository = MainContext.getContext().getBean(AgentUserRepository.class);
|
||||
|
||||
/**
|
||||
* 更新OnlineUser对象,变更为服务中,不可邀请
|
||||
*/
|
||||
|
||||
if (onlineUser != null) {
|
||||
onlineUser.setInvitestatus(MainContext.OnlineUserInviteStatus.INSERV.toString());
|
||||
onlineUserRes.save(onlineUser);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
agentUserRepository.save(agentUser);
|
||||
|
||||
CacheHelper.getAgentUserCacheBean().put(agentUser.getUserid(), agentUser, MainContext.SYSTEM_ORGI);
|
||||
|
||||
if (agentStatus != null) {
|
||||
updateAgentStatus(agentStatus, agentUser, orgi, true);
|
||||
}
|
||||
|
||||
return agentService;
|
||||
}
|
||||
|
||||
public static AgentUser deleteAgentUser(AgentUser agentUser, String orgi)
|
||||
throws Exception {
|
||||
if (agentUser != null) {
|
||||
if (!MainContext.AgentUserStatusEnum.END.toString().equals(
|
||||
agentUser.getStatus())) {
|
||||
serviceFinish(agentUser, orgi);
|
||||
}
|
||||
if (StringUtils.isNotBlank(agentUser.getId())) {
|
||||
AgentUserRepository agentUserRes = MainContext.getContext().getBean(AgentUserRepository.class);
|
||||
agentUser = agentUserRes.findByIdAndOrgi(agentUser.getId(), orgi);
|
||||
if (agentUser != null) {
|
||||
agentUserRes.delete(agentUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
return agentUser;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param agentStatus
|
||||
* @return
|
||||
*/
|
||||
public static String getSuccessMessage(AgentService agentService, String channel, String orgi) {
|
||||
String queneTip = "<span id='agentno'>" + agentService.getAgentusername() + "</span>";
|
||||
if (!MainContext.ChannelTypeEnum.WEBIM.toString().equals(channel)) {
|
||||
queneTip = agentService.getAgentusername();
|
||||
}
|
||||
SessionConfig sessionConfig = initSessionConfig(orgi);
|
||||
String successMsg = "坐席分配成功," + queneTip + "为您服务。";
|
||||
if (StringUtils.isNotBlank(sessionConfig.getSuccessmsg())) {
|
||||
successMsg = sessionConfig.getSuccessmsg().replaceAll("\\{agent\\}", queneTip);
|
||||
}
|
||||
return successMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param agentStatus
|
||||
* @return
|
||||
*/
|
||||
public static String getServiceFinishMessage(String channel, String orgi) {
|
||||
SessionConfig sessionConfig = initSessionConfig(orgi);
|
||||
String queneTip = "坐席已断开和您的对话";
|
||||
if (StringUtils.isNotBlank(sessionConfig.getFinessmsg())) {
|
||||
queneTip = sessionConfig.getFinessmsg();
|
||||
}
|
||||
return queneTip;
|
||||
}
|
||||
|
||||
public static String getNoAgentMessage(int queneIndex, String channel, String orgi) {
|
||||
if (queneIndex < 0) {
|
||||
queneIndex = 0;
|
||||
}
|
||||
String queneTip = "<span id='queneindex'>" + queneIndex + "</span>";
|
||||
if (!MainContext.ChannelTypeEnum.WEBIM.toString().equals(channel)) {
|
||||
queneTip = String.valueOf(queneIndex);
|
||||
}
|
||||
SessionConfig sessionConfig = initSessionConfig(orgi);
|
||||
String noAgentTipMsg = "坐席全忙,已进入等待队列,您也可以在其他时间再来咨询。";
|
||||
if (StringUtils.isNotBlank(sessionConfig.getNoagentmsg())) {
|
||||
noAgentTipMsg = sessionConfig.getNoagentmsg().replaceAll("\\{num\\}", queneTip);
|
||||
}
|
||||
return noAgentTipMsg;
|
||||
}
|
||||
|
||||
public static String getQueneMessage(int queneIndex, String channel, String orgi) {
|
||||
|
||||
String queneTip = "<span id='queneindex'>" + queneIndex + "</span>";
|
||||
if (!MainContext.ChannelTypeEnum.WEBIM.toString().equals(channel)) {
|
||||
queneTip = String.valueOf(queneIndex);
|
||||
}
|
||||
SessionConfig sessionConfig = initSessionConfig(orgi);
|
||||
String agentBusyTipMsg = "正在排队,请稍候,在您之前,还有 " + queneTip + " 位等待用户。";
|
||||
if (StringUtils.isNotBlank(sessionConfig.getAgentbusymsg())) {
|
||||
agentBusyTipMsg = sessionConfig.getAgentbusymsg().replaceAll("\\{num\\}", queneTip);
|
||||
}
|
||||
return agentBusyTipMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 坐席离线
|
||||
*
|
||||
* @param userid
|
||||
* @param status
|
||||
*/
|
||||
public static void deleteAgentStatus(String userid, String orgi, boolean isAdmin) {
|
||||
AgentStatusRepository agentStatusRes = MainContext.getContext().getBean(AgentStatusRepository.class);
|
||||
List<AgentStatus> agentStatusList = agentStatusRes.findByAgentnoAndOrgi(userid, orgi);
|
||||
for (AgentStatus agentStatus : agentStatusList) {
|
||||
AutomaticServiceDist.recordAgentStatus(agentStatus.getAgentno(), agentStatus.getUsername(), agentStatus.getAgentno(), agentStatus.getSkill(), isAdmin, agentStatus.getAgentno(), agentStatus.isBusy() ? MainContext.AgentStatusEnum.BUSY.toString() : MainContext.AgentStatusEnum.NOTREADY.toString(), MainContext.AgentStatusEnum.NOTREADY.toString(), MainContext.AgentWorkType.MEIDIACHAT.toString(), agentStatus.getOrgi(), agentStatus.getUpdatetime());
|
||||
agentStatusRes.delete(agentStatus);
|
||||
}
|
||||
CacheHelper.getAgentStatusCacheBean().delete(userid, orgi);
|
||||
AutomaticServiceDist.publishMessage(orgi, "agent", "leave", userid);
|
||||
}
|
||||
}
|
@ -0,0 +1,999 @@
|
||||
/*
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统
|
||||
* Modifications copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.app.basic;
|
||||
|
||||
import com.chatopera.cc.util.Constants;
|
||||
import com.chatopera.cc.util.DateConverter;
|
||||
import com.chatopera.cc.app.basic.resource.ActivityResource;
|
||||
import com.chatopera.cc.app.basic.resource.BatchResource;
|
||||
import com.chatopera.cc.app.model.Log;
|
||||
import org.apache.commons.beanutils.ConvertUtils;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
public class MainContext {
|
||||
|
||||
public static final String USER_SESSION_NAME = "user";
|
||||
public static final String GUEST_USER = "guest";
|
||||
public static final String IM_USER_SESSION_NAME = "im_user";
|
||||
public static final String UKEFU_SYSTEM_DIC = "com.dic.system.template";
|
||||
public static final String UKEFU_SYSTEM_AUTH_DIC = "com.dic.auth.resource";
|
||||
public static final String UKEFU_SYSTEM_AREA_DIC = "com.dic.address.area";
|
||||
public static final String UKEFU_SYSTEM_ADPOS_DIC = "com.dic.adv.type";
|
||||
public static final String UKEFU_SYSTEM_COMMENT_DIC = "com.dic.app.comment";
|
||||
public static final String UKEFU_SYSTEM_COMMENT_ITEM_DIC = "com.dic.app.comment.item";
|
||||
|
||||
public static final String UKEFU_SYSTEM_DIS_AI = "ownerai";
|
||||
public static final String UKEFU_SYSTEM_DIS_AGENT = "owneruser";
|
||||
public static final String UKEFU_SYSTEM_ASSUSER = "assuser";
|
||||
public static final String UKEFU_SYSTEM_DIS_ORGAN = "ownerdept";
|
||||
public static final String UKEFU_SYSTEM_DIS_TIME = "distime";
|
||||
|
||||
public static final String UKEFU_SYSTEM_COOKIES_FLAG = "uk_flagid";
|
||||
public static final String UKEFU_SYSTEM_NO_AI_CONFIG = "NOTEXIST";
|
||||
|
||||
public static final String UKEFU_SYSTEM_NO_DAT = "NOTEXIST";
|
||||
|
||||
public static final String SYSTEM_INDEX = "uckefu";
|
||||
public static final String UKEFU_SYSTEM_SECFIELD = "ukefu_sec_field";
|
||||
|
||||
|
||||
public static final String UKEFU_SYSTEM_CALLCENTER = "callcenter";
|
||||
public static final String UKEFU_SYSTEM_WORKORDEREMAIL = "workordermail";
|
||||
public static final String UKEFU_SYSTEM_SMSEMAIL = "callcenter";
|
||||
public static final String UKEFU_SYSTEM_AI_INPUT = "inputparam";
|
||||
public static final String UKEFU_SYSTEM_AI_OUTPUT = "outputparam";
|
||||
|
||||
public static final String UKEFU_SYSTEM_INFOACQ = "infoacq"; //数据采集模式
|
||||
public static final String GUEST_USER_ID_CODE = "R3GUESTUSEKEY";
|
||||
public static final String WORKORDERS_CLOSED_STATUS = "uckefu_workorders_closed";
|
||||
public static final String SERVICE_QUENE_NULL_STR = "service_quene_null";
|
||||
public static final String DEFAULT_TYPE = "default"; //默认分类代码
|
||||
public static final String START = "start"; //流程默认的开始节点
|
||||
public static final String CACHE_SKILL = "cache_skill_"; //技能组的缓存
|
||||
public static final String CACHE_AGENT = "cache_agent_"; //坐席列表的缓存
|
||||
|
||||
public static final String CUBE_TITLE_MEASURE = "指标";
|
||||
|
||||
public static final String UKEFU_SYSTEM_AREA = "uckefu_system_area";
|
||||
|
||||
public static final String UKEFU_SYSTEM_ADV = "uckefu_system_adv"; //系统广告位
|
||||
|
||||
public static final int MAX_IMAGE_WIDTH = 460;
|
||||
|
||||
private static boolean imServerRunning = false; //IM服务状态
|
||||
|
||||
public static final int AGENT_STATUS_MAX_USER = 10; //每个坐席 最大接待的 咨询数量
|
||||
|
||||
public static final String SYSTEM_CACHE_SESSION_CONFIG = "session_config";
|
||||
|
||||
public static final String SYSTEM_CACHE_SESSION_CONFIG_LIST = "session_config_list";
|
||||
|
||||
public static final String SYSTEM_CACHE_AI_CONFIG = "ai_config";
|
||||
|
||||
public static final String SYSTEM_CACHE_CALLOUT_CONFIG = "callout_config";
|
||||
|
||||
public static String SYSTEM_ORGI = "cskefu";
|
||||
|
||||
public static final String USER_CURRENT_ORGI_SESSION = "current_orgi";
|
||||
public static Map<String, Boolean> model = new HashMap<String, Boolean>();
|
||||
|
||||
public static Map<String, Class<?>> uKeFuResourceMap = new HashMap<String, Class<?>>();
|
||||
|
||||
private static int WebIMPort = 8081;
|
||||
|
||||
private static ApplicationContext applicationContext;
|
||||
|
||||
private static ElasticsearchTemplate templet;
|
||||
|
||||
public static BlockingQueue<Log> tempLogQueue = new LinkedBlockingQueue<Log>();
|
||||
|
||||
static {
|
||||
ConvertUtils.register(new DateConverter(), java.util.Date.class);
|
||||
model.put("report", true);
|
||||
|
||||
uKeFuResourceMap.put(TaskType.ACTIVE.toString(), ActivityResource.class);
|
||||
|
||||
uKeFuResourceMap.put(TaskType.BATCH.toString(), BatchResource.class);
|
||||
}
|
||||
|
||||
public enum AskSectionType {
|
||||
DEFAULT;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum ActivityExecType {
|
||||
DEFAULT, RECOVERY;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum AgentWorkType {
|
||||
MEIDIACHAT,
|
||||
CALLCENTER;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum SystemMessageType {
|
||||
EMAIL, SMS;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 名单分配状态:已分配|未分配
|
||||
*
|
||||
* @author iceworld
|
||||
*/
|
||||
public enum NamesDisStatusType {
|
||||
NOT, DISAGENT, DISORGAN, DISAI;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum ProcessType {
|
||||
WORKORDER;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum QuickTypeEnum {
|
||||
PUB,
|
||||
PRI;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum NamesProcessStatus {
|
||||
DIS,
|
||||
PREVIEW,
|
||||
CALLING,
|
||||
CALLED,
|
||||
CALLFAILD;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum FormFilterTypeEnum {
|
||||
BATCH,
|
||||
BUSINESS;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum NameStatusTypeEnum {
|
||||
CALLED, //已拨打
|
||||
NOTCALL //未拨打
|
||||
;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum NamesCalledEnum {
|
||||
SUCCESS,//拨打成功
|
||||
FAILD, //拨打失败
|
||||
NOANSWER,//无人接听
|
||||
EMPNO, //空号
|
||||
ARREARS,//欠费
|
||||
APPO, //预约拨打
|
||||
INVALID;//无效名单
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum TagTypeEnum {
|
||||
QUALITY;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum StatusTypeEnum {
|
||||
INBOUND,
|
||||
OUTBOUND;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public enum LogTypeEnum {
|
||||
REQUEST,
|
||||
CREATE,
|
||||
READ,
|
||||
UPDATE,
|
||||
DELETE,
|
||||
OTHER,
|
||||
INFO,
|
||||
WARN,
|
||||
ERROR;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum SalesNamesStatus {
|
||||
DIST, //已分配
|
||||
NOTDIST; //未分配
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public enum LeaveMsgStatus {
|
||||
PROCESSED, //已处理
|
||||
NOTPROCESS; //未处理
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public enum AdPosEnum {
|
||||
POINT,
|
||||
IMAGE,
|
||||
WELCOME,
|
||||
INVITE;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum QualityType {
|
||||
CHAT,
|
||||
VOICE;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum QualityStatus {
|
||||
NO, //未开启质检
|
||||
DIS, //已分配
|
||||
NODIS; //未分配
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum CallCenterCallTypeEnum {
|
||||
INSIDELINE("内线", 1),
|
||||
ORGCALLOUT("部门外呼", 2),
|
||||
ORGCALLIN("部门呼入", 3),
|
||||
INSIDEQUENE("内线排队", 4),
|
||||
INSIDETRANS("内线转接", 5), //已分配
|
||||
OUTSIDELINE("外线", 6),
|
||||
OUTSIDEQUENE("外线排队", 7),
|
||||
OUTSIDETRANS("外线转接", 8); //未分配
|
||||
|
||||
private final String name;
|
||||
private final int index;
|
||||
|
||||
private CallCenterCallTypeEnum(final String name, final int index) {
|
||||
this.name = name;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
public String toLetters() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
|
||||
// 服务处理类型
|
||||
public enum OptTypeEnum {
|
||||
CHATBOT("机器人客服", 1),
|
||||
HUMAN("人工客服", 2);
|
||||
|
||||
private final String name;
|
||||
private final int index;
|
||||
|
||||
private OptTypeEnum(String name, int index) {
|
||||
this.name = name;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
public String toLetters() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
|
||||
// 外呼计划状态
|
||||
public enum CallOutDialplanStatusEnum {
|
||||
RUNNING("执行中", 1),
|
||||
STOPPED("已停止", 2),
|
||||
STARTING("开始执行", 3),
|
||||
STOPPING("停止中", 4),
|
||||
INITIALIZATION("初始化", 5);
|
||||
|
||||
private final String name;
|
||||
private final int index;
|
||||
|
||||
private CallOutDialplanStatusEnum(String name, int index) {
|
||||
this.name = name;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
public String toLetters() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
|
||||
public enum CallWireEventType {
|
||||
DIALPLAN_CONN("自动外呼接通", 1),
|
||||
DIALPLAN_DISC("自动外呼挂断", 2),
|
||||
DIALPLAN_FAIL("自动外呼失败", 3),
|
||||
MANUDIAL_CONN("手动外呼接通", 4),
|
||||
MANUDIAL_DISC("手动外呼挂断", 5),
|
||||
MANUDIAL_FAIL("手动外呼失败", 6),
|
||||
CALLIN_CONN("呼入接通", 7),
|
||||
CALLIN_DIST("呼入挂断", 8),
|
||||
CALLIN_FAIL("呼入失败", 9);
|
||||
|
||||
private String name;
|
||||
private int index;
|
||||
|
||||
private CallWireEventType(final String name, final int index) {
|
||||
this.name = name;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
public String toLetters() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return this.index;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public enum CallServiceStatus {
|
||||
INQUENE("就绪", 1),
|
||||
RING("振铃", 2), //振铃
|
||||
INCALL("应答", 3), //应答
|
||||
BRIDGE("桥接", 4), //桥接
|
||||
HOLD("已挂起", 5), //已挂起
|
||||
HANGUP("已挂机", 6), //已挂机
|
||||
OFFLINE("离线", 7); //离线
|
||||
|
||||
private final String name;
|
||||
private final int index;
|
||||
|
||||
private CallServiceStatus(final String name, final int index) {
|
||||
this.name = name;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
|
||||
public String toLetters() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
|
||||
public enum CallChannelStatus {
|
||||
EARLY,
|
||||
DOWN;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum WxMpFileType {
|
||||
JPG,
|
||||
PNG;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum AgentInterType {
|
||||
SKILL,
|
||||
AGENT;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话发起方
|
||||
*
|
||||
* @author iceworld
|
||||
*/
|
||||
public enum ChatInitiatorType {
|
||||
AGENT,
|
||||
USER;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum ExtentionType {
|
||||
LINE,
|
||||
IVR,
|
||||
BUSINESS,
|
||||
SKILL,
|
||||
CONFERENCE,
|
||||
QUENE;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum DTMFTypeEnum {
|
||||
SATISF,
|
||||
PASSWORD, //密码验证
|
||||
IDCARD, //身份证号码
|
||||
CARDNO; //银行卡号
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public enum ChatbotItemType {
|
||||
USERINPUT,
|
||||
BOTREPLY;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum MultiUpdateType {
|
||||
SAVE,
|
||||
UPDATE,
|
||||
DELETE;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum ReportType {
|
||||
REPORT;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public enum TaskType {
|
||||
BATCH,
|
||||
ACTIVE;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum WorkOrdersEventType {
|
||||
ACCEPTUSER, //审批人变更
|
||||
OTHER; //其他变更
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum BpmType {
|
||||
WORKORDERS;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum AskOperatorType {
|
||||
VIEWS,
|
||||
COMMENTS,
|
||||
UPS;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum ModelType {
|
||||
USER,
|
||||
WORKORDERS,
|
||||
KBS,
|
||||
SUMMARY,
|
||||
CCSUMMARY, WEBIM, CALLOUT;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public enum AdapterType {
|
||||
TEXT,
|
||||
MEDIA,
|
||||
AGENT,
|
||||
XIAOE,
|
||||
INTER;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum MetadataTableType {
|
||||
UK_WORKORDERS;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum OnlineUserInviteStatus {
|
||||
DEFAULT,
|
||||
INVITE,
|
||||
REFUSE,
|
||||
INSERV,
|
||||
ACCEPT;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum CustomerTypeEnum {
|
||||
ENTERPRISE,
|
||||
PERSONAL;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum WeiXinEventTypeEnum {
|
||||
SUB,
|
||||
UNSUB;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum ChannelTypeEnum {
|
||||
WEIXIN,
|
||||
WEIBO,
|
||||
WEBIM,
|
||||
PHONE,
|
||||
EMAIL, AI;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum EventTypeEnum {
|
||||
SUB,
|
||||
UNSUB,
|
||||
MENU;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum AgentStatusEnum {
|
||||
READY("就绪", 1),
|
||||
NOTREADY("未就绪", 2),
|
||||
BUSY("置忙", 3),
|
||||
NOTBUSY("不忙", 4),
|
||||
IDLE("空闲", 5),
|
||||
OFFLINE("离线", 6),
|
||||
SERVICES("服务", 7);
|
||||
|
||||
private String name;
|
||||
private int index;
|
||||
|
||||
private AgentStatusEnum(final String name, final int index) {
|
||||
this.name = name;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
public String zh() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum WorkStatusEnum {
|
||||
IDLE,
|
||||
WAITTING,
|
||||
CALLOUT,
|
||||
PREVIEW,
|
||||
OUTBOUNDCALL;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum TaskStatusType {
|
||||
NORMAL("0"),
|
||||
READ("1"),
|
||||
QUEUE("5"),
|
||||
RUNNING("2"),
|
||||
END("3");
|
||||
|
||||
private String type;
|
||||
|
||||
TaskStatusType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public enum NameSpaceEnum {
|
||||
|
||||
IM("/im/user"),
|
||||
AGENT("/im/agent"),
|
||||
ENTIM("/im/ent"),
|
||||
CHATBOT("/im/chatbot"),
|
||||
CALLCENTER("/callcenter/exchange"),
|
||||
CALLOUT("/callout/exchange");
|
||||
|
||||
private String namespace;
|
||||
|
||||
public String getNamespace() {
|
||||
return namespace;
|
||||
}
|
||||
|
||||
public void setNamespace(String namespace) {
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
NameSpaceEnum(String namespace) {
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum MessageTypeEnum {
|
||||
NEW,
|
||||
MESSAGE,
|
||||
END,
|
||||
TRANS, STATUS, AGENTSTATUS, SERVICE, WRITING;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum CallCenterResultStatusEnum {
|
||||
OK;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum MediaTypeEnum {
|
||||
TEXT,
|
||||
EVENT,
|
||||
IMAGE,
|
||||
VIDIO,
|
||||
VOICE, LOCATION, FILE, COOPERATION, ACTION;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum CallTypeEnum {
|
||||
IN("呼入", 1),
|
||||
OUT("呼出", 2),
|
||||
SYSTEM("系统", 3),
|
||||
INVITE("邀请", 4);
|
||||
|
||||
private final String name;
|
||||
private final int index;
|
||||
|
||||
private CallTypeEnum(final String name, final int index) {
|
||||
this.name = name;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
public String toLetters() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public enum OnlineUserOperatorStatus {
|
||||
ONLINE,
|
||||
OFFLINE,
|
||||
REONLINE,
|
||||
CHAT,
|
||||
RECHAT,
|
||||
BYE,
|
||||
SEARCH,
|
||||
ACCESS;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum OnlineUserTypeStatus {
|
||||
USER,
|
||||
WEBIM,
|
||||
WEIXIN,
|
||||
APP,
|
||||
TELECOM,
|
||||
OTHER,
|
||||
WEIBO;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum AgentUserStatusEnum {
|
||||
INSERVICE,
|
||||
INQUENE, END;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setApplicationContext(ApplicationContext context) {
|
||||
applicationContext = context;
|
||||
}
|
||||
|
||||
public static ApplicationContext getContext() {
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
public static ElasticsearchTemplate getTemplet() {
|
||||
return templet;
|
||||
}
|
||||
|
||||
public static void setTemplet(ElasticsearchTemplate templet) {
|
||||
MainContext.templet = templet;
|
||||
}
|
||||
|
||||
public static int getWebIMPort() {
|
||||
return WebIMPort;
|
||||
}
|
||||
|
||||
public static void setWebIMPort(int webIMPort) {
|
||||
WebIMPort = webIMPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统级的加密密码 , 从CA获取
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String getSystemSecrityPassword() {
|
||||
return "UCKeFu";
|
||||
}
|
||||
|
||||
public static void setIMServerStatus(boolean running) {
|
||||
imServerRunning = running;
|
||||
}
|
||||
|
||||
public static boolean getIMServerStatus() {
|
||||
return imServerRunning;
|
||||
}
|
||||
|
||||
public enum FilterConType {
|
||||
DIMENSION,
|
||||
MEASURE;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum FilterCompType {
|
||||
NOT,
|
||||
EQUAL;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum FilterValuefilterType {
|
||||
COMPARE,
|
||||
RANGE;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum FilterConValueType {
|
||||
INPUT,
|
||||
AUTO,
|
||||
USERDEF;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum FilterModelType {
|
||||
TEXT,
|
||||
DATE,
|
||||
SIGSEL,
|
||||
SELECT;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum FilteFunType {
|
||||
FILTER,
|
||||
RANK;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum CallType {
|
||||
AI,
|
||||
AGENT;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public enum ChatbotType {
|
||||
SMARTAI,
|
||||
BUSINESSAI;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public enum ChatbotBussType {
|
||||
SALE,
|
||||
QUESURVEY;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public enum CallOutType {
|
||||
AGENT,
|
||||
AI;
|
||||
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource
|
||||
* @return
|
||||
*/
|
||||
public static Class<?> getResource(String resource) {
|
||||
return uKeFuResourceMap.get(resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否开启外呼模块
|
||||
* @return
|
||||
*/
|
||||
public static boolean isEnableCalloutModule() {
|
||||
return model.containsKey(Constants.CSKEFU_MODULE_CALLOUT) && (model.get(Constants.CSKEFU_MODULE_CALLOUT).equals(true));
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统
|
||||
* Modifications copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.app.basic;
|
||||
|
||||
public class Viewport {
|
||||
private String page ;
|
||||
private String templet;
|
||||
|
||||
public Viewport(String templet , String page){
|
||||
this.templet = templet ;
|
||||
this.page = page ;
|
||||
}
|
||||
|
||||
public Viewport(String page){
|
||||
this.page = page ;
|
||||
}
|
||||
|
||||
public String getPage() {
|
||||
return page;
|
||||
}
|
||||
public void setPage(String page) {
|
||||
this.page = page;
|
||||
}
|
||||
public String getTemplet() {
|
||||
return templet;
|
||||
}
|
||||
public void setTemplet(String templet) {
|
||||
this.templet = templet;
|
||||
}
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统
|
||||
* Modifications copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.app.basic.aop;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.chatopera.cc.app.basic.MainContext;
|
||||
import com.chatopera.cc.app.basic.MainUtils;
|
||||
import com.chatopera.cc.util.UKeFuList;
|
||||
import com.chatopera.cc.concurrent.multiupdate.MultiUpdateEvent;
|
||||
import com.chatopera.cc.app.persistence.hibernate.BaseService;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.chatopera.cc.app.model.ESBean;
|
||||
|
||||
@Aspect
|
||||
@Component
|
||||
public class SyncDatabaseExt {
|
||||
|
||||
@Autowired
|
||||
private BaseService<?> dbDataRes ;
|
||||
/**
|
||||
* 定义拦截规则:拦截org.springframework.data.elasticsearch.repository。
|
||||
*/
|
||||
@Pointcut("execution(* org.springframework.data.elasticsearch.repository.*.save(*))")
|
||||
public void syncSaveEsData(){}
|
||||
|
||||
/**
|
||||
* 定义拦截规则:拦截org.springframework.data.elasticsearch.repository。
|
||||
*/
|
||||
@Pointcut("execution(* org.springframework.data.elasticsearch.repository.*.delete(*))")
|
||||
public void syncDeleteEsData(){}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Around("syncSaveEsData()")
|
||||
public Object syncSaveEsData(ProceedingJoinPoint pjp) throws Throwable{
|
||||
Object[] args = pjp.getArgs() ;
|
||||
if(args.length == 1){
|
||||
Object data = args[0] ;
|
||||
if(data!=null){
|
||||
if(data instanceof UKeFuList){
|
||||
/**只有一个地方用到,从DB同步数据到ES**/
|
||||
}else if(data instanceof List){
|
||||
List<Object> dataList = (List<Object>)data ;
|
||||
for(Object dbData : dataList){
|
||||
MainUtils.multiupdate(new MultiUpdateEvent<Object>(dbData , dbDataRes, MainContext.MultiUpdateType.SAVE.toString()));
|
||||
}
|
||||
}else{
|
||||
MainUtils.multiupdate(new MultiUpdateEvent<Object>(data, dbDataRes, MainContext.MultiUpdateType.SAVE.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return pjp.proceed();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Around("syncDeleteEsData()")
|
||||
public Object syncDeleteEsData(ProceedingJoinPoint pjp) throws Throwable{
|
||||
Object[] args = pjp.getArgs() ;
|
||||
if(args.length == 1){
|
||||
Object data = args[0] ;
|
||||
if(data instanceof List){
|
||||
List<Object> dataList = (List<Object>)data ;
|
||||
for(Object dbData : dataList){
|
||||
MainUtils.multiupdate(new MultiUpdateEvent<Object>(dbData , dbDataRes, MainContext.MultiUpdateType.DELETE.toString()));
|
||||
}
|
||||
}else{
|
||||
if(data instanceof ESBean){
|
||||
MainUtils.multiupdate(new MultiUpdateEvent<Object>(data, dbDataRes, MainContext.MultiUpdateType.DELETE.toString()));
|
||||
}else{
|
||||
MainUtils.multiupdate(new MultiUpdateEvent<Object>(data, dbDataRes, MainContext.MultiUpdateType.DELETE.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return pjp.proceed();
|
||||
}
|
||||
}
|
@ -0,0 +1,392 @@
|
||||
/*
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统
|
||||
* Modifications copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.app.basic.resource;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import com.chatopera.cc.app.basic.MainContext;
|
||||
import com.chatopera.cc.app.model.JobDetail;
|
||||
import com.chatopera.cc.app.basic.MainUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
|
||||
import com.chatopera.cc.util.es.SearchTools;
|
||||
import com.chatopera.cc.util.es.UKDataBean;
|
||||
import com.chatopera.cc.app.persistence.impl.BatchDataProcess;
|
||||
import com.chatopera.cc.app.persistence.impl.ESDataExchangeImpl;
|
||||
import com.chatopera.cc.app.persistence.repository.CallAgentRepository;
|
||||
import com.chatopera.cc.app.persistence.repository.UKefuCallOutFilterRepository;
|
||||
import com.chatopera.cc.app.persistence.repository.UKefuCallOutTaskRepository;
|
||||
import com.chatopera.cc.app.persistence.repository.FormFilterItemRepository;
|
||||
import com.chatopera.cc.app.persistence.repository.FormFilterRepository;
|
||||
import com.chatopera.cc.app.persistence.repository.JobDetailRepository;
|
||||
import com.chatopera.cc.app.persistence.repository.MetadataRepository;
|
||||
import com.chatopera.cc.app.model.CallAgent;
|
||||
import com.chatopera.cc.app.model.UKefuCallOutFilter;
|
||||
import com.chatopera.cc.app.model.UKefuCallOutTask;
|
||||
import com.chatopera.cc.app.model.FormFilter;
|
||||
import com.chatopera.cc.app.model.FormFilterItem;
|
||||
import com.chatopera.cc.app.model.MetadataTable;
|
||||
|
||||
public class ActivityResource extends Resource{
|
||||
|
||||
private JobDetail jobDetail ;
|
||||
private FormFilterRepository formFilterRes ;
|
||||
private FormFilterItemRepository formFilterItemRes ;
|
||||
private PageImpl<UKDataBean> dataList ;
|
||||
private MetadataTable metadataTable ;
|
||||
private FormFilter formFilter = null ;
|
||||
private List<CallAgent> callAgentList ;
|
||||
|
||||
|
||||
private CallAgent current ;
|
||||
|
||||
private UKefuCallOutTask task ;
|
||||
private UKefuCallOutFilter filter ;
|
||||
|
||||
private UKefuCallOutTaskRepository callOutTaskRes ;
|
||||
|
||||
private UKefuCallOutFilterRepository callOutFilterRes ;
|
||||
|
||||
private JobDetailRepository batchRes;
|
||||
|
||||
private MetadataRepository metadataRes ;
|
||||
|
||||
private JobDetail batch ;
|
||||
|
||||
private AtomicInteger assignorganInt = new AtomicInteger() /***分配到坐席***/, assignInt = new AtomicInteger() /***分配到部门***/ , assignAiInt = new AtomicInteger() /***分配到AI***/ ,atomInt = new AtomicInteger() ;
|
||||
|
||||
private BatchDataProcess batchDataProcess ;
|
||||
|
||||
public ActivityResource(JobDetail jobDetail) {
|
||||
this.jobDetail = jobDetail ;
|
||||
this.formFilterRes = MainContext.getContext().getBean(FormFilterRepository.class) ;
|
||||
this.formFilterItemRes = MainContext.getContext().getBean(FormFilterItemRepository.class) ;
|
||||
this.callOutTaskRes = MainContext.getContext().getBean(UKefuCallOutTaskRepository.class);
|
||||
this.callOutFilterRes = MainContext.getContext().getBean(UKefuCallOutFilterRepository.class);
|
||||
this.batchRes = MainContext.getContext().getBean(JobDetailRepository.class);
|
||||
this.metadataRes = MainContext.getContext().getBean(MetadataRepository.class);
|
||||
this.batchDataProcess = new BatchDataProcess(null , MainContext.getContext().getBean(ESDataExchangeImpl.class)) ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void begin() throws Exception {
|
||||
if(!StringUtils.isBlank(jobDetail.getFilterid())) {
|
||||
formFilter = formFilterRes.findByIdAndOrgi(jobDetail.getFilterid(), this.jobDetail.getOrgi()) ;
|
||||
List<FormFilterItem> formFilterList = formFilterItemRes.findByOrgiAndFormfilterid(this.jobDetail.getOrgi(), jobDetail.getFilterid()) ;
|
||||
if(formFilter!=null && !StringUtils.isBlank(formFilter.getFiltertype())) {
|
||||
if(formFilter.getFiltertype().equals(MainContext.FormFilterTypeEnum.BATCH.toString())) {
|
||||
batch = batchRes.findByIdAndOrgi(formFilter.getBatid(), this.jobDetail.getOrgi()) ;
|
||||
if(batch!=null && !StringUtils.isBlank(batch.getActid())) {
|
||||
metadataTable = metadataRes.findByTablename(batch.getActid()) ;
|
||||
}
|
||||
}else { //业务表
|
||||
if(!StringUtils.isBlank(formFilter.getTableid())) {
|
||||
metadataTable = metadataRes.findById(formFilter.getTableid()) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(metadataTable!=null) {
|
||||
/**
|
||||
* 只加载 未分配的有效名单数据
|
||||
*/
|
||||
if(isRecovery()) {
|
||||
//回收数据 , 需要传入回收的目标 : 包括 批次ID,任务ID,筛选ID,活动ID
|
||||
dataList = SearchTools.recoversearch(this.jobDetail.getOrgi(), this.jobDetail.getExectype(), this.jobDetail.getExectarget() , metadataTable ,(int) Math.ceil(this.jobDetail.getStartindex()/50000), 50000) ;
|
||||
}else {
|
||||
dataList = SearchTools.dissearch(this.jobDetail.getOrgi(), formFilter, formFilterList , metadataTable ,(int) Math.ceil(this.jobDetail.getStartindex()/50000), 50000) ;
|
||||
}
|
||||
}
|
||||
this.callAgentList = MainContext.getContext().getBean(CallAgentRepository.class).findByActidAndOrgi(this.jobDetail.getId() , this.jobDetail.getOrgi()) ;
|
||||
/**
|
||||
* 生成 活动任务, 然后完成分配 , 同时还需要生成 筛选表单的筛选记录 , 在后台管理界面上可以看到
|
||||
*/
|
||||
if(this.callAgentList!=null && this.callAgentList.size() > 0) {
|
||||
this.current = this.callAgentList.remove(0) ;
|
||||
}
|
||||
|
||||
this.jobDetail.setExecnum(this.jobDetail.getExecnum() + 1);
|
||||
|
||||
if(this.isRecovery() && !StringUtils.isBlank(this.jobDetail.getExectype()) && (this.jobDetail.getExectype().equals("filterid") || this.jobDetail.getExectype().equals("filterskill") || this.jobDetail.getExectype().equals("taskskill") || this.jobDetail.getExectype().equals("taskid"))) {
|
||||
if(this.jobDetail.getExectype().equals("filterid") || this.jobDetail.getExectype().equals("filterskill")) {
|
||||
this.filter = this.callOutFilterRes.findByIdAndOrgi(this.jobDetail.getExectarget(), this.jobDetail.getOrgi()) ;
|
||||
}else if(this.jobDetail.getExectype().equals("taskid") || this.jobDetail.getExectype().equals("taskskill") ) {
|
||||
this.task = this.callOutTaskRes.findByIdAndOrgi(this.jobDetail.getExectarget(), this.jobDetail.getOrgi()) ;
|
||||
}
|
||||
}else {
|
||||
task = new UKefuCallOutTask() ;
|
||||
task.setName(this.jobDetail.getName() + "_" + MainUtils.dateFormate.format(new Date()));
|
||||
task.setBatid(formFilter.getBatid());
|
||||
|
||||
task.setOrgi(this.jobDetail.getOrgi());
|
||||
|
||||
if(this.isRecovery()) {
|
||||
task.setExectype(MainContext.ActivityExecType.RECOVERY.toString());
|
||||
}else {
|
||||
task.setExectype(MainContext.ActivityExecType.DEFAULT.toString());
|
||||
}
|
||||
|
||||
task.setFilterid(formFilter.getId());
|
||||
task.setActid(this.jobDetail.getId());
|
||||
|
||||
task.setExecnum(this.jobDetail.getExecnum());
|
||||
|
||||
task.setOrgan(this.jobDetail.getOrgan());
|
||||
|
||||
task.setCreatetime(new Date());
|
||||
if(this.dataList!=null) {
|
||||
task.setNamenum((int) this.dataList.getTotalElements());
|
||||
task.setNotassigned((int) this.dataList.getTotalElements());
|
||||
}
|
||||
|
||||
this.callOutTaskRes.save(task) ;
|
||||
|
||||
filter = new UKefuCallOutFilter() ;
|
||||
|
||||
formFilter.setExecnum(formFilter.getExecnum() + 1);
|
||||
|
||||
MainUtils.copyProperties(task, filter);
|
||||
filter.setName(this.formFilter.getName() + "_" + MainUtils.dateFormate.format(new Date()));
|
||||
filter.setExecnum(formFilter.getExecnum());
|
||||
this.callOutFilterRes.save(filter) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void end(boolean clear) throws Exception {
|
||||
if(this.atomInt.intValue() > 0) {
|
||||
this.batchDataProcess.end();
|
||||
}
|
||||
//doNothing
|
||||
/**
|
||||
* FormFilter的执行信息更新,执行次数
|
||||
*/
|
||||
if(formFilterRes!=null && this.formFilter != null) {
|
||||
this.formFilter.setFilternum(this.formFilter.getFilternum()+1);
|
||||
formFilterRes.save(this.formFilter) ;
|
||||
}
|
||||
/**
|
||||
* 批次的信息更新,批次剩余未分配的名单总数 , 已分配的名单总数
|
||||
*/
|
||||
if(this.batchRes!=null && this.batch != null) {
|
||||
if(this.isRecovery()) {
|
||||
batch.setAssigned(batch.getAssigned() - this.atomInt.intValue());
|
||||
}else {
|
||||
batch.setAssigned(batch.getAssigned() + this.atomInt.intValue());
|
||||
}
|
||||
batch.setNotassigned(batch.getNamenum() - batch.getAssigned());
|
||||
this.batchRes.save(batch) ;
|
||||
}
|
||||
if(this.task!=null) {
|
||||
if(this.isRecovery()) {
|
||||
if(!StringUtils.isBlank(this.jobDetail.getExecto())) {
|
||||
this.task.setReorgannum(this.atomInt.intValue());
|
||||
}else {
|
||||
this.task.setRenum(this.atomInt.intValue());
|
||||
}
|
||||
}else {
|
||||
this.task.setAssigned(this.assignInt.intValue());
|
||||
this.task.setAssignedorgan(this.assignorganInt.intValue());
|
||||
this.task.setAssignedai(this.assignAiInt.intValue());
|
||||
this.task.setNotassigned(this.task.getNamenum() - this.assignInt.intValue() - this.assignorganInt.intValue() - this.assignAiInt.intValue());
|
||||
}
|
||||
this.callOutTaskRes.save(this.task) ;
|
||||
}
|
||||
if(this.filter!=null) {
|
||||
if(this.isRecovery()) {
|
||||
if(!StringUtils.isBlank(this.jobDetail.getExecto())) {
|
||||
this.filter.setReorgannum(this.atomInt.intValue());
|
||||
}else {
|
||||
this.filter.setRenum(this.atomInt.intValue());
|
||||
}
|
||||
}else {
|
||||
this.filter.setAssigned(this.assignInt.intValue());
|
||||
this.filter.setAssignedorgan(this.assignorganInt.intValue());
|
||||
this.filter.setAssignedai(this.assignAiInt.intValue());
|
||||
this.filter.setNotassigned(this.task.getNamenum() - this.assignInt.intValue() - this.assignorganInt.intValue() - this.assignAiInt.intValue());
|
||||
}
|
||||
this.callOutFilterRes.save(this.filter) ;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新任务状态,记录生成的任务信息
|
||||
*/
|
||||
this.jobDetail.setExecmd(null);
|
||||
this.jobDetail.setExectype(null);
|
||||
this.jobDetail.setExectarget(null);
|
||||
this.jobDetail.setExecto(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JobDetail getJob() {
|
||||
return this.jobDetail;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(OutputTextFormat meta, JobDetail job) throws Exception {
|
||||
/**
|
||||
* 执行分配
|
||||
*/
|
||||
if(this.isRecovery()) {
|
||||
if(!StringUtils.isBlank(this.jobDetail.getExecto())) {
|
||||
meta.getDataBean().getValues().put(MainContext.UKEFU_SYSTEM_DIS_AGENT, null) ;
|
||||
meta.getDataBean().getValues().put(MainContext.UKEFU_SYSTEM_DIS_AI, null) ;
|
||||
// meta.getDataBean().getValues().put(MainContext.UKEFU_SYSTEM_DIS_ORGAN, this.jobDetail.getExecto()) ;
|
||||
meta.getDataBean().getValues().put(MainContext.UKEFU_SYSTEM_DIS_TIME, System.currentTimeMillis()) ;
|
||||
meta.getDataBean().getValues().put("status", MainContext.NamesDisStatusType.DISORGAN.toString()) ;
|
||||
}else {
|
||||
meta.getDataBean().getValues().put(MainContext.UKEFU_SYSTEM_DIS_AI, null) ;
|
||||
meta.getDataBean().getValues().put(MainContext.UKEFU_SYSTEM_DIS_AGENT, null) ;
|
||||
meta.getDataBean().getValues().put(MainContext.UKEFU_SYSTEM_DIS_ORGAN, null) ;
|
||||
meta.getDataBean().getValues().put(MainContext.UKEFU_SYSTEM_DIS_TIME, null) ;
|
||||
meta.getDataBean().getValues().put("status", MainContext.NamesDisStatusType.NOT.toString()) ;
|
||||
}
|
||||
}else {
|
||||
if(this.current!=null && meta!=null && meta.getDataBean()!=null) {
|
||||
this.current.getDisnames().incrementAndGet() ;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
meta.getDataBean().getValues().put(MainContext.UKEFU_SYSTEM_DIS_TIME, System.currentTimeMillis()) ;
|
||||
|
||||
meta.getDataBean().getValues().put("actid", this.jobDetail.getId()) ;
|
||||
meta.getDataBean().getValues().put("metaid", this.metadataTable.getTablename()) ;
|
||||
meta.getDataBean().getValues().put("batid", this.formFilter.getBatid()) ;
|
||||
|
||||
meta.getDataBean().getValues().put("taskid", this.task.getId()) ;
|
||||
meta.getDataBean().getValues().put("filterid", this.formFilter.getId()) ;
|
||||
meta.getDataBean().getValues().put("calloutfilid", this.filter.getId()) ;
|
||||
|
||||
meta.getDataBean().getValues().put("taskid", this.task.getId()) ;
|
||||
|
||||
|
||||
if(!StringUtils.isBlank(this.jobDetail.getUserid())){
|
||||
meta.getDataBean().getValues().put("assuser", this.jobDetail.getUserid()) ;
|
||||
}else{
|
||||
meta.getDataBean().getValues().put("assuser", this.jobDetail.getCreater()) ;
|
||||
}
|
||||
/**
|
||||
* 任务ID
|
||||
*/
|
||||
|
||||
if("agent".equals(this.current.getDistype())) {
|
||||
meta.getDataBean().getValues().put("status", MainContext.NamesDisStatusType.DISAGENT.toString()) ;
|
||||
meta.getDataBean().getValues().put(MainContext.UKEFU_SYSTEM_DIS_AGENT, this.current.getDistarget()) ;
|
||||
meta.getDataBean().getValues().put(MainContext.UKEFU_SYSTEM_DIS_ORGAN, this.current.getOrgan()) ;
|
||||
this.assignInt.incrementAndGet() ;
|
||||
}else if("skill".equals(this.current.getDistype())) {
|
||||
meta.getDataBean().getValues().put("status", MainContext.NamesDisStatusType.DISORGAN.toString()) ;
|
||||
meta.getDataBean().getValues().put(MainContext.UKEFU_SYSTEM_DIS_ORGAN, this.current.getDistarget()) ;
|
||||
this.assignorganInt.incrementAndGet() ;
|
||||
}else if("ai".equals(this.current.getDistype())) {
|
||||
meta.getDataBean().getValues().put("status", MainContext.NamesDisStatusType.DISAI.toString()) ;
|
||||
meta.getDataBean().getValues().put(MainContext.UKEFU_SYSTEM_DIS_AI, this.current.getDistarget()) ;
|
||||
meta.getDataBean().getValues().put(MainContext.UKEFU_SYSTEM_DIS_ORGAN, this.current.getOrgan()) ;
|
||||
this.assignAiInt.incrementAndGet() ;
|
||||
}
|
||||
}
|
||||
}
|
||||
meta.getDataBean().getValues().put("updatetime", System.currentTimeMillis()) ;
|
||||
|
||||
/**
|
||||
* 更新记录(是否同时保存分配信息,以便于查看分配历史?)
|
||||
*/
|
||||
batchDataProcess.process(meta.getDataBean());
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputTextFormat next() throws Exception {
|
||||
OutputTextFormat outputTextFormat = null;
|
||||
if(this.dataList!=null && this.current!=null) {
|
||||
synchronized (this.dataList) {
|
||||
if(atomInt.intValue() < this.dataList.getContent().size()) {
|
||||
if(this.isRecovery()) {
|
||||
UKDataBean dataBean = this.dataList.getContent().get(atomInt.intValue()) ;
|
||||
outputTextFormat = new OutputTextFormat(this.jobDetail);
|
||||
if(this.formFilter!=null) {
|
||||
outputTextFormat.setTitle(this.formFilter.getName());
|
||||
}
|
||||
outputTextFormat.setDataBean(dataBean);
|
||||
atomInt.incrementAndGet() ;
|
||||
}else if(this.dataList!=null) {
|
||||
if(this.current.getDisnames().intValue() >= this.current.getDisnum() ) {
|
||||
if(this.callAgentList.size() > 0) {
|
||||
this.current = this.callAgentList.remove(0) ;
|
||||
}else {
|
||||
this.current = null ;
|
||||
}
|
||||
}
|
||||
if(this.current != null) {
|
||||
UKDataBean dataBean = this.dataList.getContent().get(atomInt.intValue()) ;
|
||||
outputTextFormat = new OutputTextFormat(this.jobDetail);
|
||||
if(this.formFilter!=null) {
|
||||
outputTextFormat.setTitle(this.formFilter.getName());
|
||||
}
|
||||
outputTextFormat.setDataBean(dataBean);
|
||||
|
||||
atomInt.incrementAndGet() ;
|
||||
|
||||
/**
|
||||
* 修改为平均分配的方式 , 每个坐席或者部门评价分配
|
||||
*/
|
||||
this.callAgentList.add(this.current) ;
|
||||
if(this.callAgentList.size() > 0) {
|
||||
this.current = this.callAgentList.remove(0) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return outputTextFormat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputTextFormat getText(OutputTextFormat object) throws Exception {
|
||||
return object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rmResource() {
|
||||
/**
|
||||
* 啥也不做
|
||||
*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTask() throws Exception {
|
||||
/**
|
||||
* 更新任务状态,记录生成的任务信息
|
||||
*/
|
||||
this.jobDetail.setExecmd(null);
|
||||
this.jobDetail.setExectype(null);
|
||||
this.jobDetail.setExectarget(null);
|
||||
this.jobDetail.setExecto(null);
|
||||
}
|
||||
|
||||
private boolean isRecovery() {
|
||||
return !StringUtils.isBlank(this.jobDetail.getExecmd()) && this.jobDetail.getExecmd().equals("recovery") ;
|
||||
}
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统
|
||||
* Modifications copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.app.basic.resource;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.chatopera.cc.app.basic.MainContext;
|
||||
import com.chatopera.cc.app.persistence.impl.BatchDataProcess;
|
||||
import com.chatopera.cc.app.model.JobDetail;
|
||||
import com.chatopera.cc.app.basic.MainUtils;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.chatopera.cc.concurrent.dsdata.DSData;
|
||||
import com.chatopera.cc.concurrent.dsdata.DSDataEvent;
|
||||
import com.chatopera.cc.concurrent.dsdata.ExcelImportProecess;
|
||||
import com.chatopera.cc.app.persistence.impl.ESDataExchangeImpl;
|
||||
import com.chatopera.cc.app.persistence.repository.MetadataRepository;
|
||||
import com.chatopera.cc.app.persistence.repository.ReporterRepository;
|
||||
import com.chatopera.cc.app.model.MetadataTable;
|
||||
|
||||
public class BatchResource extends Resource{
|
||||
|
||||
private JobDetail jobDetail ;
|
||||
private MetadataTable metadataTable ;
|
||||
private ESDataExchangeImpl esDataExchange = null ;
|
||||
|
||||
private MetadataRepository metadataRes ;
|
||||
|
||||
private ReporterRepository reporterRes ;
|
||||
|
||||
public BatchResource(JobDetail jobDetail) {
|
||||
this.jobDetail = jobDetail ;
|
||||
this.metadataRes = MainContext.getContext().getBean(MetadataRepository.class);
|
||||
this.reporterRes = MainContext.getContext().getBean(ReporterRepository.class);
|
||||
this.esDataExchange = MainContext.getContext().getBean(ESDataExchangeImpl.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void begin() throws Exception {
|
||||
if(!StringUtils.isBlank(jobDetail.getActid())) {
|
||||
metadataTable = metadataRes.findByTablename(jobDetail.getActid()) ;
|
||||
}
|
||||
DSDataEvent event = new DSDataEvent();
|
||||
String path = MainContext.getContext().getEnvironment().getProperty("web.upload-path") ;
|
||||
File tempFile = null ;
|
||||
if(metadataTable!=null && !StringUtils.isBlank(this.jobDetail.getBatchtype()) && this.jobDetail.getBatchtype().equals("plan")) {
|
||||
if(!StringUtils.isBlank(this.jobDetail.getImptype())) {
|
||||
if(this.jobDetail.getImptype().equals("local")) {
|
||||
tempFile = new File(MainUtils.getTemplet(this.jobDetail.getImpurl(), new HashMap<String,Object>()));
|
||||
}else if(this.jobDetail.getImptype().equals("remote")){
|
||||
FileUtils.copyURLToFile(new URL(MainUtils.getTemplet(this.jobDetail.getImpurl(), new HashMap<String,Object>())), tempFile = File.createTempFile("UKeFu-CallOut-Temp", ".xls"));
|
||||
}
|
||||
}
|
||||
if(tempFile.exists()) {
|
||||
String fileName = "callout/batch/"+ MainUtils.getUUID() + tempFile.getName().substring(tempFile.getName().lastIndexOf(".")) ;
|
||||
File excelFile = new File(path , fileName) ;
|
||||
if(!excelFile.getParentFile().exists()){
|
||||
excelFile.getParentFile().mkdirs() ;
|
||||
}
|
||||
|
||||
event.setTablename(metadataTable.getTablename());
|
||||
event.setDSData(new DSData(null ,excelFile , tempFile.getName(), null));
|
||||
event.setOrgi(this.jobDetail.getOrgi());
|
||||
event.getValues().put("creater", this.jobDetail.getCreater()) ;
|
||||
|
||||
FileUtils.copyFile(tempFile, new File(path , fileName));
|
||||
|
||||
event.getDSData().setTask(metadataTable);
|
||||
event.getDSData().setProcess(new BatchDataProcess(metadataTable, esDataExchange));
|
||||
event.setOrgi(this.jobDetail.getOrgi());
|
||||
event.setBatid(this.jobDetail.getId());
|
||||
event.getDSData().setJobDetail(this.jobDetail);
|
||||
|
||||
event.getDSData().getReport().setOrgi(this.jobDetail.getOrgi());
|
||||
event.getDSData().getReport().setDataid(this.jobDetail.getId());
|
||||
event.getDSData().getReport().setTitle(this.jobDetail.getName() + "_" + MainUtils.dateFormate.format(new Date()));
|
||||
}else {
|
||||
event.getDSData().getReport().setError(true);
|
||||
if(tempFile!=null) {
|
||||
event.getDSData().getReport().setErrormsg(tempFile.getAbsolutePath() + " Not Exist!");
|
||||
}
|
||||
}
|
||||
reporterRes.save(event.getDSData().getReport()) ;
|
||||
new ExcelImportProecess(event).process() ; //启动导入任务
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void end(boolean clear) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public JobDetail getJob() {
|
||||
return this.jobDetail;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(OutputTextFormat meta, JobDetail job) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputTextFormat next() throws Exception {
|
||||
return null ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputTextFormat getText(OutputTextFormat object) throws Exception {
|
||||
return object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rmResource() {
|
||||
/**
|
||||
* 啥也不做
|
||||
*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTask() throws Exception {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统
|
||||
* Modifications copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.app.basic.resource;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.chatopera.cc.util.es.UKDataBean;
|
||||
import com.chatopera.cc.app.model.JobDetail;
|
||||
|
||||
public class OutputTextFormat {
|
||||
private String id ;
|
||||
private String title ;
|
||||
private String parent ;
|
||||
|
||||
private Map<String , Object> data = new HashMap<String , Object>();
|
||||
private JobDetail job ;
|
||||
private UKDataBean dataBean ;
|
||||
|
||||
public OutputTextFormat(JobDetail job){
|
||||
this.job = job ;
|
||||
}
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
public Map<String, Object> getData() {
|
||||
return data;
|
||||
}
|
||||
public void setData(Map<String, Object> data) {
|
||||
this.data = data;
|
||||
}
|
||||
public JobDetail getJob() {
|
||||
return job;
|
||||
}
|
||||
public void setJob(JobDetail job) {
|
||||
this.job = job;
|
||||
}
|
||||
public String getParent() {
|
||||
return parent;
|
||||
}
|
||||
public void setParent(String parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
public UKDataBean getDataBean() {
|
||||
return dataBean;
|
||||
}
|
||||
public void setDataBean(UKDataBean dataBean) {
|
||||
this.dataBean = dataBean;
|
||||
}
|
||||
}
|
@ -0,0 +1,113 @@
|
||||
/**
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* webapps/LICENSE-Rivulet
|
||||
*
|
||||
* 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.chatopera.cc.app.basic.resource;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.chatopera.cc.app.basic.MainContext;
|
||||
import com.chatopera.cc.app.model.JobDetail;
|
||||
|
||||
/**
|
||||
* @author jaddy0302 Rivulet Resource.java 2010-3-6
|
||||
*
|
||||
*/
|
||||
public abstract class Resource {
|
||||
|
||||
public static Logger log = Logger.getLogger(Resource.class.getName()) ;
|
||||
|
||||
public abstract void begin() throws Exception;
|
||||
|
||||
|
||||
public abstract void end(boolean clear) throws Exception;
|
||||
/**
|
||||
* Re connection
|
||||
*/
|
||||
public abstract JobDetail getJob();
|
||||
|
||||
/**
|
||||
* Re connection
|
||||
*/
|
||||
public abstract void process(OutputTextFormat meta , JobDetail job)throws Exception;
|
||||
|
||||
/**
|
||||
* synchronized
|
||||
* Single-mode single-threaded access to records under a record
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public abstract OutputTextFormat next() throws Exception;
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public abstract boolean isAvailable() ;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public abstract OutputTextFormat getText(OutputTextFormat object) throws Exception;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public abstract void rmResource() ;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public abstract void updateTask()throws Exception ;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param job
|
||||
* @return
|
||||
* @throws IllegalAccessException
|
||||
* @throws InstantiationException
|
||||
* @throws NoSuchMethodException
|
||||
* @throws SecurityException
|
||||
* @throws InvocationTargetException
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
public static Resource getResource(JobDetail job)
|
||||
throws Exception{
|
||||
return job != null
|
||||
&& MainContext.getResource(job.getTasktype()) != null ? (Resource) MainContext
|
||||
.getResource(job.getTasktype()).getConstructor(
|
||||
new Class[] { JobDetail.class }).newInstance(
|
||||
new Object[] { job })
|
||||
: null;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter
|
||||
* @param file
|
||||
* @param netFile
|
||||
* @return
|
||||
*/
|
||||
public boolean val(String inputFile , String acceptDocType){
|
||||
String file = inputFile!=null ? inputFile.toLowerCase() :null ;
|
||||
return file!=null && acceptDocType!=null && ((acceptDocType.indexOf(file.substring(file.lastIndexOf(".")+1))>=0||acceptDocType.indexOf("all")>=0)) ;
|
||||
}
|
||||
|
||||
}
|
79
contact-center/app/src/main/java/com/chatopera/cc/app/cache/CacheBean.java
vendored
Normal file
79
contact-center/app/src/main/java/com/chatopera/cc/app/cache/CacheBean.java
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统
|
||||
* Modifications copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.app.cache;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
import com.hazelcast.com.eclipsesource.json.JsonObject;
|
||||
|
||||
public interface CacheBean {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public void put(String key , Object value , String orgi) ;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public void clear(String orgi);
|
||||
|
||||
|
||||
public Object delete(String key , String orgi) ;
|
||||
|
||||
public void update(String key , String orgi , Object object) ;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param key
|
||||
* @param orgi
|
||||
* @return
|
||||
*/
|
||||
public Object getCacheObject(String key, String orgi) ;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param key
|
||||
* @param orgi
|
||||
* @return
|
||||
*/
|
||||
public Object getCacheObject(String key, String orgi,Object defaultValue) ;
|
||||
|
||||
/**
|
||||
* 获取所有缓存对象
|
||||
* @param orgi
|
||||
* @return
|
||||
*/
|
||||
public Collection<?> getAllCacheObject(String orgi) ;
|
||||
|
||||
|
||||
public CacheBean getCacheInstance(String cacheName);
|
||||
|
||||
public Object getCache();
|
||||
|
||||
public JsonObject getStatics();
|
||||
|
||||
public Lock getLock(String lock, String orgi);
|
||||
|
||||
public long getSize();
|
||||
|
||||
public long getAtomicLong(String cacheName) ;
|
||||
|
||||
public void setAtomicLong(String cacheName , long start) ; //初始化 发号器
|
||||
|
||||
}
|
64
contact-center/app/src/main/java/com/chatopera/cc/app/cache/CacheHelper.java
vendored
Normal file
64
contact-center/app/src/main/java/com/chatopera/cc/app/cache/CacheHelper.java
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统
|
||||
* Modifications copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.app.cache;
|
||||
|
||||
import com.chatopera.cc.app.cache.hazelcast.HazlcastCacheHelper;
|
||||
|
||||
public class CacheHelper {
|
||||
private static CacheHelper instance = new CacheHelper();
|
||||
|
||||
/**
|
||||
* 获取缓存实例
|
||||
*/
|
||||
public static CacheHelper getInstance(){
|
||||
return instance ;
|
||||
}
|
||||
private static CacheInstance cacheInstance = new HazlcastCacheHelper();
|
||||
|
||||
public static CacheBean getAgentStatusCacheBean() {
|
||||
return cacheInstance!=null ? cacheInstance.getAgentStatusCacheBean() : null;
|
||||
}
|
||||
public static CacheBean getAgentUserCacheBean() {
|
||||
return cacheInstance!=null ? cacheInstance.getAgentUserCacheBean() : null ;
|
||||
}
|
||||
public static CacheBean getOnlineUserCacheBean() {
|
||||
return cacheInstance!=null ? cacheInstance.getOnlineCacheBean() : null;
|
||||
}
|
||||
public static CacheBean getSystemCacheBean() {
|
||||
return cacheInstance!=null ? cacheInstance.getSystemCacheBean() : null ;
|
||||
}
|
||||
|
||||
public static CacheBean getIMRCacheBean() {
|
||||
return cacheInstance!=null ? cacheInstance.getIMRCacheBean() : null ;
|
||||
}
|
||||
public static CacheBean getCallCenterCacheBean() {
|
||||
return cacheInstance!=null ? cacheInstance.getCallCenterCacheBean() : null ;
|
||||
}
|
||||
public static CacheBean getCallCenterAgentCacheBean() {
|
||||
return cacheInstance!=null ? cacheInstance.getCallCenterAgentCacheBean() : null ;
|
||||
}
|
||||
public static CacheBean getApiUserCacheBean() {
|
||||
return cacheInstance!=null ? cacheInstance.getApiUserCacheBean() : null ;
|
||||
}
|
||||
public static CacheBean getJobCacheBean() {
|
||||
return cacheInstance!=null ? cacheInstance.getJobCacheBean(): null ;
|
||||
}
|
||||
public static CacheBean getCallOutCacheBean() {
|
||||
return cacheInstance!=null ? cacheInstance.getCallOutCacheBean(): null ;
|
||||
}
|
||||
|
||||
}
|
84
contact-center/app/src/main/java/com/chatopera/cc/app/cache/CacheInstance.java
vendored
Normal file
84
contact-center/app/src/main/java/com/chatopera/cc/app/cache/CacheInstance.java
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统
|
||||
* Modifications copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.app.cache;
|
||||
|
||||
|
||||
public interface CacheInstance {
|
||||
/**
|
||||
* 坐席状态
|
||||
* @return
|
||||
*/
|
||||
public CacheBean getAgentStatusCacheBean() ;
|
||||
|
||||
|
||||
/**
|
||||
* 服务中用户
|
||||
* @return
|
||||
*/
|
||||
public CacheBean getAgentUserCacheBean();
|
||||
|
||||
|
||||
/**
|
||||
* 在线用户
|
||||
* @return
|
||||
*/
|
||||
public CacheBean getOnlineCacheBean();
|
||||
|
||||
/**
|
||||
* 系统缓存
|
||||
* @return
|
||||
*/
|
||||
public CacheBean getSystemCacheBean();
|
||||
|
||||
|
||||
/**
|
||||
* IMR指令
|
||||
* @return
|
||||
*/
|
||||
public CacheBean getIMRCacheBean();
|
||||
|
||||
/**
|
||||
* IMR指令
|
||||
* @return
|
||||
*/
|
||||
public CacheBean getCallCenterCacheBean();
|
||||
|
||||
/**
|
||||
* IMR指令
|
||||
* @return
|
||||
*/
|
||||
public CacheBean getCallCenterAgentCacheBean();
|
||||
|
||||
/**
|
||||
* IMR指令
|
||||
* @return
|
||||
*/
|
||||
public CacheBean getApiUserCacheBean();
|
||||
|
||||
/**
|
||||
* IMR指令
|
||||
* @return
|
||||
*/
|
||||
public CacheBean getJobCacheBean();
|
||||
|
||||
/**
|
||||
* 外呼
|
||||
* @return
|
||||
*/
|
||||
public CacheBean getCallOutCacheBean();
|
||||
|
||||
}
|
91
contact-center/app/src/main/java/com/chatopera/cc/app/cache/hazelcast/HazlcastCacheHelper.java
vendored
Normal file
91
contact-center/app/src/main/java/com/chatopera/cc/app/cache/hazelcast/HazlcastCacheHelper.java
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统
|
||||
* Modifications copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.app.cache.hazelcast;
|
||||
|
||||
import com.chatopera.cc.app.basic.MainContext;
|
||||
import com.chatopera.cc.app.cache.CacheBean;
|
||||
import com.chatopera.cc.app.cache.CacheInstance;
|
||||
import com.chatopera.cc.app.cache.hazelcast.impl.AgentStatusCache;
|
||||
import com.chatopera.cc.app.cache.hazelcast.impl.AgentUserCache;
|
||||
import com.chatopera.cc.app.cache.hazelcast.impl.ApiUserCache;
|
||||
import com.chatopera.cc.app.cache.hazelcast.impl.CallCenterCache;
|
||||
import com.chatopera.cc.app.cache.hazelcast.impl.JobCache;
|
||||
import com.chatopera.cc.app.cache.hazelcast.impl.MultiCache;
|
||||
import com.chatopera.cc.app.cache.hazelcast.impl.OnlineCache;
|
||||
import com.chatopera.cc.app.cache.hazelcast.impl.SystemCache;
|
||||
/**
|
||||
* Hazlcast缓存处理实例类
|
||||
* @author admin
|
||||
*
|
||||
*/
|
||||
public class HazlcastCacheHelper implements CacheInstance {
|
||||
/**
|
||||
* 服务类型枚举
|
||||
* @author admin
|
||||
*
|
||||
*/
|
||||
public enum CacheServiceEnum{
|
||||
HAZLCAST_CLUSTER_AGENT_USER_CACHE, HAZLCAST_CLUSTER_AGENT_STATUS_CACHE, HAZLCAST_CLUSTER_QUENE_USER_CACHE,HAZLCAST_ONLINE_CACHE , HAZLCAST_CULUSTER_SYSTEM , HAZLCAST_IMR_CACHE , API_USER_CACHE , CALLCENTER_CURRENT_CALL ,CALLCENTER_AGENT,JOB_CACHE,HAZLCAST_CALLOUT_CACHE;
|
||||
public String toString(){
|
||||
return super.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CacheBean getAgentStatusCacheBean() {
|
||||
// TODO Auto-generated method stub
|
||||
return MainContext.getContext().getBean(AgentStatusCache.class).getCacheInstance(CacheServiceEnum.HAZLCAST_CLUSTER_AGENT_STATUS_CACHE.toString()) ;
|
||||
}
|
||||
@Override
|
||||
public CacheBean getAgentUserCacheBean() {
|
||||
// TODO Auto-generated method stub
|
||||
return MainContext.getContext().getBean(AgentUserCache.class).getCacheInstance(CacheServiceEnum.HAZLCAST_CLUSTER_QUENE_USER_CACHE.toString()) ;
|
||||
}
|
||||
@Override
|
||||
public CacheBean getOnlineCacheBean() {
|
||||
return MainContext.getContext().getBean(OnlineCache.class).getCacheInstance(CacheServiceEnum.HAZLCAST_ONLINE_CACHE.toString()) ;
|
||||
}
|
||||
@Override
|
||||
public CacheBean getSystemCacheBean() {
|
||||
return MainContext.getContext().getBean(SystemCache.class).getCacheInstance(CacheServiceEnum.HAZLCAST_CULUSTER_SYSTEM.toString()) ;
|
||||
}
|
||||
@Override
|
||||
public CacheBean getIMRCacheBean() {
|
||||
return MainContext.getContext().getBean(MultiCache.class).getCacheInstance(CacheServiceEnum.HAZLCAST_IMR_CACHE.toString()) ;
|
||||
}
|
||||
@Override
|
||||
public CacheBean getCallCenterCacheBean() {
|
||||
return MainContext.getContext().getBean(CallCenterCache.class).getCacheInstance(CacheServiceEnum.CALLCENTER_CURRENT_CALL.toString()) ;
|
||||
}
|
||||
@Override
|
||||
public CacheBean getCallCenterAgentCacheBean() {
|
||||
return MainContext.getContext().getBean(CallCenterCache.class).getCacheInstance(CacheServiceEnum.CALLCENTER_AGENT.toString()) ;
|
||||
}
|
||||
@Override
|
||||
public CacheBean getApiUserCacheBean() {
|
||||
return MainContext.getContext().getBean(ApiUserCache.class).getCacheInstance(CacheServiceEnum.API_USER_CACHE.toString()) ;
|
||||
}
|
||||
@Override
|
||||
public CacheBean getJobCacheBean() {
|
||||
return MainContext.getContext().getBean(JobCache.class).getCacheInstance(CacheServiceEnum.JOB_CACHE.toString()) ;
|
||||
}
|
||||
@Override
|
||||
public CacheBean getCallOutCacheBean() {
|
||||
// TODO Auto-generated method stub
|
||||
return MainContext.getContext().getBean(JobCache.class).getCacheInstance(CacheServiceEnum.HAZLCAST_CALLOUT_CACHE.toString()) ;
|
||||
}
|
||||
}
|
115
contact-center/app/src/main/java/com/chatopera/cc/app/cache/hazelcast/impl/AgentStatusCache.java
vendored
Normal file
115
contact-center/app/src/main/java/com/chatopera/cc/app/cache/hazelcast/impl/AgentStatusCache.java
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统
|
||||
* Modifications copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.app.cache.hazelcast.impl;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.hazelcast.com.eclipsesource.json.JsonObject;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import com.chatopera.cc.app.cache.CacheBean;
|
||||
|
||||
@Service("agentstatus_cache")
|
||||
public class AgentStatusCache implements CacheBean{
|
||||
|
||||
@Autowired
|
||||
public HazelcastInstance hazelcastInstance;
|
||||
|
||||
private String cacheName ;
|
||||
|
||||
public HazelcastInstance getInstance(){
|
||||
return hazelcastInstance ;
|
||||
}
|
||||
public CacheBean getCacheInstance(String cacheName){
|
||||
this.cacheName = cacheName ;
|
||||
return this ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String key, Object value, String orgi) {
|
||||
getInstance().getMap(getName()).put(key, value) ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear(String orgi) {
|
||||
getInstance().getMap(getName()).clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object delete(String key, String orgi) {
|
||||
return getInstance().getMap(getName()).remove(key) ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(String key, String orgi, Object value) {
|
||||
getInstance().getMap(getName()).put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCacheObject(String key, String orgi) {
|
||||
return getInstance().getMap(getName()).get(key);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return cacheName ;
|
||||
}
|
||||
|
||||
// @Override
|
||||
public void service() throws Exception {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<?> getAllCacheObject(String orgi) {
|
||||
return getInstance().getMap(getName()).keySet();
|
||||
}
|
||||
@Override
|
||||
public Object getCacheObject(String key, String orgi, Object defaultValue) {
|
||||
return getCacheObject(key, orgi);
|
||||
}
|
||||
@Override
|
||||
public Object getCache() {
|
||||
return getInstance().getMap(cacheName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Lock getLock(String lock , String orgi) {
|
||||
// TODO Auto-generated method stub
|
||||
return getInstance().getLock(lock);
|
||||
}
|
||||
@Override
|
||||
public long getSize() {
|
||||
return getInstance().getMap(getName()).size();
|
||||
}
|
||||
@Override
|
||||
public long getAtomicLong(String cacheName) {
|
||||
return getInstance().getAtomicLong(getName()).incrementAndGet();
|
||||
}
|
||||
@Override
|
||||
public void setAtomicLong(String cacheName, long start) {
|
||||
getInstance().getAtomicLong(getName()).set(start);
|
||||
}
|
||||
@Override
|
||||
public JsonObject getStatics() {
|
||||
// TODO Auto-generated method stub
|
||||
return getInstance().getMap(getName()).getLocalMapStats().toJson();
|
||||
}
|
||||
}
|
115
contact-center/app/src/main/java/com/chatopera/cc/app/cache/hazelcast/impl/AgentUserCache.java
vendored
Normal file
115
contact-center/app/src/main/java/com/chatopera/cc/app/cache/hazelcast/impl/AgentUserCache.java
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统
|
||||
* Modifications copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.app.cache.hazelcast.impl;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.hazelcast.com.eclipsesource.json.JsonObject;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import com.chatopera.cc.app.cache.CacheBean;
|
||||
|
||||
@Service("agentuser_cache")
|
||||
public class AgentUserCache implements CacheBean{
|
||||
|
||||
@Autowired
|
||||
public HazelcastInstance hazelcastInstance;
|
||||
|
||||
private String cacheName ;
|
||||
|
||||
public HazelcastInstance getInstance(){
|
||||
return hazelcastInstance ;
|
||||
}
|
||||
public CacheBean getCacheInstance(String cacheName){
|
||||
this.cacheName = cacheName ;
|
||||
return this ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String key, Object value, String orgi) {
|
||||
getInstance().getMap(getName()).put(key, value) ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear(String orgi) {
|
||||
getInstance().getMap(getName()).clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object delete(String key, String orgi) {
|
||||
return getInstance().getMap(getName()).remove(key) ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(String key, String orgi, Object value) {
|
||||
getInstance().getMap(getName()).put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCacheObject(String key, String orgi) {
|
||||
return getInstance().getMap(getName()).get(key);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return cacheName ;
|
||||
}
|
||||
|
||||
// @Override
|
||||
public void service() throws Exception {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<?> getAllCacheObject(String orgi) {
|
||||
return getInstance().getMap(getName()).keySet();
|
||||
}
|
||||
@Override
|
||||
public Object getCacheObject(String key, String orgi, Object defaultValue) {
|
||||
return getCacheObject(key, orgi);
|
||||
}
|
||||
@Override
|
||||
public Object getCache() {
|
||||
return getInstance().getMap(cacheName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Lock getLock(String lock , String orgi) {
|
||||
// TODO Auto-generated method stub
|
||||
return getInstance().getLock(lock);
|
||||
}
|
||||
@Override
|
||||
public long getSize() {
|
||||
return getInstance().getMap(getName()).size();
|
||||
}
|
||||
@Override
|
||||
public long getAtomicLong(String cacheName) {
|
||||
return getInstance().getAtomicLong(getName()).incrementAndGet();
|
||||
}
|
||||
@Override
|
||||
public void setAtomicLong(String cacheName, long start) {
|
||||
getInstance().getAtomicLong(getName()).set(start);
|
||||
}
|
||||
@Override
|
||||
public JsonObject getStatics() {
|
||||
// TODO Auto-generated method stub
|
||||
return getInstance().getMap(getName()).getLocalMapStats().toJson();
|
||||
}
|
||||
}
|
115
contact-center/app/src/main/java/com/chatopera/cc/app/cache/hazelcast/impl/ApiUserCache.java
vendored
Normal file
115
contact-center/app/src/main/java/com/chatopera/cc/app/cache/hazelcast/impl/ApiUserCache.java
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统
|
||||
* Modifications copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.app.cache.hazelcast.impl;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.hazelcast.com.eclipsesource.json.JsonObject;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import com.chatopera.cc.app.cache.CacheBean;
|
||||
|
||||
@Service("api_user_cache")
|
||||
public class ApiUserCache implements CacheBean{
|
||||
|
||||
@Autowired
|
||||
public HazelcastInstance hazelcastInstance;
|
||||
|
||||
private String cacheName ;
|
||||
|
||||
public HazelcastInstance getInstance(){
|
||||
return hazelcastInstance ;
|
||||
}
|
||||
public CacheBean getCacheInstance(String cacheName){
|
||||
this.cacheName = cacheName ;
|
||||
return this ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String key, Object value, String orgi) {
|
||||
getInstance().getMap(getName()).put(key, value) ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear(String orgi) {
|
||||
getInstance().getMap(getName()).clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object delete(String key, String orgi) {
|
||||
return getInstance().getMap(getName()).remove(key) ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(String key, String orgi, Object value) {
|
||||
getInstance().getMap(getName()).put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCacheObject(String key, String orgi) {
|
||||
return getInstance().getMap(getName()).get(key);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return cacheName ;
|
||||
}
|
||||
|
||||
// @Override
|
||||
public void service() throws Exception {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<?> getAllCacheObject(String orgi) {
|
||||
return getInstance().getMap(getName()).keySet();
|
||||
}
|
||||
@Override
|
||||
public Object getCacheObject(String key, String orgi, Object defaultValue) {
|
||||
return getCacheObject(key, orgi);
|
||||
}
|
||||
@Override
|
||||
public Object getCache() {
|
||||
return getInstance().getMap(cacheName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Lock getLock(String lock , String orgi) {
|
||||
// TODO Auto-generated method stub
|
||||
return getInstance().getLock(lock);
|
||||
}
|
||||
@Override
|
||||
public long getSize() {
|
||||
return getInstance().getMap(getName()).size();
|
||||
}
|
||||
@Override
|
||||
public long getAtomicLong(String cacheName) {
|
||||
return getInstance().getAtomicLong(getName()).incrementAndGet();
|
||||
}
|
||||
@Override
|
||||
public void setAtomicLong(String cacheName, long start) {
|
||||
getInstance().getAtomicLong(getName()).set(start);
|
||||
}
|
||||
@Override
|
||||
public JsonObject getStatics() {
|
||||
// TODO Auto-generated method stub
|
||||
return getInstance().getMap(getName()).getLocalMapStats().toJson();
|
||||
}
|
||||
}
|
115
contact-center/app/src/main/java/com/chatopera/cc/app/cache/hazelcast/impl/CallCenterCache.java
vendored
Normal file
115
contact-center/app/src/main/java/com/chatopera/cc/app/cache/hazelcast/impl/CallCenterCache.java
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统
|
||||
* Modifications copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.app.cache.hazelcast.impl;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.hazelcast.com.eclipsesource.json.JsonObject;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import com.chatopera.cc.app.cache.CacheBean;
|
||||
|
||||
@Service("callcenter_current_call")
|
||||
public class CallCenterCache implements CacheBean{
|
||||
|
||||
@Autowired
|
||||
public HazelcastInstance hazelcastInstance;
|
||||
|
||||
private String cacheName ;
|
||||
|
||||
public HazelcastInstance getInstance(){
|
||||
return hazelcastInstance ;
|
||||
}
|
||||
public CacheBean getCacheInstance(String cacheName){
|
||||
this.cacheName = cacheName ;
|
||||
return this ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String key, Object value, String orgi) {
|
||||
getInstance().getMap(getName()).put(key, value) ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear(String orgi) {
|
||||
getInstance().getMap(getName()).clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object delete(String key, String orgi) {
|
||||
return getInstance().getMap(getName()).remove(key) ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(String key, String orgi, Object value) {
|
||||
getInstance().getMap(getName()).put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCacheObject(String key, String orgi) {
|
||||
return getInstance().getMap(getName()).get(key);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return cacheName ;
|
||||
}
|
||||
|
||||
// @Override
|
||||
public void service() throws Exception {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<?> getAllCacheObject(String orgi) {
|
||||
return getInstance().getMap(getName()).keySet();
|
||||
}
|
||||
@Override
|
||||
public Object getCacheObject(String key, String orgi, Object defaultValue) {
|
||||
return getCacheObject(key, orgi);
|
||||
}
|
||||
@Override
|
||||
public Object getCache() {
|
||||
return getInstance().getMap(cacheName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Lock getLock(String lock , String orgi) {
|
||||
// TODO Auto-generated method stub
|
||||
return getInstance().getLock(lock);
|
||||
}
|
||||
@Override
|
||||
public long getSize() {
|
||||
return getInstance().getMap(getName()).size();
|
||||
}
|
||||
@Override
|
||||
public long getAtomicLong(String cacheName) {
|
||||
return getInstance().getAtomicLong(getName()).incrementAndGet();
|
||||
}
|
||||
@Override
|
||||
public void setAtomicLong(String cacheName, long start) {
|
||||
getInstance().getAtomicLong(getName()).set(start);
|
||||
}
|
||||
@Override
|
||||
public JsonObject getStatics() {
|
||||
// TODO Auto-generated method stub
|
||||
return getInstance().getMap(getName()).getLocalMapStats().toJson();
|
||||
}
|
||||
}
|
116
contact-center/app/src/main/java/com/chatopera/cc/app/cache/hazelcast/impl/JobCache.java
vendored
Normal file
116
contact-center/app/src/main/java/com/chatopera/cc/app/cache/hazelcast/impl/JobCache.java
vendored
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统
|
||||
* Modifications copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.app.cache.hazelcast.impl;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.hazelcast.com.eclipsesource.json.JsonObject;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import com.chatopera.cc.app.cache.CacheBean;
|
||||
|
||||
@Service("job_cache")
|
||||
public class JobCache implements CacheBean{
|
||||
|
||||
@Autowired
|
||||
public HazelcastInstance hazelcastInstance;
|
||||
|
||||
private String cacheName ;
|
||||
|
||||
public HazelcastInstance getInstance(){
|
||||
return hazelcastInstance ;
|
||||
}
|
||||
public CacheBean getCacheInstance(String cacheName){
|
||||
this.cacheName = cacheName ;
|
||||
return this ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String key, Object value, String orgi) {
|
||||
getInstance().getMap(getName()).put(key, value) ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear(String orgi) {
|
||||
getInstance().getMap(getName()).clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object delete(String key, String orgi) {
|
||||
return getInstance().getMap(getName()).remove(key) ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(String key, String orgi, Object value) {
|
||||
getInstance().getMap(getName()).put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCacheObject(String key, String orgi) {
|
||||
return getInstance().getMap(getName()).get(key);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return cacheName ;
|
||||
}
|
||||
|
||||
// @Override
|
||||
public void service() throws Exception {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<?> getAllCacheObject(String orgi) {
|
||||
return getInstance().getMap(getName()).keySet();
|
||||
}
|
||||
@Override
|
||||
public Object getCacheObject(String key, String orgi, Object defaultValue) {
|
||||
return getCacheObject(key, orgi);
|
||||
}
|
||||
@Override
|
||||
public Object getCache() {
|
||||
return getInstance().getMap(cacheName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Lock getLock(String lock , String orgi) {
|
||||
// TODO Auto-generated method stub
|
||||
return getInstance().getLock(lock);
|
||||
}
|
||||
@Override
|
||||
public long getSize() {
|
||||
return getInstance().getMap(getName()).size();
|
||||
}
|
||||
@Override
|
||||
public long getAtomicLong(String cacheName) {
|
||||
return getInstance().getAtomicLong(getName()).incrementAndGet();
|
||||
}
|
||||
@Override
|
||||
public void setAtomicLong(String cacheName, long start) {
|
||||
getInstance().getAtomicLong(getName()).set(start);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject getStatics() {
|
||||
// TODO Auto-generated method stub
|
||||
return getInstance().getMap(getName()).getLocalMapStats().toJson();
|
||||
}
|
||||
}
|
117
contact-center/app/src/main/java/com/chatopera/cc/app/cache/hazelcast/impl/MultiCache.java
vendored
Normal file
117
contact-center/app/src/main/java/com/chatopera/cc/app/cache/hazelcast/impl/MultiCache.java
vendored
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统
|
||||
* Modifications copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.app.cache.hazelcast.impl;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
import com.chatopera.cc.app.cache.CacheBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.hazelcast.com.eclipsesource.json.JsonObject;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
|
||||
@Service("multi_cache")
|
||||
public class MultiCache implements CacheBean {
|
||||
|
||||
@Autowired
|
||||
public HazelcastInstance hazelcastInstance;
|
||||
|
||||
private String cacheName ;
|
||||
|
||||
public HazelcastInstance getInstance(){
|
||||
return hazelcastInstance ;
|
||||
}
|
||||
public CacheBean getCacheInstance(String cacheName){
|
||||
this.cacheName = cacheName ;
|
||||
return this ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String key, Object value, String orgi) {
|
||||
getInstance().getMultiMap(getName()).put(key, value) ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear(String orgi) {
|
||||
getInstance().getMultiMap(getName()).clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object delete(String key, String orgi) {
|
||||
return getInstance().getMultiMap(getName()).remove(key) ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(String key, String orgi, Object value) {
|
||||
getInstance().getMultiMap(getName()).put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCacheObject(String key, String orgi) {
|
||||
return getInstance().getMultiMap(getName()).get(key);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return cacheName ;
|
||||
}
|
||||
|
||||
// @Override
|
||||
public void service() throws Exception {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<?> getAllCacheObject(String orgi) {
|
||||
return getInstance().getMultiMap(getName()).keySet();
|
||||
}
|
||||
@Override
|
||||
public Object getCacheObject(String key, String orgi, Object defaultValue) {
|
||||
return getCacheObject(key, orgi);
|
||||
|
||||
}
|
||||
@Override
|
||||
public Object getCache() {
|
||||
return getInstance().getMultiMap(cacheName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Lock getLock(String lock , String orgi) {
|
||||
// TODO Auto-generated method stub
|
||||
return getInstance().getLock(lock);
|
||||
}
|
||||
@Override
|
||||
public long getSize() {
|
||||
return getInstance().getMultiMap(getName()).size();
|
||||
}
|
||||
@Override
|
||||
public long getAtomicLong(String cacheName) {
|
||||
return getInstance().getAtomicLong(getName()).incrementAndGet();
|
||||
}
|
||||
@Override
|
||||
public void setAtomicLong(String cacheName, long start) {
|
||||
getInstance().getAtomicLong(getName()).set(start);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject getStatics() {
|
||||
// TODO Auto-generated method stub
|
||||
return getInstance().getMultiMap(getName()).getLocalMultiMapStats().toJson();
|
||||
}
|
||||
}
|
116
contact-center/app/src/main/java/com/chatopera/cc/app/cache/hazelcast/impl/OnlineCache.java
vendored
Normal file
116
contact-center/app/src/main/java/com/chatopera/cc/app/cache/hazelcast/impl/OnlineCache.java
vendored
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统
|
||||
* Modifications copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.app.cache.hazelcast.impl;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.hazelcast.com.eclipsesource.json.JsonObject;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import com.chatopera.cc.app.cache.CacheBean;
|
||||
|
||||
@Service("online_cache")
|
||||
public class OnlineCache implements CacheBean{
|
||||
|
||||
@Autowired
|
||||
public HazelcastInstance hazelcastInstance;
|
||||
|
||||
private String cacheName ;
|
||||
|
||||
public HazelcastInstance getInstance(){
|
||||
return hazelcastInstance ;
|
||||
}
|
||||
public CacheBean getCacheInstance(String cacheName){
|
||||
this.cacheName = cacheName ;
|
||||
return this ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String key, Object value, String orgi) {
|
||||
getInstance().getMap(getName()).put(key, value) ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear(String orgi) {
|
||||
getInstance().getMap(getName()).clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object delete(String key, String orgi) {
|
||||
return getInstance().getMap(getName()).remove(key) ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(String key, String orgi, Object value) {
|
||||
getInstance().getMap(getName()).put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCacheObject(String key, String orgi) {
|
||||
return getInstance().getMap(getName()).get(key);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return cacheName ;
|
||||
}
|
||||
|
||||
// @Override
|
||||
public void service() throws Exception {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<?> getAllCacheObject(String orgi) {
|
||||
return getInstance().getMap(getName()).keySet();
|
||||
}
|
||||
@Override
|
||||
public Object getCacheObject(String key, String orgi, Object defaultValue) {
|
||||
return getCacheObject(key, orgi);
|
||||
}
|
||||
@Override
|
||||
public Object getCache() {
|
||||
return getInstance().getMap(cacheName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Lock getLock(String lock , String orgi) {
|
||||
// TODO Auto-generated method stub
|
||||
return getInstance().getLock(lock);
|
||||
}
|
||||
@Override
|
||||
public long getSize() {
|
||||
return getInstance().getMap(getName()).size();
|
||||
}
|
||||
@Override
|
||||
public long getAtomicLong(String cacheName) {
|
||||
return getInstance().getAtomicLong(getName()).incrementAndGet();
|
||||
}
|
||||
@Override
|
||||
public void setAtomicLong(String cacheName, long start) {
|
||||
getInstance().getAtomicLong(getName()).set(start);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject getStatics() {
|
||||
// TODO Auto-generated method stub
|
||||
return getInstance().getMap(getName()).getLocalMapStats().toJson();
|
||||
}
|
||||
}
|
116
contact-center/app/src/main/java/com/chatopera/cc/app/cache/hazelcast/impl/SystemCache.java
vendored
Normal file
116
contact-center/app/src/main/java/com/chatopera/cc/app/cache/hazelcast/impl/SystemCache.java
vendored
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统
|
||||
* Modifications copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.app.cache.hazelcast.impl;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
import com.chatopera.cc.app.cache.CacheBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.hazelcast.com.eclipsesource.json.JsonObject;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
|
||||
@Service("system_cache")
|
||||
public class SystemCache implements CacheBean {
|
||||
|
||||
@Autowired
|
||||
public HazelcastInstance hazelcastInstance;
|
||||
|
||||
private String cacheName ;
|
||||
|
||||
public HazelcastInstance getInstance(){
|
||||
return hazelcastInstance ;
|
||||
}
|
||||
public CacheBean getCacheInstance(String cacheName){
|
||||
this.cacheName = cacheName ;
|
||||
return this ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String key, Object value, String orgi) {
|
||||
getInstance().getMap(getName()).put(key, value) ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear(String orgi) {
|
||||
getInstance().getMap(getName()).clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object delete(String key, String orgi) {
|
||||
return getInstance().getMap(getName()).remove(key) ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(String key, String orgi, Object value) {
|
||||
getInstance().getMap(getName()).put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCacheObject(String key, String orgi) {
|
||||
return getInstance().getMap(getName()).get(key);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return cacheName ;
|
||||
}
|
||||
|
||||
// @Override
|
||||
public void service() throws Exception {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<?> getAllCacheObject(String orgi) {
|
||||
return getInstance().getMap(getName()).keySet();
|
||||
}
|
||||
@Override
|
||||
public Object getCacheObject(String key, String orgi, Object defaultValue) {
|
||||
return getCacheObject(key, orgi);
|
||||
}
|
||||
@Override
|
||||
public Object getCache() {
|
||||
return getInstance().getMap(cacheName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Lock getLock(String lock , String orgi) {
|
||||
// TODO Auto-generated method stub
|
||||
return getInstance().getLock(lock);
|
||||
}
|
||||
@Override
|
||||
public long getSize() {
|
||||
return getInstance().getMap(getName()).size();
|
||||
}
|
||||
@Override
|
||||
public long getAtomicLong(String cacheName) {
|
||||
return getInstance().getAtomicLong(getName()).incrementAndGet();
|
||||
}
|
||||
@Override
|
||||
public void setAtomicLong(String cacheName, long start) {
|
||||
getInstance().getAtomicLong(getName()).set(start);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject getStatics() {
|
||||
// TODO Auto-generated method stub
|
||||
return getInstance().getMap(getName()).getLocalMapStats().toJson();
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统
|
||||
* Modifications copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.app.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import springfox.documentation.builders.ApiInfoBuilder;
|
||||
import springfox.documentation.builders.PathSelectors;
|
||||
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||
import springfox.documentation.service.ApiInfo;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spring.web.plugins.Docket;
|
||||
import springfox.documentation.swagger2.annotations.EnableSwagger2;
|
||||
|
||||
@Configuration
|
||||
@EnableSwagger2
|
||||
public class ApiConfigure {
|
||||
|
||||
@Bean
|
||||
public Docket createRestApi() {
|
||||
return new Docket(DocumentationType.SWAGGER_2)
|
||||
.groupName("CSKefu")
|
||||
.apiInfo(apiInfo())
|
||||
.select()
|
||||
.apis(RequestHandlerSelectors.basePackage("com.chatopera.cc.app.handler.api.rest"))
|
||||
.paths(PathSelectors.any())
|
||||
.build();
|
||||
}
|
||||
|
||||
private ApiInfo apiInfo() {
|
||||
return new ApiInfoBuilder()
|
||||
.title("春松客服")
|
||||
.description("春松客服 基于 JAVA 语言开发,是一个全渠道融合的客户支持服务平台,聚合企业内部多个客服渠道,帮助各种行业各种规模的企业建立完整客服体系。通过将邮件、短信、电话语音、WebIM 在线客服、微信、微博、H5 页面、APP 接口等多个渠道来源的客户服务请求与对话汇聚在一个管理平台,用统一的方式来响应和支撑客户服务。")
|
||||
.termsOfServiceUrl("http://docs.chatopera.com/")
|
||||
.contact("春松客服")
|
||||
.version("1.0.0")
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统
|
||||
* Modifications copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.app.config;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.chatopera.cc.app.cache.CacheHelper;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
|
||||
import com.chatopera.cc.app.basic.MainContext;
|
||||
|
||||
public class ApiRequestMatchingFilter implements Filter {
|
||||
private RequestMatcher[] ignoredRequests;
|
||||
|
||||
public ApiRequestMatchingFilter(RequestMatcher... matcher) {
|
||||
this.ignoredRequests = matcher;
|
||||
}
|
||||
|
||||
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
|
||||
HttpServletRequest request = (HttpServletRequest) req;
|
||||
HttpServletResponse response = (HttpServletResponse) resp;
|
||||
|
||||
String method = request.getMethod() ;
|
||||
|
||||
if(!StringUtils.isBlank(method) && method.equalsIgnoreCase("options")){
|
||||
response.setHeader("Access-Control-Allow-Origin", "*");
|
||||
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
|
||||
response.setHeader("Access-Control-Max-Age", "3600");
|
||||
response.setHeader("Access-Control-Allow-Headers", "x-requested-with,accept,authorization,content-type");
|
||||
response.setHeader("X-Frame-Options", "SAMEORIGIN");
|
||||
response.setStatus(HttpStatus.ACCEPTED.value());
|
||||
}else{
|
||||
boolean matchAnyRoles = false ;
|
||||
for(RequestMatcher anyRequest : ignoredRequests ){
|
||||
if(anyRequest.matches(request)){
|
||||
matchAnyRoles = true ;
|
||||
}
|
||||
}
|
||||
if(matchAnyRoles){
|
||||
String authorization = request.getHeader("authorization") ;
|
||||
if(StringUtils.isBlank(authorization)){
|
||||
authorization = request.getParameter("authorization") ;
|
||||
}
|
||||
if(!StringUtils.isBlank(authorization) && CacheHelper.getApiUserCacheBean().getCacheObject(authorization, MainContext.SYSTEM_ORGI) != null){
|
||||
chain.doFilter(req,resp);
|
||||
}else{
|
||||
response.sendRedirect("/tokens/error");
|
||||
}
|
||||
}else{
|
||||
chain.doFilter(req,resp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig arg0) throws ServletException {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统
|
||||
* Modifications copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.app.config;
|
||||
|
||||
import com.chatopera.cc.app.basic.MainContext;
|
||||
import com.chatopera.cc.app.model.Favorites;
|
||||
import com.chatopera.cc.app.model.WorkOrders;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.data.elasticsearch.ElasticsearchException;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class ApplicationStartupListener implements ApplicationListener<ContextRefreshedEvent> {
|
||||
|
||||
@Autowired ElasticsearchTemplate elasticSearchTemplate;
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ContextRefreshedEvent event) {
|
||||
if (!elasticSearchTemplate.indexExists(WorkOrders.class)) {
|
||||
elasticSearchTemplate.createIndex(WorkOrders.class);
|
||||
}
|
||||
if (!elasticSearchTemplate.indexExists(Favorites.class)) {
|
||||
elasticSearchTemplate.createIndex(Favorites.class);
|
||||
}
|
||||
try {
|
||||
elasticSearchTemplate.getMapping(WorkOrders.class);
|
||||
} catch (ElasticsearchException e) {
|
||||
elasticSearchTemplate.putMapping(Favorites.class);
|
||||
elasticSearchTemplate.putMapping(WorkOrders.class);
|
||||
}
|
||||
MainContext.setTemplet(elasticSearchTemplate);
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统
|
||||
* Modifications copyright (C) 2018 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.chatopera.cc.app.config;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.chatopera.cc.app.basic.MainContext;
|
||||
import com.chatopera.cc.app.model.User;
|
||||
import org.apache.catalina.connector.ClientAbortException;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
|
||||
public class DelegateRequestMatchingFilter implements Filter {
|
||||
private RequestMatcher[] ignoredRequests;
|
||||
|
||||
public DelegateRequestMatchingFilter(RequestMatcher... matcher) {
|
||||
this.ignoredRequests = matcher;
|
||||
}
|
||||
|
||||
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
|
||||
HttpServletRequest request = (HttpServletRequest) req;
|
||||
boolean matchAnyRoles = false ;
|
||||
for(RequestMatcher anyRequest : ignoredRequests ){
|
||||
if(anyRequest.matches(request)){
|
||||
matchAnyRoles = true ;
|
||||
}
|
||||
}
|
||||
User user = (User) request.getSession().getAttribute(MainContext.USER_SESSION_NAME) ;
|
||||
if(matchAnyRoles){
|
||||
if(user !=null && "0".equals(user.getUsertype())){
|
||||
chain.doFilter(req,resp);
|
||||
}else{
|
||||
//重定向到 无权限执行操作的页面
|
||||
HttpServletResponse response = (HttpServletResponse) resp ;
|
||||
response.sendRedirect("/?msg=security");
|
||||
}
|
||||
}else{
|
||||
try{
|
||||
chain.doFilter(req,resp);
|
||||
}catch(ClientAbortException ex){
|
||||
//Tomcat异常,不做处理
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig arg0) throws ServletException {
|
||||
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user