1
0
mirror of https://github.com/chatopera/cosin.git synced 2025-06-16 18:30:03 +08:00

Update README.md, add chatbot plugin and other enhancements

This commit is contained in:
Hai Liang Wang 2019-11-17 23:14:53 +08:00
parent 2633da217c
commit 647f4d617f
30 changed files with 2270 additions and 48 deletions

View File

@ -251,7 +251,7 @@ Amazing! 要的就是这个效果。
## 开源许可协议
Copyright (2018) <a href="https://www.chatopera.com/" target="_blank">北京华夏春松科技有限公司</a>
Copyright (2018-2019) <a href="https://www.chatopera.com/" target="_blank">北京华夏春松科技有限公司</a>
[Apache License Version 2.0](https://github.com/chatopera/cosin/blob/master/LICENSE)

View File

@ -495,8 +495,8 @@ public class MainUtils {
Object[] value = (Object[]) values.get(i);
WebIMReport report = new WebIMReport();
if (value.length == 2) {
if (value[0] == null || value[0].toString().equalsIgnoreCase("null")) {
report.setData("未知");
if (value[0] == null || value[0].toString().equalsIgnoreCase("null") || StringUtils.isBlank(value[0].toString())) {
report.setData("其他");
} else {
report.setData((String) value[0]);
}
@ -1238,7 +1238,7 @@ public class MainUtils {
final String accessKeySecret = systemMessage.getAppsec();//你的accessKeySecret参考本文档步骤2
//初始化ascClient,暂时不支持多region请勿修改
IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId,
accessKeySecret);
accessKeySecret);
DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);
IAcsClient acsClient = new DefaultAcsClient(profile);
//组装请求对象

View File

@ -15,7 +15,12 @@
*/
package com.chatopera.cc.basic.plugins;
import java.util.List;
import java.util.Map;
public interface IPluginDescriptor {
String getPluginName();
String getIOEventHandler();
// 获得环境变量及默认值
Map<String, String> getEnvironmentVariables();
}

View File

@ -179,14 +179,14 @@ public class EntIMController extends Handler {
view.addObject("online", NettyClients.getInstance().getEntIMClientsNum(userid) > 0);
Page<ChatMessage> chatMessageList = chatMessageRes.findByContextidAndUseridAndOrgi(userid,
super.getUser(request).getId(), super.getOrgi(request),
new PageRequest(0, 20, Sort.Direction.DESC, "createtime")
super.getUser(request).getId(), super.getOrgi(request),
new PageRequest(0, 20, Sort.Direction.DESC, "createtime")
);
view.addObject("chatMessageList", chatMessageList);
RecentUser recentUser = recentUserRes.findByCreaterAndUserAndOrgi(super.getUser(request).getId(),
new User(userid), super.getOrgi(request)
new User(userid), super.getOrgi(request)
).orElseGet(() -> {
RecentUser u = new RecentUser();
u.setOrgi(super.getOrgi(request));
@ -224,8 +224,8 @@ public class EntIMController extends Handler {
ModelAndView view = request(super.createRequestPageTempletResponse("/apps/entim/more"));
Page<ChatMessage> chatMessageList = chatMessageRes.findByContextidAndUseridAndOrgiAndCreatetimeLessThan(userid,
super.getUser(request).getId(), super.getOrgi(request), createtime,
new PageRequest(0, 20, Sort.Direction.DESC, "createtime")
super.getUser(request).getId(), super.getOrgi(request), createtime,
new PageRequest(0, 20, Sort.Direction.DESC, "createtime")
);
view.addObject("chatMessageList", chatMessageList);
@ -241,7 +241,7 @@ public class EntIMController extends Handler {
view.addObject("imGroupUserList", imGroupUserRes.findByImgroupAndOrgi(imGroup, super.getOrgi(request)));
view.addObject("contextid", id);
view.addObject("chatMessageList", chatMessageRes.findByContextidAndOrgi(id, super.getOrgi(request),
new PageRequest(0, 20, Sort.Direction.DESC, "createtime")
new PageRequest(0, 20, Sort.Direction.DESC, "createtime")
));
return view;
}
@ -254,7 +254,7 @@ public class EntIMController extends Handler {
) {
ModelAndView view = request(super.createRequestPageTempletResponse("/apps/entim/group/more"));
view.addObject("chatMessageList", chatMessageRes.findByContextidAndOrgiAndCreatetimeLessThan(id,
super.getOrgi(request), createtime, new PageRequest(0, 20, Sort.Direction.DESC, "createtime")
super.getOrgi(request), createtime, new PageRequest(0, 20, Sort.Direction.DESC, "createtime")
));
return view;
}
@ -264,20 +264,17 @@ public class EntIMController extends Handler {
public ModelAndView user(HttpServletRequest request, HttpServletResponse response, @Valid String id) {
ModelAndView view = request(super.createEntIMTempletResponse("/apps/entim/group/user"));
User logined = super.getUser(request);
userProxy.attachOrgansPropertiesForUser(logined);
HashSet<String> affiliates = logined.getAffiliates();
// TODO: 优化性能
List<String> organIds = userProxy.findOrgansByUserid(logined.getId());
if (organIds != null) {
List<User> users = userProxy.findByOrganInAndDatastatus(organIds, false);
users.stream().forEach(u -> userProxy.attachOrgansPropertiesForUser(u));
view.addObject("userList", users);
}
List<User> users = userProxy.findByOrganInAndDatastatus(new ArrayList<>(affiliates), false);
users.stream().forEach(u -> userProxy.attachOrgansPropertiesForUser(u));
view.addObject("userList", users);
IMGroup imGroup = imGroupRes.findById(id);
List<Organ> organs = organRes.findAll(affiliates);
view.addObject("imGroup", imGroup);
view.addObject("organList", logined.getOrgans().values());
view.addObject("organList", organs);
view.addObject("imGroupUserList", imGroupUserRes.findByImgroupAndOrgi(imGroup, super.getOrgi(request)));
return view;
@ -440,7 +437,7 @@ public class EntIMController extends Handler {
streamingFileRepository.save(sf);
String id = attachmentProxy.processAttachmentFile(multipart,
fileid, logined.getOrgi(), logined.getId()
fileid, logined.getOrgi(), logined.getId()
);
upload = new UploadStatus("0", "/res/file.html?id=" + id);
String file = "/res/file.html?id=" + id;

View File

@ -29,12 +29,18 @@ Created by iconfont
<glyph glyph-name="sousuo" unicode="&#58951;" d="M845.31681 91.720967 669.806116 267.231661c39.298021 47.792125 60.686788 107.250849 60.686788 169.984409 0 71.637018-27.938437 138.975815-78.493704 189.63342-50.657605 50.657605-117.996402 78.493704-189.63342 78.493704s-138.975815-27.938437-189.63342-78.493704c-50.657605-50.657605-78.493704-117.996402-78.493704-189.63342s27.938437-138.975815 78.493704-189.63342c50.657605-50.657605 117.996402-78.493704 189.63342-78.493704 65.394363 0 127.206876 23.230861 176.022387 65.906056l174.999001-174.999001c4.40056-4.40056 10.131521-6.54967 15.964821-6.54967s11.564261 2.149111 15.964821 6.54967C854.117929 68.694783 854.117929 82.919848 845.31681 91.720967zM239.267639 437.21607c0 123.010993 100.087148 223.098141 223.098141 223.098141s223.098141-100.087148 223.098141-223.098141-100.087148-223.098141-223.098141-223.098141S239.267639 314.205077 239.267639 437.21607z" horiz-adv-x="1024" />
<glyph glyph-name="jigou" unicode="&#58907;" d="M0-128v60.237913h86.817391V775.524174C86.817391 842.039652 138.640696 896 202.573913 896h405.147826c63.933217 0 115.756522-53.960348 115.756522-120.475826v-240.951652h231.513043c63.933217 0 115.756522-53.915826 115.756522-120.475826v-481.858783H1157.565217V-128H0zM665.6 775.524174c0 33.302261-25.911652 60.237913-57.878261 60.237913h-405.147826c-31.966609 0-57.878261-26.935652-57.878261-60.237913v-843.286261h520.904348V775.524174z m347.269565-361.427478c0 33.302261-25.911652 60.237913-57.878261 60.237913h-231.513043v-542.096696h289.391304V414.096696z m-57.878261-150.572522h-173.634782v60.237913h173.634782v-60.237913z m0-180.713739h-173.634782v60.237913h173.634782v-60.237913z m-578.782608 481.903304h-173.634783V624.951652h173.634783v-60.237913z m0-180.713739h-173.634783v60.237913h173.634783V384z m0-180.713739h-173.634783v60.237913h173.634783v-60.237913z m231.513043 361.427478h-173.634782V624.951652h173.634782v-60.237913z m0-180.713739h-173.634782v60.237913h173.634782V384z m0-180.713739h-173.634782v60.237913h173.634782v-60.237913z" horiz-adv-x="1157" />
<glyph glyph-name="xiaoxi" unicode="&#58881;" d="M277.504 448.512c0-19.456 15.36-35.84 34.816-35.84s34.816 15.872 34.816 35.84c0 19.456-15.36 35.84-34.816 35.84-19.456-0.512-34.816-16.384-34.816-35.84M476.16 448.512c0-19.456 15.36-35.84 34.816-35.84s34.816 15.872 34.816 35.84c0 19.456-15.36 35.84-34.816 35.84-19.456-0.512-34.816-16.384-34.816-35.84M674.816 448.512c0-19.456 15.36-35.84 34.816-35.84s34.816 15.872 34.816 35.84c0 19.456-15.36 35.84-34.816 35.84-19.456-0.512-34.816-16.384-34.816-35.84M737.792-33.792c-4.096 0-8.704 1.536-12.288 4.096l-163.328 123.392H155.648c-55.808 0-100.864 46.08-100.864 102.912V695.808C54.784 752.64 99.84 798.72 155.648 798.72h710.144c55.808 0 100.864-46.08 100.864-102.912v-498.688c0-56.832-45.568-102.912-100.864-102.912H757.76v-107.52c0-7.68-4.608-14.848-11.264-18.432-2.56-1.024-5.632-2.048-8.704-2.048zM155.648 757.76c-33.28 0-60.416-28.16-60.416-62.464v-498.688c0-34.304 27.136-62.464 60.416-62.464h413.184c4.608 0 8.704-1.536 12.288-4.096l136.192-102.912v86.528c0 5.632 2.048 10.752 6.144 14.336 3.584 3.584 9.216 6.144 14.336 6.144h128.512c33.28 0 60.416 28.16 60.416 62.464V695.808c0 34.304-27.136 62.464-60.416 62.464H155.648z" horiz-adv-x="1024" />
<glyph glyph-name="quxiaoguanlian" unicode="&#58974;" d="M668.3 317.9l-24.9 24.9L832.8 532c14.6 14.6 22.6 34.1 22.6 54.9 0 20.8-8 40.3-22.6 54.9L754.5 720c-30.3 30.2-79.5 30.2-109.8 0L455.5 530.8l-24.9 24.9L619.9 745c44 44 115.5 44 159.5 0l78.2-78.2c21.2-21.2 32.9-49.5 32.9-79.7 0-30.2-11.7-58.5-32.9-79.7L668.3 317.9zM296.3-7.3c-30.2 0-58.5 11.7-79.7 32.9l-78.2 78.2c-21.2 21.2-32.9 49.5-32.9 79.7 0 30.2 11.7 58.5 32.9 79.7l189.3 189.3 24.9-24.9-189.4-189.2c-14.6-14.6-22.6-34.1-22.6-54.9 0-20.8 8-40.3 22.6-54.9l78.2-78.2c14.6-14.6 34.1-22.6 54.9-22.6 20.8 0 40.3 8 54.9 22.6l189.3 189.3 24.9-24.9L376 25.6c-21.2-21.2-49.5-32.9-79.7-32.9z" horiz-adv-x="1024" />
<glyph glyph-name="cloudlogo" unicode="&#58882;" d="M1214.102588 508.807529C1170.130824 729.569882 981.955765 896 752.941176 896 571.030588 896 414.117647 790.407529 335.691294 636.807529 147.456 617.592471 0 454.415059 0 256 0 44.815059 169.441882-128 376.470588-128h815.706353C1364.690824-128 1505.882353 15.962353 1505.882353 191.969882c0 169.622588-128.602353 307.2-291.779765 316.837647zM1195.670588-7.529412H373.458824C234.315294-7.529412 120.470588 109.929412 120.470588 253.470118c0 143.600941 113.844706 261.059765 252.988236 261.059764h44.272941C458.872471 664.576 591.691294 775.529412 752.941176 775.529412c192.873412 0 347.858824-159.864471 347.858824-358.881883V384h94.870588c104.327529 0 189.741176-88.124235 189.741177-195.764706S1299.998118-7.529412 1195.670588-7.529412z" horiz-adv-x="1505" />
<glyph glyph-name="zhinengkefu" unicode="&#58939;" d="M874.5 558.2C830.1 676.9 715.3 762 581.5 762H439.7c-133.7 0-248.5-85.1-293-203.8-0.9-0.3-1.7-0.6-2.6-1l-3.4 141.1c13.4 5.4 22.9 18.6 22.9 33.9V804c0 20.1-16.5 36.6-36.6 36.6-20-0.1-36.5-16.5-36.5-36.6v-71.7c0-15.3 9.5-28.5 22.9-33.9l-3.9-160.5c-31.2-24.2-51.4-62.1-51.4-104.5v-109.8c0-57.4 37.1-106.6 88.6-124.8C191.2 80.1 306-5 439.7-5h141.8c133.7 0 248.5 85.1 293 203.8C925.9 217 963 266.2 963 323.6V433.4c0 57.5-37.1 106.7-88.5 124.8z m-33-224.9c0-74-60.5-134.5-134.5-134.5H314.2c-74 0-134.5 60.5-134.5 134.5v90.3c0 74 60.5 134.5 134.5 134.5H707c74 0 134.5-60.5 134.5-134.5v-90.3zM605.8 461l-81-133-100.2 196.2-78.4-154.9L226 350.8l142.2-5.2 55.5 93.7 102-191.1L608 393.9l40.3-49.3 121.5 5.9-106.4 17.3z" horiz-adv-x="1024" />
@ -44,6 +50,9 @@ Created by iconfont
<glyph glyph-name="Express_transportation" unicode="&#58885;" d="M407.104 269.76a70.56 70.56 0 0 1-71.296-71.136c0-40.32 31.392-66.88 71.296-66.88 35.648 0 66.528 26.56 66.528 66.88 0 39.808-30.88 71.136-66.528 71.136z m0-106.688c-22.336 0-35.648 17.504-35.648 35.552 0 17.504 13.312 35.552 35.648 35.552 17.568 0 30.88-18.048 30.88-35.552 0-18.048-13.312-35.552-30.88-35.552z m520.448 154.976v-70.56c0-22.304-13.312-35.552-30.816-35.552h-40.512c-4.256 44.064-44.192 79.552-88.864 79.552S682.816 256 678.496 211.936h-182.528c-9.056 44.064-44.16 79.552-88.864 79.552-48.928 0-84.576-35.488-93.632-79.552H269.28c-18.08 0-31.392 13.248-31.392 35.552v70.56h689.664zM767.36 269.76a70.56 70.56 0 0 1-71.328-71.136c0-40.32 31.36-66.88 71.328-66.88 39.872 0 71.328 26.56 71.328 66.88a70.592 70.592 0 0 1-71.328 71.136z m0-106.688c-17.632 0-35.616 17.504-35.616 35.552 0 17.504 18.016 35.552 35.616 35.552 17.568 0 35.68-18.048 35.68-35.552 0.032-18.048-18.112-35.552-35.68-35.552z m146.88 230.912l-129.312 128.48c-8.512 9.024-21.824 13.28-35.2 13.28h-62.176v26.528c0 18.048-18.048 31.328-35.68 31.328H269.28a30.464 30.464 0 0 1-31.392-31.328v-8.48l-137.824-13.824 240-30.752-244.224-26.56 244.224-31.296-244.224-21.792 142.048-27.072v-66.336h689.664v26.496c0 13.28-4.224 22.272-13.312 31.328z m-57.984-4.768h-124.512c-4.32 0-9.056 4.768-9.056 4.768v97.152c0 4.768 4.736 4.768 9.056 4.768h26.56l102.752-97.664c4.256-4.256 0-9.024-4.8-9.024z" horiz-adv-x="1024" />
<glyph glyph-name="bindNo" unicode="&#58880;" d="M564.7 281.73a24.55 24.55 0 0 1-34.72 0L334.65 86.4a89 89 0 1 0-125.89 125.88L404.1 407.62a24.55 24.55 0 0 1 0 34.72 24.55 24.55 0 0 1-34.72 0L174 247c-53.85-53.85-53.85-141.48 0-195.33a138.28 138.28 0 0 1 195.33 0L564.7 247a24.55 24.55 0 0 1 0 34.72zM848.85 724.27c-54 52.11-140.3 51.53-193.56-1.73L459.95 527.21a24.55 24.55 0 0 1 0-34.72 24.55 24.55 0 0 1 34.72 0L690 687.82a89.12 89.12 0 0 0 124.74 1.12c36-34.71 35.62-92.53 0.27-127.87L620.56 366.6a24.55 24.55 0 0 1 0-34.72 24.55 24.55 0 0 1 34.72 0L849.6 526.2c54.79 54.8 54.99 144.27-0.75 198.07z" horiz-adv-x="1024" />
</font>

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -46,9 +46,9 @@
<#if user?? && (user.roleAuthMap[ "A02_A01_A02_B09"]?? || user.admin)>
<div class="layui-btn-group ukefu-btn-group">
<button class="layui-btn layui-btn-small dropdown-menu">
<i class="kfont">&#xe672;</i> 导出
<i class="layui-icon">&#xe625;</i>
</button>
<i class="kfont">&#xe672;</i> 导出
<i class="layui-icon">&#xe625;</i>
</button>
<ul class="ukefu-dropdown-menu layui-anim layui-anim-upbit">
<li>
<a href="javascript:void(0)" id="batexp" title="导出联系人" target="_blank">

View File

@ -248,10 +248,12 @@
}
window.onbeforeunload = function(){
if(service_end == false){
<#if sessionConfig?? && sessionConfig.satisfaction?? && sessionConfig.satisfaction>
document.getElementById("diaShade").style.display = "block" ;
document.getElementById("dialogWrap").style.display = "block" ;
</#if>
// 因为满意度问卷在访客切换机器人坐席时也会弹出但是服务并未结束目前先去掉END事件时弹出满意度
// TODO #744 优化方案见 https://gitlab.chatopera.com/chatopera/cosinee/issues/744
// <#if sessionConfig?? && sessionConfig.satisfaction?? && sessionConfig.satisfaction>
// document.getElementById("diaShade").style.display = "block" ;
// document.getElementById("dialogWrap").style.display = "block" ;
// </#if>
return "您确定断开对话?" ;
}
}
@ -492,7 +494,7 @@
var hostname = location.hostname ;
var protocol = window.location.protocol.replace(/:/g,'');
var username = encodeURIComponent("${username}");
var socket = io(protocol + '://'+hostname+':${port}/im/user?userid=${userid!''}&orgi=${orgi!''}&session=${sessionid!''}&appid=${appid!''}&osname=${(osname!'')?url}&browser=${(browser!'')?url}<#if skill??>&skill=${skill}</#if><#if username??>&nickname='+username+'</#if><#if agent??>&agent=${agent}</#if><#if title??>&title=${title?url}</#if><#if traceid??>&url=${url?url}</#if><#if traceid??>&traceid=${traceid}</#if>', {transports: ['websocket', 'polling']});
var socket = io(protocol + '://'+hostname+':${port}/im/chatbot?userid=${userid!''}&orgi=${orgi!''}&session=${sessionid!''}&appid=${appid!''}&osname=${(osname!'')?url}&browser=${(browser!'')?url}<#if skill??>&skill=${skill}</#if><#if username??>&nickname='+username+'</#if><#if agent??>&agent=${agent}</#if><#if title??>&title=${title?url}</#if><#if traceid??>&url=${url?url}</#if><#if traceid??>&traceid=${traceid}</#if><#if aiid??>&aiid=${aiid}</#if>', {transports: ['websocket', 'polling']});
socket.on('connect',function(){
<#if contacts?? && contacts.name??>
socket.emit('new', {
@ -517,11 +519,12 @@
if(data.messageType == "end"){
service_end = true ;
editor.readonly();
<#if sessionConfig?? && sessionConfig.satisfaction?? && sessionConfig.satisfaction>
document.getElementById("diaShade").style.display = "block" ;
document.getElementById("dialogWrap").style.display = "block";
</#if>
// 因为满意度问卷在访客切换机器人坐席时也会弹出但是服务并未结束目前先去掉END事件时弹出满意度
// TODO #744 优化方案见 https://gitlab.chatopera.com/chatopera/cosinee/issues/744
// <#if sessionConfig?? && sessionConfig.satisfaction?? && sessionConfig.satisfaction>
// document.getElementById("diaShade").style.display = "block" ;
// document.getElementById("dialogWrap").style.display = "block";
// </#if>
}
if(document.getElementById("agentserviceid")){
document.getElementById("agentserviceid").value = data.agentserviceid ;

View File

@ -325,7 +325,7 @@
var hostname = location.hostname ;
var protocol = window.location.protocol.replace(/:/g,'');
var username = encodeURIComponent("${username}");
var socket = io(protocol + '://'+hostname+':${port}/im/user?userid=${userid!''}&orgi=${orgi!''}&session=${sessionid!''}&appid=${appid!''}&osname=${(osname!'')?url}&browser=${(browser!'')?url}<#if skill??>&skill=${skill}</#if><#if username??>&nickname='+username+'</#if><#if agent??>&agent=${agent}</#if><#if title??>&title=${title?url}</#if><#if traceid??>&url=${url?url}</#if><#if traceid??>&traceid=${traceid}</#if>', {transports: ['websocket', 'polling']});
var socket = io(protocol + '://'+hostname+':${port}/im/chatbot?userid=${userid!''}&orgi=${orgi!''}&session=${sessionid!''}&appid=${appid!''}&osname=${(osname!'')?url}&browser=${(browser!'')?url}<#if skill??>&skill=${skill}</#if><#if username??>&nickname='+username+'</#if><#if agent??>&agent=${agent}</#if><#if title??>&title=${title?url}</#if><#if traceid??>&url=${url?url}</#if><#if traceid??>&traceid=${traceid}</#if><#if aiid??>&aiid=${aiid}</#if>', {transports: ['websocket', 'polling']});
socket.on('connect',function(){
//service.sendRequestMessage();
//output('<span id="callOutConnect-message">'+ new Date().format("yyyy-MM-dd hh:mm:ss") + ' 开始沟通' +'</span>' , 'message callOutConnect-message');

View File

@ -593,13 +593,15 @@
console.log("[status] data", data);
if(data.messageType == "end"){
service_end = true ;
service_end = true;
editor.readonly();
<#if sessionConfig?? && sessionConfig.satisfaction?? && sessionConfig.satisfaction>
document.getElementById("diaShade").style.display = "block";
document.getElementById("dialogWrap").style.display = "block";
isAgentEnds = true;
</#if>
// 因为满意度问卷在访客切换机器人坐席时也会弹出但是服务并未结束目前先去掉END事件时弹出满意度
// TODO 优化方案见 https://gitlab.chatopera.com/chatopera/cosinee/issues/744
// <#if sessionConfig?? && sessionConfig.satisfaction?? && sessionConfig.satisfaction>
// document.getElementById("diaShade").style.display = "block";
// document.getElementById("dialogWrap").style.display = "block";
// isAgentEnds = true;
// </#if>
} else if(data.messageType == "text"){
service_end = false;
editor.readonly(false);

View File

@ -33,7 +33,7 @@
</td>
<td>${black.agentuser!''}${black.userid!''}</td>
<td>${black.createtime!''}</td>
<td>${black.createtime?string("yyyy-MM-dd HH:mm:ss")}</td>
<td><#if black.endtime??>${black.endtime?string("yyyy-MM-dd HH:mm:ss")}<#else>永久</#if></td>
<td>
${(black.chattime/(1000*60*60))?string('00')}:${((black.chattime%(1000*60*60))/(1000*60))?string('00')}:${((black.chattime%(1000*60))/(1000))?string('00')}

View File

@ -7164,12 +7164,6 @@ INSERT INTO `uk_sysdic` VALUES ('402881ef612b1f5b01612ce85460057c', '呼叫中
INSERT INTO `uk_sysdic` VALUES ('402881ef612b1f5b01612ce88154057d', '已处理呼叫中心', 'pub', 'A08_A03_A04', NULL, 'auth', '402881ef612b1f5b01612cdce2a4056f', NULL, NULL, '&#x756e646566696e6564;', NULL, NULL, '297e8c7b455798280145579c73e501c1', '2018-01-25 18:41:30', NULL, 0, 0, '402888815d2fe37f015d2fe75cc80002', 0, 0, '/apps/callcenter/processed/index.html', 'webim', '3', NULL, 'left');
INSERT INTO `uk_sysdic` VALUES ('402881ef612b1f5b01612ce8b990057e', '在线坐席', 'pub', 'A08_A04_A01', NULL, 'auth', '402881ef612b1f5b01612cdd1e930570', NULL, NULL, '&#x756e646566696e6564;', NULL, NULL, '297e8c7b455798280145579c73e501c1', '2018-01-25 18:41:44', NULL, 0, 0, '402888815d2fe37f015d2fe75cc80002', 0, 0, '/service/agent/index.html', 'webim', '3', NULL, 'left');
INSERT INTO `uk_sysdic` VALUES ('402881ef612b1f5b01612ce8e6a2057f', '全部坐席', 'pub', 'A08_A04_A02', NULL, 'auth', '402881ef612b1f5b01612cdd1e930570', NULL, NULL, '&#x756e646566696e6564;', NULL, NULL, '297e8c7b455798280145579c73e501c1', '2018-01-25 18:41:56', NULL, 0, 0, '402888815d2fe37f015d2fe75cc80002', 0, 0, '/service/user/index.html', 'webim', '3', NULL, 'left');
INSERT INTO `uk_sysdic` VALUES ('402881ef612b1f5b01612ce964ae0580', '智能机器人', 'pub', 'A09_A01', NULL, 'auth', '402881ef612b1f5b01612cc602450546', NULL, NULL, '&#x756e646566696e6564;', NULL, NULL, '297e8c7b455798280145579c73e501c1', '2018-01-25 18:42:28', NULL, 0, 0, '402888815d2fe37f015d2fe75cc80002', 0, 0, 'javascript:;', 'webim', '2', NULL, 'left');
INSERT INTO `uk_sysdic` VALUES ('402881ef612b1f5b01612ce996f80581', '语料库配置', 'pub', 'A09_A02', NULL, 'auth', '402881ef612b1f5b01612cc602450546', NULL, NULL, '&#x756e646566696e6564;', NULL, NULL, '297e8c7b455798280145579c73e501c1', '2018-01-25 18:42:41', NULL, 0, 0, '402888815d2fe37f015d2fe75cc80002', 0, 0, 'javascript:;', 'webim', '2', NULL, 'left');
INSERT INTO `uk_sysdic` VALUES ('402881ef612b1f5b01612ce9d8150582', '机器人管理', 'pub', 'A09_A01_A01', NULL, 'auth', '402881ef612b1f5b01612ce964ae0580', NULL, NULL, '&#x756e646566696e6564;', NULL, NULL, '297e8c7b455798280145579c73e501c1', '2018-01-25 18:42:58', NULL, 0, 0, '402888815d2fe37f015d2fe75cc80002', 0, 0, '/apps/xiaoe/index.html', 'webim', '3', NULL, 'left');
INSERT INTO `uk_sysdic` VALUES ('402881ef612b1f5b01612ceaff050587', '对话场景', 'pub', 'A09_A02_A03', NULL, 'auth', '402881ef612b1f5b01612ce996f80581', NULL, NULL, '&#x756e646566696e6564;', NULL, NULL, '297e8c7b455798280145579c73e501c1', '2018-01-25 18:44:13', NULL, 0, 0, '402888815d2fe37f015d2fe75cc80002', 0, 0, '/apps/xiaoe/scene.html', 'webim', '3', NULL, 'left');
INSERT INTO `uk_sysdic` VALUES ('402881ef612b1f5b01612ced9d5f0588', '知识维护', 'pub', 'A09_A02_A01', NULL, 'auth', '402881ef612b1f5b01612ce996f80581', NULL, NULL, '&#x756e646566696e6564;', NULL, NULL, '297e8c7b455798280145579c73e501c1', '2018-01-25 18:47:05', NULL, 0, 0, '402888815d2fe37f015d2fe75cc80002', 0, 0, '/apps/xiaoe/knowledge.html', 'webim', '3', NULL, 'left');
INSERT INTO `uk_sysdic` VALUES ('402881ef612b1f5b01612cedca5e0589', '词库配置', 'pub', 'A09_A02_A02', NULL, 'auth', '402881ef612b1f5b01612ce996f80581', NULL, NULL, '&#x756e646566696e6564;', NULL, NULL, '297e8c7b455798280145579c73e501c1', '2018-01-25 18:47:16', NULL, 0, 0, '402888815d2fe37f015d2fe75cc80002', 0, 0, '/apps/xiaoe/words.html', 'webim', '3', NULL, 'left');
INSERT INTO `uk_sysdic` VALUES ('402881ef612b1f5b01612cee4fbb058a', '通话信息', 'pub', 'A10_A01', NULL, 'auth', '402881ef612b1f5b01612cc626f90547', NULL, NULL, '&#x756e646566696e6564;', NULL, NULL, '297e8c7b455798280145579c73e501c1', '2018-01-25 18:47:50', NULL, 0, 0, '402888815d2fe37f015d2fe75cc80002', 0, 0, 'javascript:;', 'webim', '2', NULL, 'left');
INSERT INTO `uk_sysdic` VALUES ('402881ef612b1f5b01612cee80ed058b', '坐席信息', 'pub', 'A10_A02', NULL, 'auth', '402881ef612b1f5b01612cc626f90547', NULL, NULL, '&#x756e646566696e6564;', NULL, NULL, '297e8c7b455798280145579c73e501c1', '2018-01-25 18:48:03', NULL, 0, 0, '402888815d2fe37f015d2fe75cc80002', 0, 0, 'javascript:;', 'webim', '2', NULL, 'left');
INSERT INTO `uk_sysdic` VALUES ('402881ef612b1f5b01612ceeca0c058c', '历史通话', 'pub', 'A10_A01_A01', NULL, 'auth', '402881ef612b1f5b01612cee4fbb058a', NULL, NULL, '&#x756e646566696e6564;', NULL, NULL, '297e8c7b455798280145579c73e501c1', '2018-01-25 18:48:22', NULL, 0, 0, '402888815d2fe37f015d2fe75cc80002', 0, 0, '/apps/callcenter/service/index.html', 'webim', '3', NULL, 'left');
@ -7945,6 +7939,61 @@ CREATE TABLE `uk_tableproperties` (
KEY `FKF8D747811BE44FF` (`FKPROPERTY`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT COMMENT='元数据字段表';
INSERT INTO `uk_tableproperties` (`ID`, `NAME`, `CODE`, `GROUPID`, `USERID`, `FIELDNAME`, `DATATYPECODE`, `DATATYPENAME`, `DBTABLEID`, `INDEXDATATYPE`, `PK`, `MODITS`, `INDEXFIELD`, `PLUGIN`, `ORGI`, `FKTABLE`, `FKPROPERTY`, `TABLENAME`, `viewtype`, `SORTINDEX`, `SYSTEMFIELD`, `INX`, `TOKEN`, `LENGTH`, `FIELDSTATUS`, `SELDATA`, `SELDATACODE`, `SELDATAKEY`, `SELDATAVALUE`, `SELDATATYPE`, `REFTBID`, `REFTPID`, `REFTYPE`, `REFTBNAME`, `REFTPNAME`, `REFTPTITLEFIELD`, `REFFK`, `DEFAULTSORT`, `DEFAULTVALUE`, `DEFAULTVALUETITLE`, `DEFAULTFIELDVALUE`, `MULTPARTFILE`, `UPLOADTYPE`, `cascadetype`, `title`, `DESCORDER`, `impfield`, `tokentype`, `phonenumber`, `phonetype`, `phonememo`, `secfield`, `secdistype`, `styletype`, `sysfield`)
VALUES
('402870876e5d9773016e5e95575801c2', '企(事)业单位名称', NULL, NULL, NULL, 'name', 0, 'text', '402870876e5d9773016e5e95575601c0', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_entcustomer', NULL, 100, 0, 1, 0, 255, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95575801c3', '单位性质', NULL, NULL, NULL, 'etype', 0, 'text', '402870876e5d9773016e5e95575601c0', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_entcustomer', NULL, 100, 0, 1, 0, 60, 1, 1, 'com.dic.customer.etype', NULL, NULL, '', '', NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95575b01c4', '客户类别', NULL, NULL, NULL, 'ekind', 0, 'text', '402870876e5d9773016e5e95575601c0', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_entcustomer', NULL, 100, 0, 1, 0, 60, 1, 1, 'com.dic.contacts.entype', NULL, NULL, '', '', NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95575c01c5', '客户级别', NULL, NULL, NULL, 'elevel', 0, 'text', '402870876e5d9773016e5e95575601c0', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_entcustomer', NULL, 100, 0, 1, 0, 60, 1, 1, 'com.dic.contacts.elevel', NULL, NULL, '', '', NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95575c01c8', '来源', NULL, NULL, NULL, 'esource', 0, 'text', '402870876e5d9773016e5e95575601c0', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_entcustomer', NULL, 100, 0, 1, 0, 64, 1, 1, 'com.dic.contacts.esource', NULL, NULL, '', '', NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95576101d1', '电子邮件', NULL, NULL, NULL, 'email', 0, 'text', '402870876e5d9773016e5e95575601c0', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_entcustomer', NULL, 100, 0, 1, 0, 128, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95576101d3', '办公电话', NULL, NULL, NULL, 'phone', 0, 'text', '402870876e5d9773016e5e95575601c0', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_entcustomer', NULL, 100, 0, 1, 0, 40, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95576201d8', '', NULL, NULL, NULL, 'province', 0, 'text', '402870876e5d9773016e5e95575601c0', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_entcustomer', NULL, 100, 0, 1, 0, 60, 1, 1, 'com.dic.address.area', NULL, NULL, '', '', NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95576201d9', '市区县', NULL, NULL, NULL, 'city', 0, 'text', '402870876e5d9773016e5e95575601c0', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_entcustomer', NULL, 100, 0, 1, 0, 60, 1, 1, 'com.dic.address.area', NULL, NULL, '', '', NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95576201db', '地址', NULL, NULL, NULL, 'address', 0, 'text', '402870876e5d9773016e5e95575601c0', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_entcustomer', NULL, 100, 0, 1, 0, 255, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95576301e3', '所属行业', NULL, NULL, NULL, 'industry', 0, 'text', '402870876e5d9773016e5e95575601c0', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_entcustomer', NULL, 100, 0, 1, 0, 60, 1, 1, 'com.dic.contacts.industry', NULL, NULL, '', '', NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95576301e4', '效力状态', NULL, NULL, NULL, 'validstatus', 0, 'text', '402870876e5d9773016e5e95575601c0', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_entcustomer', NULL, 100, 0, 1, 0, 50, 1, 1, 'com.dic.contacts.validstatus', NULL, NULL, '', '', NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95576301ef', '描述', NULL, NULL, NULL, 'description', 0, 'text', '402870876e5d9773016e5e95575601c0', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_entcustomer', NULL, 100, 0, 1, 0, 65535, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95576401f0', '创建人', NULL, NULL, NULL, 'creater', 0, 'text', '402870876e5d9773016e5e95575601c0', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_entcustomer', NULL, 100, 0, 1, 0, 60, 1, 0, '', NULL, NULL, '', 'userdata', NULL, NULL, NULL, NULL, NULL, 1, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95576401f4', '修改时间', NULL, NULL, NULL, 'updatetime', 0, 'datetime', '402870876e5d9773016e5e95575601c0', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_entcustomer', NULL, 100, 0, 1, 0, 19, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95576401f6', '创建时间', NULL, NULL, NULL, 'createtime', 0, 'datetime', '402870876e5d9773016e5e95575601c0', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_entcustomer', NULL, 100, 0, 1, 0, 19, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95576401f9', '分享给', NULL, NULL, NULL, 'shares', 0, 'text', '402870876e5d9773016e5e95575601c0', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_entcustomer', NULL, 100, 0, 1, 0, 255, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95d6350206', '联系人性别', NULL, NULL, NULL, 'gender', 0, 'text', '402870876e5d9773016e5e95d6350204', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_contacts', NULL, 100, 0, 1, 0, 60, 1, 1, 'com.dic.contacts.sex', NULL, NULL, '', '', NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95d6350207', '出生日期', NULL, NULL, NULL, 'cusbirthday', 0, 'text', '402870876e5d9773016e5e95d6350204', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_contacts', NULL, 100, 0, 1, 0, 50, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95d6360209', '联系人类别', NULL, NULL, NULL, 'ckind', 0, 'text', '402870876e5d9773016e5e95d6350204', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_contacts', NULL, 100, 0, 1, 0, 60, 1, 1, 'com.dic.contacts.ckind', NULL, NULL, '', '', NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95d6370215', '电子邮件', NULL, NULL, NULL, 'email', 0, 'text', '402870876e5d9773016e5e95d6350204', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_contacts', NULL, 100, 0, 1, 0, 128, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95d6370217', '手机号码', NULL, NULL, NULL, 'mobileno', 0, 'text', '402870876e5d9773016e5e95d6350204', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_contacts', NULL, 100, 0, 1, 0, 40, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95d6370219', '办公电话', NULL, NULL, NULL, 'phone', 0, 'text', '402870876e5d9773016e5e95d6350204', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_contacts', NULL, 100, 0, 1, 0, 40, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95d6380222', '', NULL, NULL, NULL, 'province', 0, 'text', '402870876e5d9773016e5e95d6350204', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_contacts', NULL, 100, 0, 1, 0, 60, 1, 1, 'com.dic.address.area', NULL, NULL, '', '', NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95d6380223', '市(区)县', NULL, NULL, NULL, 'city', 0, 'text', '402870876e5d9773016e5e95d6350204', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_contacts', NULL, 100, 0, 1, 0, 60, 1, 1, 'com.dic.address.area', NULL, NULL, '', '', NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95d6380224', '地址', NULL, NULL, NULL, 'address', 0, 'text', '402870876e5d9773016e5e95d6350204', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_contacts', NULL, 100, 0, 1, 0, 65535, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95d63a0232', '最后联系时间', NULL, NULL, NULL, 'touchtime', 0, 'datetime', '402870876e5d9773016e5e95d6350204', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_contacts', NULL, 100, 0, 1, 0, 19, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95d63a0235', '创建人', NULL, NULL, NULL, 'creater', 0, 'text', '402870876e5d9773016e5e95d6350204', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_contacts', NULL, 100, 0, 1, 0, 60, 1, 0, '', NULL, NULL, '', 'userdata', NULL, NULL, NULL, NULL, NULL, 1, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95d63b0238', '联系人备注', NULL, NULL, NULL, 'memo', 0, 'text', '402870876e5d9773016e5e95d6350204', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_contacts', NULL, 100, 0, 1, 0, 255, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95d63b023a', '修改时间', NULL, NULL, NULL, 'updatetime', 0, 'datetime', '402870876e5d9773016e5e95d6350204', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_contacts', NULL, 100, 0, 1, 0, 19, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95d63b023d', '创建时间', NULL, NULL, NULL, 'createtime', 0, 'datetime', '402870876e5d9773016e5e95d6350204', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_contacts', NULL, 100, 0, 1, 0, 19, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95d63b023e', '联系人名称', NULL, NULL, NULL, 'name', 0, 'text', '402870876e5d9773016e5e95d6350204', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_contacts', NULL, 100, 0, 1, 0, 255, 1, 0, '', NULL, NULL, '', '', NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95d63c0241', '分享给', NULL, NULL, NULL, 'shares', 0, 'text', '402870876e5d9773016e5e95d6350204', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_contacts', NULL, 100, 0, 1, 0, 255, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e95d63c0247', 'skypeid', NULL, NULL, NULL, 'skypeid', 0, 'text', '402870876e5d9773016e5e95d6350204', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_contacts', NULL, 100, 0, 1, 0, 100, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e9cc6380272', '坐席用户名', NULL, NULL, NULL, 'agentusername', 0, 'text', '402870876e5d9773016e5e9cc6370270', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_servicesummary', NULL, 100, 0, 1, 0, 100, 1, 0, '', NULL, NULL, '', 'userdata', NULL, NULL, NULL, NULL, NULL, 1, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e9cc6380275', '服务次数', NULL, NULL, NULL, 'times', 0, 'number', '402870876e5d9773016e5e9cc6370270', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_servicesummary', NULL, 100, 0, 1, 0, 10, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e9cc6390278', '用户名', NULL, NULL, NULL, 'username', 0, 'text', '402870876e5d9773016e5e9cc6370270', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_servicesummary', NULL, 100, 0, 1, 0, 100, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e9cc63a027a', '渠道', NULL, NULL, NULL, 'channel', 0, 'text', '402870876e5d9773016e5e9cc6370270', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_servicesummary', NULL, 100, 0, 1, 0, 32, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e9cc63a027b', '登录时间', NULL, NULL, NULL, 'logindate', 0, 'datetime', '402870876e5d9773016e5e9cc6370270', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_servicesummary', NULL, 100, 0, 1, 0, 19, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e9cc63a027c', '服务类型', NULL, NULL, NULL, 'servicetype', 0, 'text', '402870876e5d9773016e5e9cc6370270', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_servicesummary', NULL, 100, 0, 1, 0, 65535, 1, 0, 'com.dic.db.type', NULL, NULL, '', '', NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e9cc63a027d', '是否预约', NULL, NULL, NULL, 'reservation', 0, 'text', '402870876e5d9773016e5e9cc6370270', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_servicesummary', NULL, 100, 0, 1, 0, 3, 1, 1, 'com.dic.yesorno', NULL, NULL, '', '', NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e9cc63b027e', '预约方式', NULL, NULL, NULL, 'reservtype', 0, 'text', '402870876e5d9773016e5e9cc6370270', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_servicesummary', NULL, 100, 0, 1, 0, 32, 1, 1, 'com.dic.summary.reservtype', NULL, NULL, '', '', NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e9cc63b027f', 'reservtime', NULL, NULL, NULL, 'reservtime', 0, 'datetime', '402870876e5d9773016e5e9cc6370270', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_servicesummary', NULL, 100, 0, 1, 0, 19, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e9cc63b0280', '电子邮件', NULL, NULL, NULL, 'email', 0, 'text', '402870876e5d9773016e5e9cc6370270', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_servicesummary', NULL, 100, 0, 1, 0, 100, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e9cc63b0281', '电话号码', NULL, NULL, NULL, 'phonenumber', 0, 'text', '402870876e5d9773016e5e9cc6370270', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_servicesummary', NULL, 100, 0, 1, 0, 32, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e9cc63b0282', '服务记录', NULL, NULL, NULL, 'summary', 0, 'text', '402870876e5d9773016e5e9cc6370270', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_servicesummary', NULL, 100, 0, 1, 0, 65535, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e9cc63b0284', '创建人', NULL, NULL, NULL, 'creater', 0, 'text', '402870876e5d9773016e5e9cc6370270', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_servicesummary', NULL, 100, 0, 1, 0, 32, 1, 0, '', NULL, NULL, '', 'userdata', NULL, NULL, NULL, NULL, NULL, 1, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e9cc63c0285', '创建时间', NULL, NULL, NULL, 'createtime', 0, 'datetime', '402870876e5d9773016e5e9cc6370270', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_servicesummary', NULL, 100, 0, 1, 0, 19, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e9cc63d028c', 'process', NULL, NULL, NULL, 'process', 0, 'text', '402870876e5d9773016e5e9cc6370270', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_servicesummary', NULL, 100, 0, 1, 0, 3, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e9cc63d028d', 'updateuser', NULL, NULL, NULL, 'updateuser', 0, 'text', '402870876e5d9773016e5e9cc6370270', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_servicesummary', NULL, 100, 0, 1, 0, 32, 1, 0, '', NULL, NULL, '', 'userdata', NULL, NULL, NULL, NULL, NULL, 1, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e9cc63d028e', 'updatetime', NULL, NULL, NULL, 'updatetime', 0, 'datetime', '402870876e5d9773016e5e9cc6370270', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_servicesummary', NULL, 100, 0, 1, 0, 19, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0),
('402870876e5d9773016e5e9cc63d028f', 'processmemo', NULL, NULL, NULL, 'processmemo', 0, 'text', '402870876e5d9773016e5e9cc6370270', NULL, 0, 0, NULL, NULL, 'cskefu', NULL, NULL, 'uk_servicesummary', NULL, 100, 0, 1, 0, 65535, 1, 0, NULL, NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, 1, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0);
-- ----------------------------
-- Table structure for uk_tabletask
-- ----------------------------
@ -7992,6 +8041,12 @@ CREATE TABLE `uk_tabletask` (
PRIMARY KEY (`ID`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT COMMENT='元数据信息表';
INSERT INTO `uk_tabletask` (`ID`, `NAME`, `SECURE`, `TASKSTATUS`, `TABLEDIRID`, `DBID`, `CODE`, `GROUPID`, `CREATER`, `CREATERNAME`, `TASKTYPE`, `TASKNAME`, `TASKPLAN`, `CONFIGURE`, `SECURECONF`, `USERID`, `PREVIEWTEMPLET`, `LISTBLOCKTEMPLET`, `TABLENAME`, `TABLETYPE`, `STARTINDEX`, `UPDATETIME`, `UPDATETIMENUMBER`, `DATASQL`, `DATABASETASK`, `DRIVERPLUGIN`, `ORGI`, `WORKFLOW`, `FROMDB`, `tabtype`, `pid`, `secmenuid`, `reportid`, `eventname`, `tltemplet`, `timeline`, `tbversion`, `LASTUPDATE`, `CREATETIME`)
VALUES
('402870876e5d9773016e5e95575601c0', 'uk_entcustomer', NULL, NULL, '0', NULL, NULL, NULL, '402870876e4f5bdd016e4f77eb6f0075', 'xiaoxiao', NULL, 'uk_entcustomer', NULL, NULL, NULL, NULL, NULL, NULL, 'uk_entcustomer', '1', 0, '2019-11-12 15:47:53', 0, NULL, NULL, NULL, 'cskefu', 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, '2019-11-12 15:47:53'),
('402870876e5d9773016e5e95d6350204', 'uk_contacts', NULL, NULL, '0', NULL, NULL, NULL, '402870876e4f5bdd016e4f77eb6f0075', 'xiaoxiao', NULL, 'uk_contacts', NULL, NULL, NULL, NULL, NULL, NULL, 'uk_contacts', '1', 0, '2019-11-12 15:48:28', 0, NULL, NULL, NULL, 'cskefu', 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, '2019-11-12 15:48:28'),
('402870876e5d9773016e5e9cc6370270', 'uk_servicesummary', NULL, NULL, '0', NULL, NULL, NULL, '402870876e4f5bdd016e4f77eb6f0075', 'xiaoxiao', NULL, 'uk_servicesummary', NULL, NULL, NULL, NULL, NULL, NULL, 'uk_servicesummary', '1', 0, '2019-11-12 15:56:02', 0, NULL, NULL, NULL, 'cskefu', 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, '2019-11-12 15:56:02');
-- ----------------------------
-- Table structure for uk_tag
-- ----------------------------

View File

@ -0,0 +1,28 @@
# 春松客服智能机器人插件
## 使用介绍
[春松客服机器人客服文档](https://docs.chatopera.com/products/cskefu/work-chatbot.html)
## 安装插件
```
./scripts/install.sh
```
## 卸载插件
```
./scripts/uninstall.sh
```
## 开源许可协议
Copyright (2018-2019) <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

View File

@ -0,0 +1,562 @@
/*
* Copyright (C) 2018-2019 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.plugins.chatbot;
import com.chatopera.bot.exception.ChatbotException;
import com.chatopera.cc.basic.Constants;
import com.chatopera.cc.basic.MainUtils;
import com.chatopera.cc.controller.Handler;
import com.chatopera.cc.controller.api.request.RestUtils;
import com.chatopera.cc.model.Chatbot;
import com.chatopera.cc.model.CousultInvite;
import com.chatopera.cc.model.SNSAccount;
import com.chatopera.cc.model.User;
import com.chatopera.cc.persistence.repository.ChatbotRepository;
import com.chatopera.cc.persistence.repository.ConsultInviteRepository;
import com.chatopera.cc.persistence.repository.SNSAccountRepository;
import com.chatopera.cc.persistence.repository.UserRepository;
import com.chatopera.cc.proxy.OnlineUserProxy;
import com.chatopera.cc.util.Menu;
import com.chatopera.cc.util.SystemEnvHelper;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.apache.commons.lang.StringUtils;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.net.MalformedURLException;
import java.util.Date;
import java.util.List;
/**
* 聊天机器人
* 请求聊天机器人服务
*/
@RestController
@RequestMapping("/api/chatbot")
public class ApiChatbotController extends Handler {
private final static Logger logger = LoggerFactory.getLogger(ApiChatbotController.class);
@Autowired
private ChatbotRepository chatbotRes;
@Autowired
private SNSAccountRepository snsAccountRes;
@Autowired
private UserRepository userRes;
@Autowired
private ConsultInviteRepository consultInviteRes;
private final static String botServiecProvider = SystemEnvHelper.getenv(
ChatbotConstants.BOT_PROVIDER, ChatbotConstants.DEFAULT_BOT_PROVIDER);
/**
* 聊天机器人
*
* @param request
* @param body
* @return
* @throws Exception
*/
@RequestMapping(method = RequestMethod.POST)
@Menu(type = "apps", subtype = "chatbot", access = true)
public ResponseEntity<String> operations(HttpServletRequest request, @RequestBody final String body) throws Exception {
final JsonObject j = (new JsonParser()).parse(body).getAsJsonObject();
logger.info("[chatbot] operations payload {}", j.toString());
JsonObject json = new JsonObject();
HttpHeaders headers = RestUtils.header();
final User logined = super.getUser(request);
final String orgi = logined.getOrgi();
if (!j.has("ops")) {
json.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_1);
json.addProperty(RestUtils.RESP_KEY_ERROR, "不合法的请求参数。");
} else {
switch (StringUtils.lowerCase(j.get("ops").getAsString())) {
case "create":
// TODO 支持将一个用户添加到多个部门后此处需要在Payload中传入部门ID , organ
// 2019-10-10 Wang Hai Liang
json = create(j, logined.getId(), logined.getOrgi());
break;
case "delete":
json = delete(j, logined.getId(), logined.getOrgi());
break;
case "fetch":
json = fetch(
j, logined.getId(), logined.isAdmin(), orgi, super.getP(request), super.getPs(request));
break;
case "update":
json = update(j);
break;
case "enable":
json = enable(j, true);
break;
case "disable":
json = enable(j, false);
break;
case "vacant":
json = vacant(j, orgi, logined.isAdmin());
break;
default:
json.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_2);
json.addProperty(RestUtils.RESP_KEY_ERROR, "不合法的操作。");
}
}
return new ResponseEntity<String>(json.toString(), headers, HttpStatus.OK);
}
/**
* 获取空缺聊天机器人的网站渠道列表
*
* @param j
* @param orgi
* @return
*/
private JsonObject vacant(final JsonObject j, String orgi, boolean isSuperuser) {
JsonObject resp = new JsonObject();
if (!isSuperuser) {
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_SUCC);
resp.addProperty(RestUtils.RESP_KEY_ERROR, "您不具有访问该资源的权限。");
return resp;
}
List<SNSAccount> records = snsAccountRes.findBySnstypeAndOrgi(Constants.CHANNEL_TYPE_WEBIM, orgi);
JsonArray ja = new JsonArray();
for (SNSAccount r : records) {
if (!chatbotRes.existsBySnsAccountIdentifierAndOrgi(r.getSnsid(), orgi)) {
JsonObject o = new JsonObject();
o.addProperty("id", r.getId());
o.addProperty("snsid", r.getSnsid());
o.addProperty("snsType", r.getSnstype());
o.addProperty("snsurl", r.getBaseURL());
ja.add(o);
}
}
resp.add("data", ja);
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_SUCC);
return resp;
}
/**
* Enable Chatbot
*
* @param j
* @return
*/
private JsonObject enable(JsonObject j, boolean isEnabled) {
JsonObject resp = new JsonObject();
if ((!j.has("id")) || StringUtils.isBlank(j.get("id").getAsString())) {
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_3);
resp.addProperty(RestUtils.RESP_KEY_ERROR, "不合法的操作id不能为空。");
return resp;
}
final String id = j.get("id").getAsString();
Chatbot c = chatbotRes.findOne(id);
if (c == null) {
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_4);
resp.addProperty(RestUtils.RESP_KEY_ERROR, "该聊天机器人不存在。");
return resp;
}
try {
com.chatopera.bot.sdk.Chatbot bot = new com.chatopera.bot.sdk.Chatbot(
c.getClientId(), c.getSecret(), botServiecProvider);
if (bot.exists()) {
c.setEnabled(isEnabled);
chatbotRes.save(c);
// 更新访客网站配置
CousultInvite invite = OnlineUserProxy.consult(c.getSnsAccountIdentifier(), c.getOrgi());
invite.setAi(isEnabled);
consultInviteRes.save(invite);
OnlineUserProxy.cacheConsult(invite);
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_SUCC);
resp.addProperty(RestUtils.RESP_KEY_DATA, "完成。");
} else {
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_7);
resp.addProperty(RestUtils.RESP_KEY_ERROR, "智能问答引擎不存在该聊天机器人,未能正确设置。");
}
} catch (MalformedURLException e) {
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_6);
resp.addProperty(RestUtils.RESP_KEY_DATA, "设置不成功,智能问答引擎地址不合法。");
} catch (ChatbotException e) {
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_5);
resp.addProperty(RestUtils.RESP_KEY_DATA, "设置不成功,智能问答引擎服务异常。");
}
return resp;
}
/**
* 更新聊天机器人
*
* @param j
* @return
*/
private JsonObject update(JsonObject j) throws Exception {
JsonObject resp = new JsonObject();
if (!j.has("id")) {
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_3);
resp.addProperty(RestUtils.RESP_KEY_ERROR, "非法参数id不能为空。");
return resp;
}
final String id = j.get("id").getAsString();
Chatbot c = chatbotRes.findOne(id);
if (c == null) {
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_4);
resp.addProperty(RestUtils.RESP_KEY_ERROR, "该聊天机器人不存在。");
return resp;
}
// update clientId and secret
if (j.has("clientId")) {
c.setClientId(j.get("clientId").getAsString());
} else {
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_3);
resp.addProperty(RestUtils.RESP_KEY_ERROR, "不合法的参数未传入【clientId】。");
return resp;
}
if (j.has("secret")) {
c.setSecret(j.get("secret").getAsString());
} else {
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_3);
resp.addProperty(RestUtils.RESP_KEY_ERROR, "不合法的参数未传入【secret】。");
return resp;
}
// 更新访客网站配置
CousultInvite invite = OnlineUserProxy.consult(c.getSnsAccountIdentifier(), c.getOrgi());
if (j.has("workmode") && Constants.CHATBOT_VALID_WORKMODELS.contains(j.get("workmode").getAsString())) {
c.setWorkmode(j.get("workmode").getAsString());
invite.setAifirst(StringUtils.equals(Constants.CHATBOT_CHATBOT_FIRST, c.getWorkmode()));
}
if (j.has("enabled")) {
boolean enabled = j.get("enabled").getAsBoolean();
c.setEnabled(enabled);
invite.setAi(enabled);
}
try {
com.chatopera.bot.sdk.Chatbot bot = new com.chatopera.bot.sdk.Chatbot(
c.getClientId(), c.getSecret(), botServiecProvider);
if (bot.exists()) {
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_SUCC);
JsonObject data = new JsonObject();
data.addProperty("id", c.getId());
resp.add(RestUtils.RESP_KEY_DATA, data);
resp.addProperty(RestUtils.RESP_KEY_MSG, "更新成功。");
JSONObject botDetails = bot.details();
c.setDescription(botDetails.getJSONObject("data").getString("description"));
c.setFallback(botDetails.getJSONObject("data").getString("fallback"));
c.setWelcome(botDetails.getJSONObject("data").getString("welcome"));
invite.setAisuccesstip(botDetails.getJSONObject("data").getString("welcome"));
c.setName(botDetails.getJSONObject("data").getString("name"));
invite.setAiname(c.getName());
} else {
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_6);
resp.addProperty(
RestUtils.RESP_KEY_ERROR,
"Chatopera云服务无法访问该机器人请确认【1】该服务器可以访问互联网【2】该聊天机器人已经创建【3】clientId和Secret正确设置。提示该机器人不存在请先创建机器人, 登录 https://bot.chatopera.com");
return resp;
}
} catch (ChatbotException e) {
logger.error("bot create error", e);
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_5);
resp.addProperty(
RestUtils.RESP_KEY_ERROR,
"Chatopera云服务无法访问该机器人请确认【1】该服务器可以访问互联网【2】该聊天机器人已经创建【3】clientId和Secret正确设置。");
return resp;
} catch (MalformedURLException e) {
logger.error("bot request error", e);
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_7);
resp.addProperty(RestUtils.RESP_KEY_ERROR, "更新智能问答引擎失败。" + e.toString());
return resp;
}
c.setUpdatetime(new Date());
chatbotRes.save(c);
consultInviteRes.save(invite);
OnlineUserProxy.cacheConsult(invite);
return resp;
}
/**
* 获取聊天机器人列表
*
* @param j
* @param id
* @param orgi
* @param p
* @param ps
* @return
*/
private JsonObject fetch(JsonObject j, String id, boolean isSuperuser, String orgi, int p, int ps) {
JsonObject resp = new JsonObject();
if (!isSuperuser) {
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_3);
resp.addProperty(RestUtils.RESP_KEY_ERROR, "当前登录用户不是管理员,无权访问机器人客服资源。");
return resp;
}
Page<Chatbot> records = chatbotRes.findWithPagination(
new PageRequest(p, ps, Sort.Direction.DESC, "createtime"));
JsonArray ja = new JsonArray();
for (Chatbot c : records) {
JsonObject o = new JsonObject();
o.addProperty("id", c.getId());
o.addProperty("name", c.getName());
o.addProperty("primaryLanguage", c.getPrimaryLanguage());
o.addProperty("description", c.getDescription());
o.addProperty("fallback", c.getFallback());
o.addProperty("welcome", c.getWelcome());
o.addProperty("workmode", c.getWorkmode());
o.addProperty("channel", c.getChannel());
o.addProperty("snsid", c.getSnsAccountIdentifier());
o.addProperty("enabled", c.isEnabled());
// SNSAccount
SNSAccount snsAccount = snsAccountRes.findBySnsidAndOrgi(c.getSnsAccountIdentifier(), orgi);
if (snsAccount == null) {
chatbotRes.delete(c); // 删除不存在snsAccount的机器人
continue; // 忽略不存在snsAccount的机器人
}
o.addProperty("snsurl", snsAccount.getBaseURL());
// 创建人
User user = userRes.findById(c.getCreater());
if (user != null) {
o.addProperty("creater", c.getCreater());
o.addProperty("creatername", user.getUname());
}
ja.add(o);
}
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_SUCC);
resp.add("data", ja);
resp.addProperty("size", records.getSize()); // 每页条数
resp.addProperty("number", records.getNumber()); // 当前页
resp.addProperty("totalPage", records.getTotalPages()); // 所有页
resp.addProperty("totalElements", records.getTotalElements()); // 所有检索结果数量
return resp;
}
/**
* 删除聊天机器人
*
* @param j
* @param uid
* @param orgi
* @return
*/
private JsonObject delete(final JsonObject j, final String uid, final String orgi) {
JsonObject resp = new JsonObject();
if ((!j.has("id")) || StringUtils.isBlank(j.get("id").getAsString())) {
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_3);
resp.addProperty(RestUtils.RESP_KEY_ERROR, "不合法的参数未传入id。");
return resp;
}
final String id = j.get("id").getAsString();
Chatbot c = chatbotRes.findOne(id);
if (c == null) {
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_3);
resp.addProperty(RestUtils.RESP_KEY_ERROR, "不合法的参数,不存在该聊天机器人。");
return resp;
}
// 更新访客网站配置
CousultInvite invite = OnlineUserProxy.consult(c.getSnsAccountIdentifier(), c.getOrgi());
if (invite != null) {
invite.setAi(false);
invite.setAiname(null);
invite.setAisuccesstip(null);
invite.setAifirst(false);
invite.setAiid(null);
consultInviteRes.save(invite);
OnlineUserProxy.cacheConsult(invite);
}
chatbotRes.delete(c);
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_SUCC);
resp.addProperty(RestUtils.RESP_KEY_DATA, "删除成功。");
return resp;
}
/**
* 创建聊天机器人
*
* @param j
* @param creater
* @param orgi
* @return
*/
private JsonObject create(final JsonObject j, final String creater, final String orgi) throws Exception {
JsonObject resp = new JsonObject();
String snsid = null;
String workmode = null;
String clientId = null;
String secret = null;
if ((!j.has("clientId")) || StringUtils.isBlank(j.get("clientId").getAsString())) {
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_3);
resp.addProperty(RestUtils.RESP_KEY_ERROR, "不合法的参数未传入【clientId】。");
return resp;
} else {
clientId = j.get("clientId").getAsString();
}
if ((!j.has("secret")) || StringUtils.isBlank(j.get("secret").getAsString())) {
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_3);
resp.addProperty(RestUtils.RESP_KEY_ERROR, "不合法的参数未传入【secret】。");
return resp;
} else {
secret = j.get("secret").getAsString();
}
if (!(j.has("workmode") && Constants.CHATBOT_VALID_WORKMODELS.contains(j.get("workmode").getAsString()))) {
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_3);
resp.addProperty(RestUtils.RESP_KEY_ERROR, "不合法的参数未传入有效【workmode】。");
return resp;
} else {
workmode = j.get("workmode").getAsString();
}
if ((!j.has("snsid")) || StringUtils.isBlank(j.get("snsid").getAsString())) {
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_3);
resp.addProperty(RestUtils.RESP_KEY_ERROR, "不合法的参数未传入【snsid】。");
return resp;
} else {
snsid = j.get("snsid").getAsString();
// #TODO 仅支持webim
if (!snsAccountRes.existsBySnsidAndSnstypeAndOrgi(snsid, Constants.CHANNEL_TYPE_WEBIM, orgi)) {
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_3);
resp.addProperty(RestUtils.RESP_KEY_ERROR, "不合法的参数不存在【snsid】对应的网站渠道。");
return resp;
}
if (chatbotRes.existsBySnsAccountIdentifierAndOrgi(snsid, orgi)) {
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_3);
resp.addProperty(RestUtils.RESP_KEY_ERROR, "不合法的参数该渠道【snsid】已经存在聊天机器人。");
return resp;
}
}
if (chatbotRes.existsByClientIdAndOrgi(clientId, orgi)) {
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_3);
resp.addProperty(RestUtils.RESP_KEY_ERROR, "不合法的参数,数据库中存在该聊天机器人。");
return resp;
}
try {
logger.info("create bot with url {}", botServiecProvider);
com.chatopera.bot.sdk.Chatbot bot = new com.chatopera.bot.sdk.Chatbot(clientId, secret, botServiecProvider);
if (bot.exists()) { // 该机器人存在clientId Secret配对成功
// 创建成功
Chatbot c = new Chatbot();
JSONObject botDetails = bot.details();
c.setId(MainUtils.getUUID());
c.setClientId(clientId);
c.setSecret(secret);
c.setBaseUrl(botServiecProvider);
c.setDescription(botDetails.getJSONObject("data").getString("description"));
c.setFallback(botDetails.getJSONObject("data").getString("fallback"));
c.setPrimaryLanguage(botDetails.getJSONObject("data").getString("primaryLanguage"));
c.setName(botDetails.getJSONObject("data").getString("name"));
c.setWelcome(botDetails.getJSONObject("data").getString("welcome"));
c.setCreater(creater);
c.setOrgi(orgi);
c.setChannel(Constants.CHANNEL_TYPE_WEBIM);
c.setSnsAccountIdentifier(snsid);
Date dt = new Date();
c.setCreatetime(dt);
c.setUpdatetime(dt);
c.setWorkmode(workmode);
// 默认不开启
boolean enabled = false;
c.setEnabled(enabled);
// 更新访客网站配置
CousultInvite invite = OnlineUserProxy.consult(c.getSnsAccountIdentifier(), c.getOrgi());
invite.setAi(enabled);
invite.setAifirst(StringUtils.equals(Constants.CHATBOT_CHATBOT_FIRST, workmode));
invite.setAiid(c.getId());
invite.setAiname(c.getName());
invite.setAisuccesstip(c.getWelcome());
consultInviteRes.save(invite);
OnlineUserProxy.cacheConsult(invite);
chatbotRes.save(c);
JsonObject data = new JsonObject();
data.addProperty("id", c.getId());
resp.add(RestUtils.RESP_KEY_DATA, data);
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_SUCC);
return resp;
} else {
// 创建失败
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_6);
resp.addProperty(
RestUtils.RESP_KEY_ERROR, "Chatopera云服务该机器人不存在请先创建机器人, 登录 https://bot.chatopera.com");
return resp;
}
} catch (ChatbotException e) {
logger.error("bot create error", e);
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_5);
resp.addProperty(
RestUtils.RESP_KEY_ERROR,
"Chatopera云服务无法访问该机器人请确认【1】该服务器可以访问互联网【2】该聊天机器人已经创建【3】clientId和Secret正确设置。");
return resp;
} catch (MalformedURLException e) {
logger.error("bot request error", e);
resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_4);
resp.addProperty(RestUtils.RESP_KEY_ERROR, "Chatopera云服务不合法的聊天机器人服务URL。");
return resp;
}
}
}

View File

@ -0,0 +1,21 @@
/*
* Copyright (C) 2018-2019 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.plugins.chatbot;
public class ChatbotConstants {
public static final String BOT_PROVIDER = "BOT_PROVIDER";
public static final String DEFAULT_BOT_PROVIDER = "https://bot.chatopera.com";
}

View File

@ -0,0 +1,152 @@
/*
* Copyright (C) 2018-2019 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.plugins.chatbot;
import com.chatopera.cc.controller.Handler;
import com.chatopera.cc.exception.CSKefuException;
import com.chatopera.cc.model.Chatbot;
import com.chatopera.cc.model.Organ;
import com.chatopera.cc.model.SNSAccount;
import com.chatopera.cc.model.User;
import com.chatopera.cc.persistence.repository.ChatbotRepository;
import com.chatopera.cc.persistence.repository.SNSAccountRepository;
import com.chatopera.cc.proxy.OrganProxy;
import com.chatopera.cc.proxy.UserProxy;
import com.chatopera.cc.util.Menu;
import com.chatopera.cc.util.SystemEnvHelper;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.util.List;
import java.util.Optional;
@Controller
@RequestMapping(value = "/apps/chatbot")
public class ChatbotController extends Handler {
private final static Logger logger = LoggerFactory.getLogger(ChatbotController.class);
@Autowired
private ChatbotRepository chatbotRes;
@Autowired
private SNSAccountRepository snsAccountRes;
@Autowired
private UserProxy userProxy;
@Autowired
private SNSAccountRepository snsAccountRepository;
private final static String botServiecProvider = SystemEnvHelper.getenv(
ChatbotConstants.BOT_PROVIDER, ChatbotConstants.DEFAULT_BOT_PROVIDER);
@RequestMapping(value = "/index")
@Menu(type = "chatbot", subtype = "index", access = true)
public ModelAndView index(ModelMap map, HttpServletRequest request, @Valid final String chatbotid) throws CSKefuException {
logger.info("[index] chatbot id {}", chatbotid);
ModelAndView view = request(super.createAppsTempletResponse("/apps/chatbot/index"));
List<Chatbot> chatbots = chatbotRes.findByOrgi(super.getOrgi(request));
Chatbot currentbot = null;
logger.info("[index] chatbot size {}", chatbots.size());
if (chatbots.size() > 0) {
view.addObject("chatbots", chatbots);
if (StringUtils.isNotBlank(chatbotid)) {
view.addObject("currentbotid", chatbotid);
boolean resolved = false;
for (final Chatbot b : chatbots) {
if (StringUtils.equals(b.getId(), chatbotid)) {
view.addObject("currentbot", b);
currentbot = b;
resolved = true;
break;
}
}
if (!resolved) {
// TODO 优化查到不到Bot的提示
throw new CSKefuException("Can not find target chatbot by id [" + chatbotid + "]");
}
} else {
currentbot = chatbots.get(0);
view.addObject("currentbotid", currentbot.getId());
view.addObject("currentbot", currentbot);
}
}
view.addObject("botServiecProvider", botServiecProvider);
// 增加当前bot的更多信息
if (currentbot != null) {
// 创建人
final User creator = userProxy.findOne(currentbot.getCreater());
if (creator != null) {
view.addObject("creatorname", creator.getUname());
}
// 隶属渠道
if (StringUtils.isNotBlank(currentbot.getSnsAccountIdentifier())) {
snsAccountRepository.findOneBySnsTypeAndSnsIdAndOrgi(
currentbot.getChannel(),
currentbot.getSnsAccountIdentifier(),
currentbot.getOrgi()).ifPresent(p -> {
view.addObject("snsAccountName", p.getName());
view.addObject("snsAccountId", p.getId());
});
}
}
return view;
}
@RequestMapping(value = "/edit")
@Menu(type = "chatbot", subtype = "index", access = true)
public ModelAndView eidt(ModelMap map, HttpServletRequest request, @Valid String id) {
User curruser = super.getUser(request);
ModelAndView view = request(super.createAppsTempletResponse("/apps/chatbot/edit"));
if (id != null) {
Chatbot c = chatbotRes.findOne(id);
SNSAccount snsAccount = snsAccountRes.findBySnsidAndOrgi(c.getSnsAccountIdentifier(), curruser.getOrgi());
view.addObject("snsurl", snsAccount.getBaseURL());
view.addObject("bot", c);
}
view.addObject("id", id);
view.addObject("botServiecProvider", botServiecProvider);
return view;
}
}

View File

@ -0,0 +1,375 @@
/*
* Copyright (C) 2018-2019 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.plugins.chatbot;
import com.chatopera.cc.acd.AutomaticServiceDist;
import com.chatopera.cc.basic.Constants;
import com.chatopera.cc.basic.MainContext;
import com.chatopera.cc.basic.MainUtils;
import com.chatopera.cc.model.*;
import com.chatopera.cc.persistence.repository.AgentUserRepository;
import com.chatopera.cc.persistence.repository.ChatbotRepository;
import com.chatopera.cc.persistence.repository.OnlineUserRepository;
import com.chatopera.cc.proxy.OnlineUserProxy;
import com.chatopera.cc.socketio.client.NettyClients;
import com.chatopera.cc.socketio.message.AgentStatusMessage;
import com.chatopera.cc.socketio.message.ChatMessage;
import com.chatopera.cc.socketio.message.Message;
import com.chatopera.cc.socketio.util.IMServiceUtils;
import com.chatopera.cc.util.IP;
import com.chatopera.cc.util.IPTools;
import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.OnConnect;
import com.corundumstudio.socketio.annotation.OnDisconnect;
import com.corundumstudio.socketio.annotation.OnEvent;
import org.apache.commons.lang.StringUtils;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import java.net.InetSocketAddress;
import java.util.Date;
public class ChatbotEventHandler {
private static final Logger logger = LoggerFactory.getLogger(ChatbotEventHandler.class);
protected SocketIOServer server;
private static AgentUserRepository agentUserRes;
private static OnlineUserRepository onlineUserRes;
private static ChatbotRepository chatbotRes;
private static ChatbotProxy chatbotProxy;
@Autowired
public ChatbotEventHandler(SocketIOServer server) {
this.server = server;
}
@OnConnect
public void onConnect(SocketIOClient client) {
try {
String user = client.getHandshakeData().getSingleUrlParam("userid");
String nickname = client.getHandshakeData().getSingleUrlParam("nickname");
String orgi = client.getHandshakeData().getSingleUrlParam("orgi");
String session = MainUtils.getContextID(client.getHandshakeData().getSingleUrlParam("session"));
String appid = client.getHandshakeData().getSingleUrlParam("appid");
String aiid = client.getHandshakeData().getSingleUrlParam("aiid");
logger.info(
"[onConnect] userid {}, nickname {}, session {}, appid {}, aiid {}", user, nickname, session, appid,
aiid);
client.set("aiid", aiid);
client.set("session", session);
client.set("userid", user);
client.set("appid", appid);
client.set("orgi", orgi);
Date now = new Date();
if (StringUtils.isNotBlank(user)) {
/**
* 加入到 缓存列表
*/
NettyClients.getInstance().putChatbotEventClient(user, client);
CousultInvite invite = OnlineUserProxy.consult(appid, orgi);
/**
* 更新坐席服务类型
*/
IMServiceUtils.shiftOpsType(user, orgi, MainContext.OptType.CHATBOT);
// send out tip
Message tip = new Message();
tip.setMessage("您正在使用机器人客服!");
tip.setMessageType(MainContext.MessageType.MESSAGE.toString());
tip.setCalltype(MainContext.CallType.IN.toString());
tip.setCreatetime(MainUtils.dateFormate.format(now));
client.sendEvent(MainContext.MessageType.STATUS.toString(), tip);
// send out welcome message
if (invite != null) {
Chatbot chatbot = getChatbotRes().findOne(invite.getAiid());
com.chatopera.bot.sdk.Chatbot bot = new com.chatopera.bot.sdk.Chatbot(
chatbot.getClientId(), chatbot.getSecret(), chatbot.getBaseUrl());
JSONObject details = bot.details();
// 发送欢迎语
if (details.has("rc") &&
details.getInt("rc") == 0) {
ChatMessage welcome = new ChatMessage();
String welcomeTextMessage = details.getJSONObject("data").getString("welcome");
if (StringUtils.isNotBlank(welcomeTextMessage)) {
welcome.setCalltype(MainContext.CallType.OUT.toString());
welcome.setAppid(appid);
welcome.setOrgi(orgi);
welcome.setAiid(aiid);
welcome.setMessage(welcomeTextMessage);
welcome.setTouser(user);
welcome.setMsgtype(MainContext.MessageType.MESSAGE.toString());
welcome.setUserid(user);
welcome.setUsername(invite.getAiname());
welcome.setUpdatetime(System.currentTimeMillis());
client.sendEvent(MainContext.MessageType.MESSAGE.toString(), welcome);
}
// 发送常见问题列表
JSONObject faqhotresp = bot.conversation(user, "__faq_hot_list");
logger.info("faqhot {}", faqhotresp.toString());
if (faqhotresp.getInt("rc") == 0) {
JSONObject faqhotdata = faqhotresp.getJSONObject("data");
if ((!faqhotdata.getBoolean("logic_is_fallback")) &&
faqhotdata.has("string") &&
faqhotdata.has("params")) {
ChatMessage faqhotmsg = new ChatMessage();
faqhotmsg.setCalltype(MainContext.CallType.OUT.toString());
faqhotmsg.setAppid(appid);
faqhotmsg.setOrgi(orgi);
faqhotmsg.setAiid(aiid);
faqhotmsg.setMessage(faqhotdata.getString("string"));
faqhotmsg.setExpmsg(faqhotdata.getJSONArray("params").toString());
faqhotmsg.setTouser(user);
faqhotmsg.setMsgtype(MainContext.MessageType.MESSAGE.toString());
faqhotmsg.setUserid(user);
faqhotmsg.setUsername(invite.getAiname());
faqhotmsg.setUpdatetime(System.currentTimeMillis());
client.sendEvent(MainContext.MessageType.MESSAGE.toString(), faqhotmsg);
}
}
}
}
InetSocketAddress address = (InetSocketAddress) client.getRemoteAddress();
String ip = MainUtils.getIpAddr(client.getHandshakeData().getHttpHeaders(), address.getHostString());
OnlineUser onlineUser = getOnlineUserRes().findOne(user);
if (onlineUser == null) {
onlineUser = new OnlineUser();
onlineUser.setAppid(appid);
if (StringUtils.isNotBlank(nickname)) {
onlineUser.setUsername(nickname);
} else {
onlineUser.setUsername(Constants.GUEST_USER + "_" + MainUtils.genIDByKey(user));
}
onlineUser.setSessionid(session);
onlineUser.setOptype(MainContext.OptType.CHATBOT.toString());
onlineUser.setUserid(user);
onlineUser.setId(user);
onlineUser.setOrgi(orgi);
onlineUser.setChannel(MainContext.ChannelType.WEBIM.toString());
onlineUser.setIp(ip);
onlineUser.setUpdatetime(now);
onlineUser.setLogintime(now);
onlineUser.setCreatetime(now);
IP ipdata = IPTools.getInstance().findGeography(ip);
onlineUser.setCity(ipdata.getCity());
onlineUser.setCountry(ipdata.getCountry());
onlineUser.setProvince(ipdata.getProvince());
onlineUser.setIsp(ipdata.getIsp());
onlineUser.setRegion(ipdata.getRegion());
onlineUser.setStatus(MainContext.OnlineUserStatusEnum.ONLINE.toString());
}
// 在线客服访客咨询记录
AgentUser agentUser = new AgentUser(
onlineUser.getId(),
MainContext.ChannelType.WEBIM.toString(), // callout
onlineUser.getId(),
onlineUser.getUsername(),
MainContext.SYSTEM_ORGI,
appid);
agentUser.setServicetime(now);
agentUser.setCreatetime(now);
agentUser.setUpdatetime(now);
agentUser.setSessionid(session);
agentUser.setRegion(onlineUser.getRegion());
// 聊天机器人处理的请求
agentUser.setOpttype(MainContext.OptType.CHATBOT.toString());
agentUser.setAgentno(aiid); // 聊天机器人ID
agentUser.setAgentname(invite != null ? invite.getAiname() : "机器人客服");
agentUser.setCity(onlineUser.getCity());
agentUser.setProvince(onlineUser.getProvince());
agentUser.setCountry(onlineUser.getCountry());
AgentService agentService = AutomaticServiceDist.processChatbotService(
invite != null ? invite.getAiname() : "机器人客服", agentUser, orgi);
agentUser.setAgentserviceid(agentService.getId());
// 标记为机器人坐席
agentUser.setChatbotops(true);
// 保存到MySQL
getAgentUserRes().save(agentUser);
getOnlineUserRes().save(onlineUser);
}
} catch (Exception e) {
logger.info("[onConnect] error", e);
}
}
// 添加 @OnDisconnect 事件客户端断开连接时调用刷新客户端信息
@OnDisconnect
public void onDisconnect(SocketIOClient client) {
String user = client.getHandshakeData().getSingleUrlParam("userid");
String orgi = client.getHandshakeData().getSingleUrlParam("orgi");
if (StringUtils.isNotBlank(user)) {
NettyClients.getInstance().removeChatbotEventClient(
user, MainUtils.getContextID(client.getSessionId().toString()));
OnlineUser onlineUser = MainContext.getCache().findOneOnlineUserByUserIdAndOrgi(user, orgi);
MainContext.getCache().findOneAgentUserByUserIdAndOrgi(user, orgi).ifPresent(p -> {
AutomaticServiceDist.processChatbotService(null, p, orgi);
MainContext.getCache().deleteAgentUserByUserIdAndOrgi(user, orgi);
MainContext.getCache().deleteOnlineUserByIdAndOrgi(user, orgi);
p.setStatus(MainContext.OnlineUserStatusEnum.OFFLINE.toString());
onlineUser.setStatus(MainContext.OnlineUserStatusEnum.OFFLINE.toString());
getAgentUserRes().save(p);
getOnlineUserRes().save(onlineUser);
});
}
client.disconnect();
}
// 消息接收入口网站有新用户接入对话
@OnEvent(value = "new")
public void onEvent(SocketIOClient client, AckRequest request, Message data) {
}
// 消息接收入口坐席状态更新
@OnEvent(value = "agentstatus")
public void onEvent(SocketIOClient client, AckRequest request, AgentStatusMessage data) {
logger.info("[onEvent] agentstatus: ", data.getMessage());
}
// 消息接收入口收发消息用户向机器人发送消息
@OnEvent(value = "message")
public void onEvent(SocketIOClient client, AckRequest request, ChatMessage data) {
String orgi = client.get("orgi");
String aiid = client.get("aiid");
String user = client.get("userid");
String sessionid = client.get("session");
String appid = client.get("appid");
logger.info("[onEvent] message: session {}, aiid {}, userid {}, dataType {}, appid {}, orgi {}", sessionid, aiid, user, data.getType(), appid, orgi);
// ignore event if dataType is not message.
if (!StringUtils.equals(data.getType(), Constants.IM_MESSAGE_TYPE_MESSAGE)) {
return;
}
MainContext.getCache().findOneAgentUserByUserIdAndOrgi(user, orgi).ifPresent(p -> {
/**
* 以下代码主要用于检查 访客端的字数限制
*/
CousultInvite invite = OnlineUserProxy.consult(data.getAppid(), data.getOrgi());
// ignore event if no invite found.
if (invite == null) {
return;
}
// ignore if Chatbot is turnoff.
if (!invite.isAi()) {
return;
}
Date now = new Date();
if (invite.getMaxwordsnum() > 0) {
if (StringUtils.isNotBlank(data.getMessage()) && data.getMessage().length() > invite.getMaxwordsnum()) {
data.setMessage(data.getMessage().substring(0, invite.getMaxwordsnum()));
}
} else if (StringUtils.isNotBlank(data.getMessage()) && data.getMessage().length() > 300) {
data.setMessage(data.getMessage().substring(0, 300));
}
data.setUsession(user); // 绑定唯一用户
data.setSessionid(sessionid);
data.setMessage(MainUtils.processEmoti(data.getMessage())); // 处理表情
data.setTouser(aiid);
data.setUsername(p.getUsername());
data.setAiid(aiid);
data.setAgentserviceid(p.getAgentserviceid());
data.setChannel(p.getChannel());
data.setContextid(p.getAgentserviceid()); // 一定要设置 ContextID
data.setCalltype(MainContext.CallType.IN.toString());
// 保存并发送消息给访客
getChatbotProxy().createTextMessage(
data,
MainContext.CallType.IN.toString());
// 更新访客咨询记录
p.setUpdatetime(now);
p.setLastmessage(now);
p.setLastmsg(data.getMessage());
getAgentUserRes().save(p);
// 发送消息给Bot
getChatbotProxy().publishMessage(data, Constants.CHATBOT_EVENT_TYPE_CHAT);
});
}
/**
* Lazy load
*
* @return
*/
private AgentUserRepository getAgentUserRes() {
if (agentUserRes == null) {
agentUserRes = MainContext.getContext().getBean(AgentUserRepository.class);
}
return agentUserRes;
}
/**
* Lazy load
*
* @return
*/
private ChatbotProxy getChatbotProxy() {
if (chatbotProxy == null) {
chatbotProxy = MainContext.getContext().getBean(ChatbotProxy.class);
}
return chatbotProxy;
}
private OnlineUserRepository getOnlineUserRes() {
if (onlineUserRes == null) {
onlineUserRes = MainContext.getContext().getBean(OnlineUserRepository.class);
}
return onlineUserRes;
}
private ChatbotRepository getChatbotRes() {
if (chatbotRes == null) {
chatbotRes = MainContext.getContext().getBean(ChatbotRepository.class);
}
return chatbotRes;
}
}

View File

@ -0,0 +1,147 @@
/*
* Copyright (C) 2018-2019 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.plugins.chatbot;
import com.chatopera.bot.exception.ChatbotException;
import com.chatopera.cc.basic.MainContext;
import com.chatopera.cc.cache.Cache;
import com.chatopera.cc.controller.api.request.RestUtils;
import com.chatopera.cc.socketio.message.ChatMessage;
import com.chatopera.cc.model.Chatbot;
import com.chatopera.cc.persistence.repository.AgentUserRepository;
import com.chatopera.cc.persistence.repository.ChatbotRepository;
import com.chatopera.cc.basic.Constants;
import com.chatopera.cc.util.SerializeUtil;
import com.chatopera.cc.util.SystemEnvHelper;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
import java.net.MalformedURLException;
/**
* 发送消息给聊天机器人并处理返回结果
*/
@Component
public class ChatbotEventSubscription {
private final static Logger logger = LoggerFactory.getLogger(ChatbotEventSubscription.class);
@Autowired
private Cache cache;
@Autowired
private AgentUserRepository agentUserRes;
@Autowired
private ChatbotRepository chatbotRes;
private final static String botServiecProvider = SystemEnvHelper.getenv(
ChatbotConstants.BOT_PROVIDER, ChatbotConstants.DEFAULT_BOT_PROVIDER);
@Autowired
private ChatbotProxy chatbotProxy;
/**
* 接收发送消息给聊天机器人的请求
*
* @param payload
*/
@JmsListener(destination = Constants.INSTANT_MESSAGING_MQ_QUEUE_CHATBOT, containerFactory = "jmsListenerContainerQueue")
public void onMessage(final String payload) {
ChatMessage message = SerializeUtil.deserialize(payload);
try {
chat(message);
} catch (MalformedURLException e) {
logger.error("[onMessage] error", e);
} catch (ChatbotException e) {
logger.error("[onMessage] error", e);
}
}
private void chat(final ChatMessage request) throws MalformedURLException, ChatbotException, JSONException {
Chatbot c = chatbotRes
.findOne(request.getAiid());
logger.info(
"[chat] chat request baseUrl {}, chatbot {}, fromUserId {}, textMessage {}", botServiecProvider, c.getName(),
request.getUserid(), request.getMessage());
// Get response from Conversational Engine.
com.chatopera.bot.sdk.Chatbot bot = new com.chatopera.bot.sdk.Chatbot(
c.getClientId(), c.getSecret(), botServiecProvider);
JSONObject result = bot.conversation(request.getUserid(), request.getMessage());
// parse response
if (result != null) {
logger.info("[chat] chat response {}", result.toString());
if (result.getInt(RestUtils.RESP_KEY_RC) == 0) {
// reply
JSONObject data = result.getJSONObject("data");
ChatMessage resp = new ChatMessage();
resp.setCalltype(MainContext.CallType.OUT.toString());
resp.setAppid(resp.getAppid());
resp.setOrgi(request.getOrgi());
resp.setAiid(request.getAiid());
resp.setMessage(data.getString("string"));
resp.setTouser(request.getUserid());
resp.setAgentserviceid(request.getAgentserviceid());
resp.setMsgtype(request.getMsgtype());
resp.setUserid(request.getUserid());
resp.setType(request.getType());
resp.setChannel(request.getChannel());
if (data.has("params")) {
resp.setExpmsg(data.get("params").toString());
}
resp.setContextid(request.getContextid());
resp.setSessionid(request.getSessionid());
resp.setUsession(request.getUsession());
resp.setUsername(c.getName());
resp.setUpdatetime(System.currentTimeMillis());
// 更新聊天机器人累计值
updateAgentUserWithRespData(request.getUserid(), request.getOrgi(), data);
// 保存并发送
chatbotProxy.saveAndPublish(resp);
} else {
logger.warn("[chat] can not get expected response {}", result.toString());
}
}
}
/**
* 根据聊天机器人返回数据更新agentUser
*
* @param userid
* @param data
*/
private void updateAgentUserWithRespData(final String userid, final String orgi, final JSONObject data) throws JSONException {
cache.findOneAgentUserByUserIdAndOrgi(userid, orgi).ifPresent(p -> {
p.setChatbotround(p.getChatbotround() + 1);
if (data.has("logic_is_unexpected") && data.getBoolean("logic_is_unexpected")) {
p.setChatbotlogicerror(p.getChatbotlogicerror() + 1);
}
agentUserRes.save(p);
});
}
}

View File

@ -0,0 +1,180 @@
/*
* Copyright (C) 2018-2019 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.plugins.chatbot;
import com.chatopera.cc.activemq.BrokerPublisher;
import com.chatopera.cc.basic.MainContext;
import com.chatopera.cc.cache.Cache;
import com.chatopera.cc.socketio.client.NettyClients;
import com.chatopera.cc.socketio.message.ChatMessage;
import com.chatopera.cc.model.Chatbot;
import com.chatopera.cc.socketio.message.Message;
import com.chatopera.cc.persistence.repository.ChatMessageRepository;
import com.chatopera.cc.persistence.repository.ChatbotRepository;
import com.chatopera.cc.basic.Constants;
import com.chatopera.cc.util.SerializeUtil;
import com.chatopera.cc.util.SystemEnvHelper;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class ChatbotProxy {
private final static Logger logger = LoggerFactory.getLogger(ChatbotProxy.class);
private final static String botServiecProvider = SystemEnvHelper.getenv(
ChatbotConstants.BOT_PROVIDER, ChatbotConstants.DEFAULT_BOT_PROVIDER);
@Autowired
private BrokerPublisher brokerPublisher;
@Autowired
private ChatbotRepository chatbotRes;
@Autowired
private ChatMessageRepository chatMessageRes;
@Autowired
private Cache cache;
/**
* publish Message into ActiveMQ
*
* @param data
* @param eventType
*/
public void publishMessage(final ChatMessage data, final String eventType) {
logger.info("[publishMessage] eventType {}", eventType);
brokerPublisher.send(Constants.INSTANT_MESSAGING_MQ_QUEUE_CHATBOT, SerializeUtil.serialize(data));
}
/**
* 使用chatbotID得到snsid
*
* @param chatbotID
* @return
*/
public static String resolveSnsidWithChatbotID(String chatbotID, String clientId) {
return StringUtils.remove(chatbotID, clientId.toLowerCase() + "_");
}
/**
* @param data
* @param direction
* @return
*/
public Message createMessage(ChatMessage data, String direction) {
if (!cache.findOneAgentUserByUserIdAndOrgi(data.getUserid(), data.getOrgi()).isPresent()) {
return null;
}
// 设置发送消息体
Message outMessage = new Message();
outMessage.setMessage(data.getMessage());
outMessage.setMessageType(data.getMsgtype());
outMessage.setCalltype(direction);
outMessage.setAgentUser(null);
outMessage.setSnsAccount(null);
if (StringUtils.isNotBlank(data.getSuggestmsg())) {
outMessage.setSuggest(data.getSuggest());
}
outMessage.setContextid(data.getContextid());
// FIXME 设置onlineUserName等信息
// outMessage.setFromUser(data.getUserid());
// outMessage.setToUser(data.getTouser());
outMessage.setChannelMessage(data);
outMessage.setCreatetime(Constants.DISPLAY_DATE_FORMATTER.format(data.getCreatetime()));
/**
* 保存消息
*/
chatMessageRes.save(data);
//将消息发送给 访客
NettyClients.getInstance().sendChatbotEventMessage(
data.getUserid(), MainContext.MessageType.MESSAGE.toString(), data);
return outMessage;
}
/**
* 发送聊天机器人消息
*
* @param data
* @param appid
* @param channel
* @param direction
* @param chatype
* @param msgtype
* @param userid
* @return
*/
public Message createMessage(
final ChatMessage data,
final String appid,
final String channel,
final String direction,
final String chatype,
final String msgtype,
final String userid,
final String orgi) {
final Chatbot c = chatbotRes.findBySnsAccountIdentifierAndOrgi(appid, orgi);
if (c == null) // ignore event if chatbot not exist.
{
return null;
}
data.setAiid(c.getId());
data.setOrgi(orgi);
data.setUserid(userid);
data.setAgentserviceid(data.getContextid());
data.setChatype(chatype);
data.setChannel(channel);
data.setMsgtype(msgtype);
data.setUsession(data.getUserid()); //agentUser作为 session id
data.setCalltype(direction);
data.setUpdatetime(System.currentTimeMillis());
return createMessage(data, direction);
}
/**
* 发送文字消息
*
* @param data
* @param direction
* @return
*/
public Message createTextMessage(final ChatMessage data, final String direction) {
data.setMsgtype(MainContext.MediaType.TEXT.toString());
return createMessage(data, direction);
}
/**
* 保存到数据库发送到ChatMessage
*
* @param resp
*/
public void saveAndPublish(final ChatMessage resp) {
NettyClients.getInstance().sendChatbotEventMessage(
resp.getUserid(), MainContext.MessageType.MESSAGE.toString(), resp);
chatMessageRes.save(resp);
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (C) 2018-2019 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.plugins.chatbot;
import com.chatopera.cc.basic.plugins.IPluginDescriptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
/**
* 定义Plugin存在
*/
public class PluginDescriptor implements IPluginDescriptor {
private final static Logger logger = LoggerFactory.getLogger(PluginDescriptor.class);
private final static String pluginName = "chatbot";
/**
* 获取消息服务的Bean的名字
* 当该方法存在时加载到消息处理的调用栈 PeerSyncIM
*
* @return
*/
@Override
public String getPluginName() {
return pluginName;
}
@Override
public String getIOEventHandler() {
return ChatbotEventHandler.class.getName();
}
@Override
public Map<String, String> getEnvironmentVariables() {
Map<String, String> env = new HashMap<>();
env.put(ChatbotConstants.BOT_PROVIDER, "https://bot.chatopera.com");
return env;
}
}

View File

@ -0,0 +1,67 @@
#! /bin/bash
###########################################
# Install Plugin
# Copyright (2019) 北京华夏春松科技有限公司
###########################################
# constants
baseDir=$(cd `dirname "$0"`;pwd)
rootDir=$(cd -P $baseDir/..;pwd)
upperDir=$(cd -P $rootDir/..;pwd)
COSINEE_BASEDIR=$(cd -P $upperDir/../..;pwd)
pluginName=$(basename $rootDir)
# functions
# main
[ -z "${BASH_SOURCE[0]}" -o "${BASH_SOURCE[0]}" = "$0" ] || return
cd $rootDir/..
echo "[plugins] path" `pwd`
if [ -d $COSINEE_BASEDIR ]; then
PLUGINS_DIR=$COSINEE_BASEDIR/contact-center/app/src/main/java/com/chatopera/cc/plugins
echo "[plugin] link" $rootDir "as" $pluginName "..."
if [ ! -d $PLUGINS_DIR ]; then
mkdir -p $PLUGINS_DIR
fi
cd $PLUGINS_DIR
pwd
if [ -e $pluginName ]; then
rm -rf $pluginName
fi
echo "[plugin] link source codes"
ln -s $rootDir/classes $pluginName
# Install channel views
if [ -d $rootDir/views/channel/$pluginName ]; then
echo "[plugin] unlink views for channel"
VIEW_ADMIN_CHANNEL=$COSINEE_BASEDIR/contact-center/app/src/main/resources/templates/admin/channel
if [ -d $VIEW_ADMIN_CHANNEL/$pluginName ]; then
rm -rf $VIEW_ADMIN_CHANNEL/$pluginName
fi
cd $VIEW_ADMIN_CHANNEL
ln -s $rootDir/views/channel/$pluginName .
fi
# Install apps view
if [ -d $rootDir/views/apps/$pluginName ]; then
echo "[plugin] unlink views for apps"
VIEW_ADMIN_APPS=$COSINEE_BASEDIR/contact-center/app/src/main/resources/templates/apps
if [ -d $VIEW_ADMIN_APPS/$pluginName ]; then
rm -rf $VIEW_ADMIN_APPS/$pluginName
fi
cd $VIEW_ADMIN_APPS
ln -s $rootDir/views/apps/$pluginName .
fi
echo "[plugin] install done."
else
echo "[error] not found cosinee dir."
exit 2
fi

View File

@ -0,0 +1,56 @@
#! /bin/bash
###########################################
# Uninstall Plugin
# Copyright (2019) 北京华夏春松科技有限公司
###########################################
# constants
baseDir=$(cd `dirname "$0"`;pwd)
rootDir=$(cd -P $baseDir/..;pwd)
upperDir=$(cd -P $rootDir/..;pwd)
COSINEE_BASEDIR=$(cd -P $upperDir/../..;pwd)
pluginName=$(basename $rootDir)
# functions
# main
[ -z "${BASH_SOURCE[0]}" -o "${BASH_SOURCE[0]}" = "$0" ] || return
cd $rootDir/..
echo "[plugins] path" `pwd`
if [ -d $COSINEE_BASEDIR ]; then
PLUGINS_DIR=$COSINEE_BASEDIR/contact-center/app/src/main/java/com/chatopera/cc/plugins
echo "[plugin] unlink" $pluginName "..."
if [ ! -d $PLUGINS_DIR ]; then
mkdir -p $PLUGINS_DIR
fi
cd $PLUGINS_DIR
if [ -e $pluginName ]; then
rm -rf $pluginName
fi
if [ -d $rootDir/views/channel/$pluginName ]; then
echo "[plugin] unlink views for channel"
VIEW_ADMIN_CHANNEL=$COSINEE_BASEDIR/contact-center/app/src/main/resources/templates/admin/channel
if [ -d $VIEW_ADMIN_CHANNEL/$pluginName ]; then
rm -rf $VIEW_ADMIN_CHANNEL/$pluginName
fi
fi
# Install apps view
if [ -d $rootDir/views/apps/$pluginName ]; then
echo "[plugin] unlink views for apps"
VIEW_ADMIN_APPS=$COSINEE_BASEDIR/contact-center/app/src/main/resources/templates/apps
if [ -d $VIEW_ADMIN_APPS/$pluginName ]; then
rm -rf $VIEW_ADMIN_APPS/$pluginName
fi
fi
echo "[plugin] uninstall done."
else
echo "[error] not found cosinee dir."
exit 2
fi

View File

@ -0,0 +1,133 @@
<script src="/js/utils.js"></script>
<script src="/js/CSKeFu_Rest_Request.v1.js"></script>
<style>
#create {
margin: 10px;
border-top: 1px solid #EEEEEE;
padding-top: 5px;
}
#create input {
width: 400px;
}
</style>
<div class="row">
<div class="col-lg-12">
<div id="create">
<form class="layui-form uk-form" style="width: 700px;margin: auto;">
<input id="id" type="hidden" name="id" value="${id}">
<div class="layui-form-item">
<label class="layui-form-label">网站渠道</label>
<div class="layui-input-inline">
<#if id!=null>
<input type="text" name="snsurl" required lay-verify="required" value="${snsurl}"
autocomplete="off" class="layui-input" disabled>
<#else>
<select id="snsid" name="snsid" lay-verify="required">
<option></option>
</select>
</#if>
</div>
<div class="layui-form-mid layui-word-aux">智能机器人服务的渠道标识</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">ClientId</label>
<div class="layui-input-inline">
<input type="text" name="clientId" required lay-verify="required" placeholder="请输入ClientId" autocomplete="off"
class="layui-input" value="${bot.clientId}">
</div>
<div class="layui-form-mid layui-word-aux">智能机器人ClientId还没有<a href="${botServiecProvider}/dashboard" target="_blank">现在去创建!</a></div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">Secret</label>
<div class="layui-input-inline">
<input type="password" name="secret" required lay-verify="required" placeholder="请输入Secret" autocomplete="off"
class="layui-input" value="${bot.secret}">
</div>
<div class="layui-form-mid layui-word-aux">智能机器人Secret</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">工作模式</label>
<div class="layui-input-inline">
<select name="workmode" lay-verify="required">
<option <#if bot.workmode=="机器人客服优先">selected="selected"</#if>>机器人客服优先</option>
<option <#if bot.workmode=="人工客服优先">selected="selected"</#if>>人工客服优先</option>
</select>
</div>
<div class="layui-form-mid layui-word-aux">来自访客的会话默认以什么方式接待</div>
</div>
<div class="layui-form-item">
<div class="layui-input-inline">
<button class="layui-btn" lay-submit lay-filter="save">保存</button>
</div>
</div>
</form>
</div>
</div>
</div>
<script>
// 保存成功,刷新页面
function submitChatbotSucc(bot) {
if(bot.rc != 0){
submitChatbotFail(bot);
} else {
parent.location.href = "/apps/chatbot/index.html?chatbotid="+bot.data.id;
}
}
// 保存失败
function submitChatbotFail(err){
layer.confirm(err.data||err.error, {
btn: ['关闭'],
icon: 2,
title: '提示'
}, function (popup, layero) {
layer.close(popup)
}, function (popup) {
// 取消方法
});
}
layui.use(['form'], function () {
var form = layui.form();
form.on('submit(save)', function (data) {
var field = data.field;
if (field.id) {
field.ops = 'update';
restApiRequest({
silent: true,
path: "chatbot",
data: field
}).then(submitChatbotSucc, submitChatbotFail);
} else {
field.ops = 'create';
restApiRequest({
path: "chatbot",
data: field
}).then(submitChatbotSucc, submitChatbotFail);
}
return false;
});
if (!$('#id').val()) {
restApiRequest({
path: "chatbot",
silent: true,
data: {
ops: "vacant"
}
}).then(function (data) {
if(data.rc == 0 && data.data.length > 0){
var options = $.map(data.data, function (r) {
return '<option value="' + r.snsid + '">' + r.snsurl + '</option>';
});
$('#snsid').html(options);
form.render('select');
}
}, function(error){
console.log("error", error);
})
}
})
</script>

View File

@ -0,0 +1,25 @@
<script src="/js/utils.js"></script>
<script src="/js/CSKeFu_Rest_Request.v1.js"></script>
<h1 class="site-h1" style="border-top:1px solid #e6e6e6;">
列表
</h1>
<#if chatbots??>
<ul class="layui-nav layui-nav-tree" lay-filter="chatbots-nav-tree">
<li class="layui-nav-item layui-nav-itemed">
<dl class="layui-nav-child">
<#list chatbots as chatbot>
<dd style="text-align: center;border-bottom:thin solid #F5F5F5; list-style: none;" <#if currentbotid?? && chatbot.id == currentbotid>class="layui-this"</#if>>
<a href="/apps/chatbot/index.html?chatbotid=${chatbot.id}">${chatbot.name}</a>
</dd>
</#list>
</dl>
</li>
</ul>
<#else>
<div class="ukefu-empty" style="background: none">
<i class="layui-icon">&#xe63a;</i>
<div style="">还没有集成智能机器人</div>
</div>
</#if>

View File

@ -0,0 +1,287 @@
<div class="layui-side layui-bg-black">
<div class="layui-side-scroll">
<#include "/apps/chatbot/include/left.html">
</div>
</div>
<div class="layui-body">
<#if currentbotid??>
<div class="row">
<div class="col-lg-12">
<h1 class="site-h1 ukefu-tab" style="border-top:1px solid #e6e6e6;height:26px;">
<span class="ukefu-bt">
<div class="ukefu-bt-text">
<div class="ukefu-bt-text-title">
智能机器人
</div>
</div>
</span>
<!-- 按钮组件 -->
<div class="ukefu-bt-text-content" style="position: absolute;right: 5px;top: 0px;">
<div class="layui-btn-group ukefu-btn-group">
<button class="layui-btn layui-btn-small" onclick="showChatbotEditDialogue('${currentbotid}')">
<i class="layui-icon">&#xe643;</i> 绑定
</button>
<button class="layui-btn layui-btn-small" onclick="openChatbotBotPlatform('${currentbot.clientId}')">
<i class="layui-icon">&#xe631;</i> 配置
</button>
<button class="layui-btn layui-btn-small" onclick="showChatbotCreateDialogue()">
<i class="layui-icon">&#xe654;</i> 新建
</button>
<button class="layui-btn layui-btn-danger layui-btn-small" onclick="showChatbotDeleteDialogue()">
<i class="layui-icon">&#xe640;</i> 删除
</button>
<button class="layui-btn layui-btn-warm layui-btn-small" onclick="openChatbotIntegrationInfoCenter()">
<i class="layui-icon">&#xe60a;</i> 文档中心
</button>
</div>
</div>
</h1>
</div>
</div>
<!-- 提示 -->
<div class="row" style="padding-left:5px;">
<div class="row">
<blockquote class="layui-elem-quote">
<p>智能机器人用于在访客端实现机器人客服,具体使用参考文档中心<a href="https://docs.chatopera.com/products/cskefu/work-chatbot.html" target="_blank">《春松客服机器人客服》</a>
</p>
</blockquote>
</div>
</div>
<!-- 工作状态 -->
<div class="row" style="padding:5px;">
<div class="col-lg-12">
<fieldset class="layui-elem-field layui-field-title">
<legend>工作状态</legend>
<div class="layui-field-box">
<blockquote class="layui-elem-quote layui-quote-nm">
<p>在开启状态下,在访客端可以看到【智能坐席】对话窗口并与机器人客服对话。</p>
</blockquote>
<form class="layui-form" action="">
<div class="layui-form-item">
<div class="layui-input-block">
<input type="checkbox" lay-filter="bot-workstatus" name="switch" lay-skin="switch" lay-text="启用|禁用" <#if currentbot.enabled>checked</#if>>
</div>
<!-- <div class="layui-form-mid layui-word-aux">在开启状态下,在访客端可以看到【智能坐席】对话窗口并与机器人客服对话。</div>-->
</div>
</form>
</div>
</fieldset>
</div>
</div>
<!-- 绑定 -->
<div class="row" style="padding-left:5px;">
<div class="col-lg-12">
<fieldset class="layui-elem-field layui-field-title">
<legend>绑定</legend>
<div class="layui-field-box">
<blockquote class="layui-elem-quote layui-quote-nm">
<p>以下信息为春松客服使用该智能机器人集成的信息,去<a href="javascript:void(0)" onclick="showChatbotEditDialogue('${currentbotid}')">设置</a></p>
</blockquote>
<table class="layui-table" lay-even lay-skin="nob">
<colgroup>
<col width="150">
<col width="200">
<col width="400">
</colgroup>
<tbody>
<tr>
<td>渠道</td>
<td>
<#if currentbot.channel == "webim">
网站
<#elseif currentbot.channel == "skype">
Skype
<#elseif currentbot.channel == "callout">
外呼
<#else>
未知渠道类型
</#if>
</td>
<td><p style="color: #9C9C9C">访客接入的方式</p></td>
</tr>
<tr>
<td>渠道名称</td>
<td>${snsAccountName!""}</td>
<td><p style="color: #9C9C9C">限定了智能机器人出现的渠道标识。</p></td>
</tr>
<tr>
<td>创建人</td>
<td>${creatorname!"未知"}</td>
<td><p style="color: #9C9C9C">创建人初次设定了智能机器人。</p></td>
</tr>
<tr>
<td>工作模式</td>
<td>
${currentbot.workmode}
</td>
<td><p style="color: #9C9C9C">工作模式有机器人优先和人工坐席优先两种,这决定了访客连线后默认对接的方式。</p></td>
</tr>
</tbody>
</table>
</div>
</fieldset>
</div>
</div>
<!-- 配置 -->
<div class="row" style="padding:5px;">
<div class="col-lg-12">
<fieldset class="layui-elem-field layui-field-title">
<legend>配置</legend>
<div class="layui-field-box">
<blockquote class="layui-elem-quote layui-quote-nm">
<p><a href="javascript:void(0)" onclick="openChatbotBotPlatform('${currentbot.clientId}')">进入Chatopera云服务</a>设置以下信息及知识库、多轮对话、意图识别、使用情况分析和对话历史等信息。</p>
</blockquote>
<table class="layui-table" lay-even lay-skin="nob">
<colgroup>
<col width="150">
<col width="200">
<col width="300">
</colgroup>
<tbody>
<tr>
<td>描述</td>
<td>${currentbot.description}</td>
<td><p style="color: #9C9C9C">智能机器人的描述,侧重于业务,访客不会看到该信息。</p></td>
</tr>
<tr>
<td>欢迎语</td>
<td>${currentbot.welcome}</td>
<td><p style="color: #9C9C9C">与访客建立连接后,智能机器人发送的打招呼信息。</p></td>
</tr>
<tr>
<td>兜底回复</td>
<td>${currentbot.fallback}</td>
<td><p style="color: #9C9C9C">当智能机器人不清楚如何回复时的回复。</p></td>
</tr>
</tbody>
</table>
</div>
</fieldset>
</div>
</div>
<#else>
<div class="row">
<div class="col-lg-12">
<h1 class="site-h1 ukefu-tab" style="border-top:1px solid #e6e6e6;height:26px;">
<span class="ukefu-bt">
<div class="ukefu-bt-text">
<div class="ukefu-bt-text-title">
智能机器人
</div>
</div>
</span>
<!-- 按钮组件 -->
<div class="ukefu-bt-text-content" style="position: absolute;right: 5px;top: 0px;">
<div class="layui-btn-group ukefu-btn-group">
<button class="layui-btn layui-btn-small" onclick="showChatbotCreateDialogue()">
<i class="layui-icon">&#xe654;</i> 新建
</button>
<button class="layui-btn layui-btn-small" onclick="openChatbotBotPlatform()">
<i class="csfont">&#xe602;</i> 登录Chatopera云服务
</button>
<button class="layui-btn layui-btn-warm layui-btn-small" onclick="openChatbotIntegrationInfoCenter()">
<i class="layui-icon">&#xe60a;</i> 文档中心
</button>
</div>
</div>
</h1>
</div>
</div>
<div class="row" style="padding:5px;">
<div class="col-lg-12">
还没有智能机器人,<a href="javascript:void(0)" onclick="showChatbotCreateDialogue()">现在去创建!</a>
</div>
</div>
</#if>
</div>
<script>
// 打开文档中心
function openChatbotIntegrationInfoCenter(){
window.open("https://docs.chatopera.com/products/cskefu/work-chatbot.html", "_blank");
}
// 展示编辑机器人的窗口
function showChatbotEditDialogue(chatbotid) {
console.log("showChatbotEditDialogue", chatbotid);
layer.open({
title: '编辑智能机器人',
type: 2,
area: ['800px', '450px'],
content: 'edit.html?id=' + chatbotid
})
}
// 展示创建机器人的对话框
function showChatbotCreateDialogue() {
layer.open({
title: '集成智能机器人',
type: 2,
area: ['800px', '450px'],
content: 'edit.html'
})
}
// 删除机器人
function showChatbotDeleteDialogue() {
var lindex = layer.confirm('请确认是否删除?', {
btn: ['确认', '删除']
}, function () {
return restApiRequest({
path: "chatbot",
data: {
ops: 'delete',
id: '${currentbotid}'
}
}).then(function (result) {
// 刷新页面
layer.close(lindex);
setTimeout(function () {
location.href = "/apps/chatbot/index.html";
}, 300);
}, function (reason) {
// TODO 提示错误
console.log("[ChatbotDelete] error: ", reason);
layer.close(lindex);
});
}, function () {
layer.close(lindex);
});
}
// 打开机器人管理地址
function openChatbotBotPlatform(chatbotid){
var botMgrUrl = "${botServiecProvider}";
if(chatbotid){
botMgrUrl = "${botServiecProvider}/dashboard/clients/"+chatbotid+"/control-center";
}
window.open(botMgrUrl, "_blank");
}
// 设置机器人工作状态
function setChatbotWorkstatus(id, workstatus){
restApiRequest({
path: "chatbot",
data: {
ops: workstatus?"enable":"disable",
id: id
}
});
}
layui.use(['form'], function(){
var form = layui.form();
form.render();
form.on('switch(bot-workstatus)', function (data) {
setChatbotWorkstatus('${currentbotid}', data.elem.checked);
});
});
</script>

View File

@ -0,0 +1,31 @@
#! /bin/bash
###########################################
# Install All public plugins
# Copyright (2019) 北京华夏春松科技有限公司
###########################################
# constants
baseDir=$(cd `dirname "$0"`;pwd)
# functions
function install_plugin(){
echo "Install plugin" $1 "..."
cd $baseDir/../$1
if [ -f ./scripts/install.sh ]; then
./scripts/install.sh
else
echo "[WARN] not exist command" $baseDir/../$1/scripts/install.sh
fi
cd $baseDir/..
}
# main
[ -z "${BASH_SOURCE[0]}" -o "${BASH_SOURCE[0]}" = "$0" ] || return
cd $baseDir/..
echo "Inspect plugins -->" `pwd`
for x in `ls .`; do
if [ $x != "scripts" ] && [ -d ./$x ]; then
install_plugin $x
echo -e "\n"
fi
done

View File

@ -0,0 +1,31 @@
#! /bin/bash
###########################################
# Uninstall all public plugins
# Copyright (2019) 北京华夏春松科技有限公司
###########################################
# constants
baseDir=$(cd `dirname "$0"`;pwd)
# functions
function uninstall_plugin(){
echo "Uninstall plugin" $1 "..."
cd $baseDir/../$1
if [ -f ./scripts/uninstall.sh ]; then
./scripts/uninstall.sh
else
echo "[WARN] not exist command" $baseDir/../$1/scripts/uninstall.sh
fi
cd $baseDir/..
}
# main
[ -z "${BASH_SOURCE[0]}" -o "${BASH_SOURCE[0]}" = "$0" ] || return
cd $baseDir/..
echo "Inspect plugins -->" `pwd`
for x in `ls .`; do
if [ $x != "scripts" ] && [ -d ./$x ]; then
uninstall_plugin $x
echo -e "\n"
fi
done