mirror of
https://github.com/chatopera/cosin.git
synced 2025-06-16 18:30:03 +08:00
https://github.com/cskefu/cskefu/issues/937 init license schema as metakv
Signed-off-by: Hai Liang Wang <hai@chatopera.com>
This commit is contained in:
parent
e69dfd398d
commit
629abe88d2
@ -88,6 +88,7 @@
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>3.1.3</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
|
@ -1,14 +1,14 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Beijing Huaxia Chunsong Technology Co., Ltd.
|
||||
* <https://www.chatopera.com>, Licensed under the Chunsong Public
|
||||
/*
|
||||
* Copyright (C) 2023 Beijing Huaxia Chunsong Technology Co., Ltd.
|
||||
* <https://www.chatopera.com>, Licensed under the Chunsong Public
|
||||
* License, Version 1.0 (the "License"), https://docs.cskefu.com/licenses/v1.html
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* Copyright (C) 2019-2022 Chatopera Inc, <https://www.chatopera.com>,
|
||||
* Licensed under the Apache License, Version 2.0,
|
||||
* Copyright (C) 2019-2022 Chatopera Inc, <https://www.chatopera.com>,
|
||||
* Licensed under the Apache License, Version 2.0,
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package com.cskefu.cc.basic;
|
||||
@ -217,4 +217,13 @@ public class Constants {
|
||||
public static final String AUTH_TOKEN_TYPE_BEARER = "Bearer";
|
||||
public static final String AUTH_TOKEN_TYPE_BASIC = "Basic";
|
||||
|
||||
/**
|
||||
* License
|
||||
*/
|
||||
public static final String LICENSE_SERVER_INST_ID = "SERVERINSTID";
|
||||
public static final String LICENSE_SERVICE_NAME = "SERVICENAME";
|
||||
public static final String LICENSE_SERVICE_NAME_PREFIX = "春松客服";
|
||||
public static final String LICENSES = "LICENSES";
|
||||
public static final String METAKV_DATATYPE_STRING = "string";
|
||||
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import com.cskefu.cc.model.BlackEntity;
|
||||
import com.cskefu.cc.model.SysDic;
|
||||
import com.cskefu.cc.model.SystemConfig;
|
||||
import com.cskefu.cc.persistence.repository.*;
|
||||
import com.cskefu.cc.proxy.LicenseProxy;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -36,7 +37,6 @@ public class AppCtxRefreshEventListener implements ApplicationListener<ContextRe
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AppCtxRefreshEventListener.class);
|
||||
|
||||
|
||||
private void setupSysdicCacheAndExtras(final ContextRefreshedEvent event, final String cacheSetupStrategy, final Cache cache, final SysDicRepository sysDicRes, final BlackListRepository blackListRes) {
|
||||
if (!StringUtils.equalsIgnoreCase(cacheSetupStrategy, Constants.cache_setup_strategy_skip)) {
|
||||
|
||||
@ -138,6 +138,9 @@ public class AppCtxRefreshEventListener implements ApplicationListener<ContextRe
|
||||
logger.info("[Plugins] registered plugin id {}, class {}", p.getPluginId(), p.getClass().getName());
|
||||
}
|
||||
|
||||
// 初始化 ServerInstId
|
||||
LicenseProxy licenseProxy = event.getApplicationContext().getBean(LicenseProxy.class);
|
||||
licenseProxy.checkOnStartup();
|
||||
} else {
|
||||
logger.info("[onApplicationEvent] bypass, initialization has been done already.");
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.PersistenceContext;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.validation.Valid;
|
||||
import java.sql.Connection;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
@ -31,7 +31,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
@ -31,7 +31,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
@ -0,0 +1,77 @@
|
||||
package com.cskefu.cc.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 存储元数据
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "cs_metakv")
|
||||
@org.hibernate.annotations.Proxy(lazy = false)
|
||||
public class Metakv implements java.io.Serializable {
|
||||
|
||||
@Id
|
||||
private String metakey;
|
||||
|
||||
private String metavalue;
|
||||
|
||||
private String datatype;
|
||||
|
||||
private String comment;
|
||||
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date updatetime;
|
||||
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date createtime;
|
||||
|
||||
public String getMetakey() {
|
||||
return metakey;
|
||||
}
|
||||
|
||||
public void setMetakey(String metakey) {
|
||||
this.metakey = metakey;
|
||||
}
|
||||
|
||||
public String getMetavalue() {
|
||||
return metavalue;
|
||||
}
|
||||
|
||||
public void setMetavalue(String metavalue) {
|
||||
this.metavalue = metavalue;
|
||||
}
|
||||
|
||||
public String getDatatype() {
|
||||
return datatype;
|
||||
}
|
||||
|
||||
public void setDatatype(String datatype) {
|
||||
this.datatype = datatype;
|
||||
}
|
||||
|
||||
public String getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
public void setComment(String comment) {
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
public Date getUpdatetime() {
|
||||
return updatetime;
|
||||
}
|
||||
|
||||
public void setUpdatetime(Date updatetime) {
|
||||
this.updatetime = updatetime;
|
||||
}
|
||||
|
||||
public Date getCreatetime() {
|
||||
return createtime;
|
||||
}
|
||||
|
||||
public void setCreatetime(Date createtime) {
|
||||
this.createtime = createtime;
|
||||
}
|
||||
}
|
@ -1,33 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Beijing Huaxia Chunsong Technology Co., Ltd.
|
||||
* <https://www.chatopera.com>, Licensed under the Chunsong Public
|
||||
* License, Version 1.0 (the "License"), https://docs.cskefu.com/licenses/v1.html
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* Copyright (C) 2018- Jun. 2023 Chatopera Inc, <https://www.chatopera.com>, Licensed under the Apache License, Version 2.0,
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统, Licensed under the Apache License, Version 2.0,
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package com.cskefu.cc.persistence.repository;
|
||||
|
||||
import com.cskefu.cc.model.MetadataTable;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface MetadataRepository extends JpaRepository<MetadataTable, String>{
|
||||
|
||||
MetadataTable findByTablename(String tablename);
|
||||
|
||||
Page<MetadataTable> findAll(Pageable paramPageable);
|
||||
|
||||
int countByTablename(String tableName) ;
|
||||
|
||||
List<MetadataTable> findAll();
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2023 Beijing Huaxia Chunsong Technology Co., Ltd.
|
||||
* <https://www.chatopera.com>, Licensed under the Chunsong Public
|
||||
* License, Version 1.0 (the "License"), https://docs.cskefu.com/licenses/v1.html
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* Copyright (C) 2018- Jun. 2023 Chatopera Inc, <https://www.chatopera.com>, Licensed under the Apache License, Version 2.0,
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统, Licensed under the Apache License, Version 2.0,
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package com.cskefu.cc.persistence.repository;
|
||||
|
||||
import com.cskefu.cc.model.MetadataTable;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface MetadataRepository extends JpaRepository<MetadataTable, String>{
|
||||
|
||||
MetadataTable findByTablename(String tablename);
|
||||
|
||||
Page<MetadataTable> findAll(Pageable paramPageable);
|
||||
|
||||
int countByTablename(String tableName) ;
|
||||
|
||||
List<MetadataTable> findAll();
|
||||
}
|
||||
|
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Beijing Huaxia Chunsong Technology Co., Ltd.
|
||||
* <https://www.chatopera.com>, Licensed under the Chunsong Public
|
||||
* License, Version 1.0 (the "License"), https://docs.cskefu.com/licenses/v1.html
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.cskefu.cc.persistence.repository;
|
||||
|
||||
import com.cskefu.cc.model.Metakv;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface MetakvRepository extends JpaRepository<Metakv, String> {
|
||||
|
||||
Optional<Metakv> findFirstByMetakey(final String p1);
|
||||
|
||||
}
|
@ -1,371 +1,371 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Beijing Huaxia Chunsong Technology Co., Ltd.
|
||||
* <https://www.chatopera.com>, Licensed under the Chunsong Public
|
||||
* License, Version 1.0 (the "License"), https://docs.cskefu.com/licenses/v1.html
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* Copyright (C) 2019-Jun. 2023 Chatopera Inc, <https://www.chatopera.com>,
|
||||
* Licensed under the Apache License, Version 2.0,
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package com.cskefu.cc.plugins.chatbot;
|
||||
|
||||
import com.chatopera.bot.sdk.Response;
|
||||
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 com.cskefu.cc.acd.ACDServiceRouter;
|
||||
import com.cskefu.cc.basic.Constants;
|
||||
import com.cskefu.cc.basic.MainContext;
|
||||
import com.cskefu.cc.basic.MainUtils;
|
||||
import com.cskefu.cc.model.*;
|
||||
import com.cskefu.cc.persistence.repository.AgentUserRepository;
|
||||
import com.cskefu.cc.persistence.repository.ChatbotRepository;
|
||||
import com.cskefu.cc.persistence.repository.PassportWebIMUserRepository;
|
||||
import com.cskefu.cc.proxy.OnlineUserProxy;
|
||||
import com.cskefu.cc.socketio.client.NettyClients;
|
||||
import com.cskefu.cc.socketio.message.AgentStatusMessage;
|
||||
import com.cskefu.cc.socketio.message.ChatMessage;
|
||||
import com.cskefu.cc.socketio.message.Message;
|
||||
import com.cskefu.cc.socketio.util.IMServiceUtils;
|
||||
import com.cskefu.cc.util.IP;
|
||||
import com.cskefu.cc.util.IPTools;
|
||||
import org.apache.commons.lang3.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 final SocketIOServer server;
|
||||
|
||||
private static AgentUserRepository agentUserRes;
|
||||
private static PassportWebIMUserRepository 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 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);
|
||||
|
||||
Date now = new Date();
|
||||
|
||||
if (StringUtils.isNotBlank(user)) {
|
||||
/**
|
||||
* 加入到 缓存列表
|
||||
*/
|
||||
NettyClients.getInstance().putChatbotEventClient(user, client);
|
||||
CousultInvite invite = OnlineUserProxy.consult(appid);
|
||||
|
||||
/**
|
||||
* 更新坐席服务类型
|
||||
*/
|
||||
IMServiceUtils.shiftOpsType(user, 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().findById(invite.getAiid()).orElse(null);
|
||||
com.chatopera.bot.sdk.Chatbot bot = new com.chatopera.bot.sdk.Chatbot(
|
||||
chatbot.getClientId(), chatbot.getSecret(), chatbot.getBaseUrl());
|
||||
Response result = bot.command("GET", "/");
|
||||
|
||||
// 发送欢迎语
|
||||
if (result.getRc() == 0) {
|
||||
JSONObject details = (JSONObject) result.getData();
|
||||
ChatMessage welcome = new ChatMessage();
|
||||
String welcomeTextMessage = details.getString("welcome");
|
||||
if (StringUtils.isNotBlank(welcomeTextMessage)) {
|
||||
welcome.setCalltype(MainContext.CallType.OUT.toString());
|
||||
welcome.setAppid(appid);
|
||||
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.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);
|
||||
}
|
||||
}
|
||||
} else if (result.getRc() == 999 || result.getRc() == 998) {
|
||||
logger.error("[chat] chatbot agent response rc {}, error {}", result.getRc(), result.getError());
|
||||
} else {
|
||||
logger.error("[chat] chatbot agent response rc {}, error {}", result.getRc(), result.getError());
|
||||
}
|
||||
}
|
||||
|
||||
InetSocketAddress address = (InetSocketAddress) client.getRemoteAddress();
|
||||
String ip = MainUtils.getIpAddr(client.getHandshakeData().getHttpHeaders(), address.getHostString());
|
||||
PassportWebIMUser passportWebIMUser = getOnlineUserRes().findById(user).orElse(null);
|
||||
|
||||
if (passportWebIMUser == null) {
|
||||
passportWebIMUser = new PassportWebIMUser();
|
||||
passportWebIMUser.setAppid(appid);
|
||||
if (StringUtils.isNotBlank(nickname)) {
|
||||
passportWebIMUser.setUsername(nickname);
|
||||
} else {
|
||||
passportWebIMUser.setUsername(Constants.GUEST_USER + "_" + MainUtils.genIDByKey(user));
|
||||
}
|
||||
|
||||
passportWebIMUser.setSessionid(session);
|
||||
passportWebIMUser.setOptype(MainContext.OptType.CHATBOT.toString());
|
||||
passportWebIMUser.setUserid(user);
|
||||
passportWebIMUser.setId(user);
|
||||
passportWebIMUser.setChannel(MainContext.ChannelType.WEBIM.toString());
|
||||
passportWebIMUser.setIp(ip);
|
||||
passportWebIMUser.setUpdatetime(now);
|
||||
passportWebIMUser.setLogintime(now);
|
||||
passportWebIMUser.setCreatetime(now);
|
||||
IP ipdata = IPTools.getInstance().findGeography(ip);
|
||||
passportWebIMUser.setCity(ipdata.getCity());
|
||||
passportWebIMUser.setCountry(ipdata.getCountry());
|
||||
passportWebIMUser.setProvince(ipdata.getProvince());
|
||||
passportWebIMUser.setIsp(ipdata.getIsp());
|
||||
passportWebIMUser.setRegion(ipdata.getRegion());
|
||||
passportWebIMUser.setStatus(MainContext.OnlineUserStatusEnum.ONLINE.toString());
|
||||
}
|
||||
|
||||
// 在线客服访客咨询记录
|
||||
AgentUser agentUser = new AgentUser(
|
||||
passportWebIMUser.getId(),
|
||||
MainContext.ChannelType.WEBIM.toString(), // callout
|
||||
passportWebIMUser.getId(),
|
||||
passportWebIMUser.getUsername(),
|
||||
appid);
|
||||
|
||||
agentUser.setServicetime(now);
|
||||
agentUser.setCreatetime(now);
|
||||
agentUser.setUpdatetime(now);
|
||||
agentUser.setSessionid(session);
|
||||
agentUser.setRegion(passportWebIMUser.getRegion());
|
||||
|
||||
// 聊天机器人处理的请求
|
||||
agentUser.setOpttype(MainContext.OptType.CHATBOT.toString());
|
||||
agentUser.setAgentno(aiid); // 聊天机器人ID
|
||||
agentUser.setAgentname(invite != null ? invite.getAiname() : "机器人客服");
|
||||
agentUser.setCity(passportWebIMUser.getCity());
|
||||
agentUser.setProvince(passportWebIMUser.getProvince());
|
||||
agentUser.setCountry(passportWebIMUser.getCountry());
|
||||
AgentService agentService = ACDServiceRouter.getAcdChatbotService().processChatbotService(
|
||||
invite != null ? invite.getAiname() : "机器人客服", agentUser);
|
||||
agentUser.setAgentserviceid(agentService.getId());
|
||||
|
||||
// 标记为机器人坐席
|
||||
agentUser.setChatbotops(true);
|
||||
|
||||
// 保存到MySQL
|
||||
getAgentUserRes().save(agentUser);
|
||||
getOnlineUserRes().save(passportWebIMUser);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.info("[onConnect] error", e);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加 @OnDisconnect 事件,客户端断开连接时调用,刷新客户端信息
|
||||
@OnDisconnect
|
||||
public void onDisconnect(SocketIOClient client) {
|
||||
String user = client.getHandshakeData().getSingleUrlParam("userid");
|
||||
if (StringUtils.isNotBlank(user)) {
|
||||
NettyClients.getInstance().removeChatbotEventClient(
|
||||
user, MainUtils.getContextID(client.getSessionId().toString()));
|
||||
PassportWebIMUser passportWebIMUser = MainContext.getCache().findOneOnlineUserByUserId(user);
|
||||
|
||||
MainContext.getCache().findOneAgentUserByUserId(user).ifPresent(p -> {
|
||||
ACDServiceRouter.getAcdChatbotService().processChatbotService(null, p);
|
||||
|
||||
MainContext.getCache().deleteAgentUserByUserId(p);
|
||||
MainContext.getCache().deleteOnlineUserById(user);
|
||||
|
||||
p.setStatus(MainContext.AgentUserStatusEnum.END.toString());
|
||||
passportWebIMUser.setStatus(MainContext.OnlineUserStatusEnum.OFFLINE.toString());
|
||||
|
||||
getAgentUserRes().save(p);
|
||||
getOnlineUserRes().save(passportWebIMUser);
|
||||
});
|
||||
}
|
||||
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 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 {}", sessionid, aiid,
|
||||
user, data.getType(), appid);
|
||||
|
||||
// ignore event if dataType is not message.
|
||||
if (!StringUtils.equals(data.getType(), Constants.IM_MESSAGE_TYPE_MESSAGE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MainContext.getCache().findOneAgentUserByUserId(user).ifPresent(p -> {
|
||||
/**
|
||||
* 以下代码主要用于检查 访客端的字数限制
|
||||
*/
|
||||
CousultInvite invite = OnlineUserProxy.consult(data.getAppid());
|
||||
// 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.getChanneltype());
|
||||
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 PassportWebIMUserRepository getOnlineUserRes() {
|
||||
if (onlineUserRes == null) {
|
||||
onlineUserRes = MainContext.getContext().getBean(PassportWebIMUserRepository.class);
|
||||
}
|
||||
|
||||
return onlineUserRes;
|
||||
}
|
||||
|
||||
private ChatbotRepository getChatbotRes() {
|
||||
if (chatbotRes == null) {
|
||||
chatbotRes = MainContext.getContext().getBean(ChatbotRepository.class);
|
||||
}
|
||||
|
||||
return chatbotRes;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2023 Beijing Huaxia Chunsong Technology Co., Ltd.
|
||||
* <https://www.chatopera.com>, Licensed under the Chunsong Public
|
||||
* License, Version 1.0 (the "License"), https://docs.cskefu.com/licenses/v1.html
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* Copyright (C) 2019-Jun. 2023 Chatopera Inc, <https://www.chatopera.com>,
|
||||
* Licensed under the Apache License, Version 2.0,
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package com.cskefu.cc.plugins.chatbot;
|
||||
|
||||
import com.chatopera.bot.sdk.Response;
|
||||
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 com.cskefu.cc.acd.ACDServiceRouter;
|
||||
import com.cskefu.cc.basic.Constants;
|
||||
import com.cskefu.cc.basic.MainContext;
|
||||
import com.cskefu.cc.basic.MainUtils;
|
||||
import com.cskefu.cc.model.*;
|
||||
import com.cskefu.cc.persistence.repository.AgentUserRepository;
|
||||
import com.cskefu.cc.persistence.repository.ChatbotRepository;
|
||||
import com.cskefu.cc.persistence.repository.PassportWebIMUserRepository;
|
||||
import com.cskefu.cc.proxy.OnlineUserProxy;
|
||||
import com.cskefu.cc.socketio.client.NettyClients;
|
||||
import com.cskefu.cc.socketio.message.AgentStatusMessage;
|
||||
import com.cskefu.cc.socketio.message.ChatMessage;
|
||||
import com.cskefu.cc.socketio.message.Message;
|
||||
import com.cskefu.cc.socketio.util.IMServiceUtils;
|
||||
import com.cskefu.cc.util.IP;
|
||||
import com.cskefu.cc.util.IPTools;
|
||||
import org.apache.commons.lang3.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 final SocketIOServer server;
|
||||
|
||||
private static AgentUserRepository agentUserRes;
|
||||
private static PassportWebIMUserRepository 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 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);
|
||||
|
||||
Date now = new Date();
|
||||
|
||||
if (StringUtils.isNotBlank(user)) {
|
||||
/**
|
||||
* 加入到 缓存列表
|
||||
*/
|
||||
NettyClients.getInstance().putChatbotEventClient(user, client);
|
||||
CousultInvite invite = OnlineUserProxy.consult(appid);
|
||||
|
||||
/**
|
||||
* 更新坐席服务类型
|
||||
*/
|
||||
IMServiceUtils.shiftOpsType(user, 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().findById(invite.getAiid()).orElse(null);
|
||||
com.chatopera.bot.sdk.Chatbot bot = new com.chatopera.bot.sdk.Chatbot(
|
||||
chatbot.getClientId(), chatbot.getSecret(), chatbot.getBaseUrl());
|
||||
Response result = bot.command("GET", "/");
|
||||
|
||||
// 发送欢迎语
|
||||
if (result.getRc() == 0) {
|
||||
JSONObject details = (JSONObject) result.getData();
|
||||
ChatMessage welcome = new ChatMessage();
|
||||
String welcomeTextMessage = details.getString("welcome");
|
||||
if (StringUtils.isNotBlank(welcomeTextMessage)) {
|
||||
welcome.setCalltype(MainContext.CallType.OUT.toString());
|
||||
welcome.setAppid(appid);
|
||||
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.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);
|
||||
}
|
||||
}
|
||||
} else if (result.getRc() == 999 || result.getRc() == 998) {
|
||||
logger.error("[chat] chatbot agent response rc {}, error {}", result.getRc(), result.getError());
|
||||
} else {
|
||||
logger.error("[chat] chatbot agent response rc {}, error {}", result.getRc(), result.getError());
|
||||
}
|
||||
}
|
||||
|
||||
InetSocketAddress address = (InetSocketAddress) client.getRemoteAddress();
|
||||
String ip = MainUtils.getIpAddr(client.getHandshakeData().getHttpHeaders(), address.getHostString());
|
||||
PassportWebIMUser passportWebIMUser = getOnlineUserRes().findById(user).orElse(null);
|
||||
|
||||
if (passportWebIMUser == null) {
|
||||
passportWebIMUser = new PassportWebIMUser();
|
||||
passportWebIMUser.setAppid(appid);
|
||||
if (StringUtils.isNotBlank(nickname)) {
|
||||
passportWebIMUser.setUsername(nickname);
|
||||
} else {
|
||||
passportWebIMUser.setUsername(Constants.GUEST_USER + "_" + MainUtils.genIDByKey(user));
|
||||
}
|
||||
|
||||
passportWebIMUser.setSessionid(session);
|
||||
passportWebIMUser.setOptype(MainContext.OptType.CHATBOT.toString());
|
||||
passportWebIMUser.setUserid(user);
|
||||
passportWebIMUser.setId(user);
|
||||
passportWebIMUser.setChannel(MainContext.ChannelType.WEBIM.toString());
|
||||
passportWebIMUser.setIp(ip);
|
||||
passportWebIMUser.setUpdatetime(now);
|
||||
passportWebIMUser.setLogintime(now);
|
||||
passportWebIMUser.setCreatetime(now);
|
||||
IP ipdata = IPTools.getInstance().findGeography(ip);
|
||||
passportWebIMUser.setCity(ipdata.getCity());
|
||||
passportWebIMUser.setCountry(ipdata.getCountry());
|
||||
passportWebIMUser.setProvince(ipdata.getProvince());
|
||||
passportWebIMUser.setIsp(ipdata.getIsp());
|
||||
passportWebIMUser.setRegion(ipdata.getRegion());
|
||||
passportWebIMUser.setStatus(MainContext.OnlineUserStatusEnum.ONLINE.toString());
|
||||
}
|
||||
|
||||
// 在线客服访客咨询记录
|
||||
AgentUser agentUser = new AgentUser(
|
||||
passportWebIMUser.getId(),
|
||||
MainContext.ChannelType.WEBIM.toString(), // callout
|
||||
passportWebIMUser.getId(),
|
||||
passportWebIMUser.getUsername(),
|
||||
appid);
|
||||
|
||||
agentUser.setServicetime(now);
|
||||
agentUser.setCreatetime(now);
|
||||
agentUser.setUpdatetime(now);
|
||||
agentUser.setSessionid(session);
|
||||
agentUser.setRegion(passportWebIMUser.getRegion());
|
||||
|
||||
// 聊天机器人处理的请求
|
||||
agentUser.setOpttype(MainContext.OptType.CHATBOT.toString());
|
||||
agentUser.setAgentno(aiid); // 聊天机器人ID
|
||||
agentUser.setAgentname(invite != null ? invite.getAiname() : "机器人客服");
|
||||
agentUser.setCity(passportWebIMUser.getCity());
|
||||
agentUser.setProvince(passportWebIMUser.getProvince());
|
||||
agentUser.setCountry(passportWebIMUser.getCountry());
|
||||
AgentService agentService = ACDServiceRouter.getAcdChatbotService().processChatbotService(
|
||||
invite != null ? invite.getAiname() : "机器人客服", agentUser);
|
||||
agentUser.setAgentserviceid(agentService.getId());
|
||||
|
||||
// 标记为机器人坐席
|
||||
agentUser.setChatbotops(true);
|
||||
|
||||
// 保存到MySQL
|
||||
getAgentUserRes().save(agentUser);
|
||||
getOnlineUserRes().save(passportWebIMUser);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.info("[onConnect] error", e);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加 @OnDisconnect 事件,客户端断开连接时调用,刷新客户端信息
|
||||
@OnDisconnect
|
||||
public void onDisconnect(SocketIOClient client) {
|
||||
String user = client.getHandshakeData().getSingleUrlParam("userid");
|
||||
if (StringUtils.isNotBlank(user)) {
|
||||
NettyClients.getInstance().removeChatbotEventClient(
|
||||
user, MainUtils.getContextID(client.getSessionId().toString()));
|
||||
PassportWebIMUser passportWebIMUser = MainContext.getCache().findOneOnlineUserByUserId(user);
|
||||
|
||||
MainContext.getCache().findOneAgentUserByUserId(user).ifPresent(p -> {
|
||||
ACDServiceRouter.getAcdChatbotService().processChatbotService(null, p);
|
||||
|
||||
MainContext.getCache().deleteAgentUserByUserId(p);
|
||||
MainContext.getCache().deleteOnlineUserById(user);
|
||||
|
||||
p.setStatus(MainContext.AgentUserStatusEnum.END.toString());
|
||||
passportWebIMUser.setStatus(MainContext.OnlineUserStatusEnum.OFFLINE.toString());
|
||||
|
||||
getAgentUserRes().save(p);
|
||||
getOnlineUserRes().save(passportWebIMUser);
|
||||
});
|
||||
}
|
||||
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 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 {}", sessionid, aiid,
|
||||
user, data.getType(), appid);
|
||||
|
||||
// ignore event if dataType is not message.
|
||||
if (!StringUtils.equals(data.getType(), Constants.IM_MESSAGE_TYPE_MESSAGE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MainContext.getCache().findOneAgentUserByUserId(user).ifPresent(p -> {
|
||||
/**
|
||||
* 以下代码主要用于检查 访客端的字数限制
|
||||
*/
|
||||
CousultInvite invite = OnlineUserProxy.consult(data.getAppid());
|
||||
// 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.getChanneltype());
|
||||
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 PassportWebIMUserRepository getOnlineUserRes() {
|
||||
if (onlineUserRes == null) {
|
||||
onlineUserRes = MainContext.getContext().getBean(PassportWebIMUserRepository.class);
|
||||
}
|
||||
|
||||
return onlineUserRes;
|
||||
}
|
||||
|
||||
private ChatbotRepository getChatbotRes() {
|
||||
if (chatbotRes == null) {
|
||||
chatbotRes = MainContext.getContext().getBean(ChatbotRepository.class);
|
||||
}
|
||||
|
||||
return chatbotRes;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Beijing Huaxia Chunsong Technology Co., Ltd.
|
||||
* <https://www.chatopera.com>, Licensed under the Chunsong Public
|
||||
* License, Version 1.0 (the "License"), https://docs.cskefu.com/licenses/v1.html
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.cskefu.cc.proxy;
|
||||
|
||||
import com.cskefu.cc.basic.Constants;
|
||||
import com.cskefu.cc.basic.MainUtils;
|
||||
import com.cskefu.cc.model.Metakv;
|
||||
import com.cskefu.cc.persistence.repository.MetakvRepository;
|
||||
import com.cskefu.cc.util.Base62;
|
||||
import org.json.JSONArray;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class LicenseProxy {
|
||||
private final static Logger logger = LoggerFactory.getLogger(LicenseProxy.class);
|
||||
|
||||
@Autowired
|
||||
private MetakvRepository metkvRes;
|
||||
|
||||
/**
|
||||
* 初始化 serverinstId
|
||||
* serverinstId 作为服务唯一的实例ID
|
||||
*/
|
||||
public void checkOnStartup() {
|
||||
Optional<Metakv> metaServerinstIdOpt = metkvRes.findFirstByMetakey(Constants.LICENSE_SERVER_INST_ID);
|
||||
if (metaServerinstIdOpt.isEmpty()) {
|
||||
// 没有 serverinstId 信息,初始化
|
||||
final String serverinstId = MainUtils.getUUID();
|
||||
createMetakv(Constants.LICENSE_SERVER_INST_ID, serverinstId, Constants.METAKV_DATATYPE_STRING);
|
||||
}
|
||||
|
||||
Optional<Metakv> metaServicenameOpt = metkvRes.findFirstByMetakey(Constants.LICENSE_SERVICE_NAME);
|
||||
if (metaServicenameOpt.isEmpty()) {
|
||||
// 没有 Service Name 信息,初始化
|
||||
final String serviceName = generateLicenseServiceName();
|
||||
createMetakv(Constants.LICENSE_SERVICE_NAME, serviceName, Constants.METAKV_DATATYPE_STRING);
|
||||
}
|
||||
|
||||
Optional<Metakv> metaLicensesOpt = metkvRes.findFirstByMetakey(Constants.LICENSES);
|
||||
if (metaLicensesOpt.isEmpty()) {
|
||||
// 没有 license 信息,初始化
|
||||
createMetakv(Constants.LICENSES, (new JSONArray()).toString(), Constants.METAKV_DATATYPE_STRING);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 建立 Metakv 数据
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
* @param datatype
|
||||
*/
|
||||
public void createMetakv(final String key, final String value, final String datatype) {
|
||||
Date now = new Date();
|
||||
Metakv metakv = new Metakv();
|
||||
metakv.setCreatetime(now);
|
||||
metakv.setUpdatetime(now);
|
||||
metakv.setMetakey(key);
|
||||
metakv.setMetavalue(value);
|
||||
metakv.setDatatype(datatype);
|
||||
|
||||
metkvRes.save(metakv);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成随机字符串,作为服务名称
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private String generateLicenseServiceName() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
||||
sb.append(Constants.LICENSE_SERVICE_NAME_PREFIX);
|
||||
sb.append(Base62.generatingRandomAlphanumericString(5));
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -1,15 +1,15 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Beijing Huaxia Chunsong Technology Co., Ltd.
|
||||
* <https://www.chatopera.com>, Licensed under the Chunsong Public
|
||||
* Copyright (C) 2023 Beijing Huaxia Chunsong Technology Co., Ltd.
|
||||
* <https://www.chatopera.com>, Licensed under the Chunsong Public
|
||||
* License, Version 1.0 (the "License"), https://docs.cskefu.com/licenses/v1.html
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* Copyright (C) 2018- Jun. 2023 Chatopera Inc, <https://www.chatopera.com>, Licensed under the Apache License, Version 2.0,
|
||||
* Copyright (C) 2018- Jun. 2023 Chatopera Inc, <https://www.chatopera.com>, Licensed under the Apache License, Version 2.0,
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统, Licensed under the Apache License, Version 2.0,
|
||||
* Copyright (C) 2017 优客服-多渠道客服系统, Licensed under the Apache License, Version 2.0,
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package com.cskefu.cc.util;
|
||||
@ -17,47 +17,68 @@ package com.cskefu.cc.util;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class Base62 {
|
||||
private static final int BINARY = 0x2;
|
||||
private static final int BINARY = 0x2;
|
||||
|
||||
private static final int NUMBER_61 = 0x0000003d;
|
||||
|
||||
static final char[] DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
|
||||
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B',
|
||||
'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
|
||||
'X', 'Y', 'Z' };
|
||||
private static final int NUMBER_61 = 0x0000003d;
|
||||
|
||||
static final char[] DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
|
||||
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B',
|
||||
'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
|
||||
'X', 'Y', 'Z'};
|
||||
|
||||
|
||||
public static String encode(long value){
|
||||
return encode(String.valueOf(value)).toLowerCase() ;
|
||||
}
|
||||
|
||||
public static String encode(String str){
|
||||
String md5Hex = DigestUtils.md5Hex(str);
|
||||
// 6 digit binary can indicate 62 letter & number from 0-9a-zA-Z
|
||||
int binaryLength = 6 * 6;
|
||||
long binaryLengthFixer = Long.valueOf(StringUtils.repeat("1", binaryLength), BINARY);
|
||||
for (int i = 0; i < 4;) {
|
||||
String subString = StringUtils.substring(md5Hex, i * 8, (i + 1) * 8);
|
||||
subString = Long.toBinaryString(Long.valueOf(subString, 16) & binaryLengthFixer);
|
||||
subString = StringUtils.leftPad(subString, binaryLength, "0");
|
||||
StringBuilder sbBuilder = new StringBuilder();
|
||||
for (int j = 0; j < 6; j++) {
|
||||
String subString2 = StringUtils.substring(subString, j * 6, (j + 1) * 6);
|
||||
int charIndex = Integer.valueOf(subString2, BINARY) & NUMBER_61;
|
||||
sbBuilder.append(DIGITS[charIndex]);
|
||||
}
|
||||
String shortUrl = sbBuilder.toString();
|
||||
if(shortUrl!=null){
|
||||
return shortUrl;
|
||||
}
|
||||
}
|
||||
// if all 4 possibilities are already exists
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String encode(long value) {
|
||||
return encode(String.valueOf(value)).toLowerCase();
|
||||
}
|
||||
|
||||
public static String encode(String str) {
|
||||
String md5Hex = DigestUtils.md5Hex(str);
|
||||
// 6 digit binary can indicate 62 letter & number from 0-9a-zA-Z
|
||||
int binaryLength = 6 * 6;
|
||||
long binaryLengthFixer = Long.valueOf(StringUtils.repeat("1", binaryLength), BINARY);
|
||||
for (int i = 0; i < 4; ) {
|
||||
String subString = StringUtils.substring(md5Hex, i * 8, (i + 1) * 8);
|
||||
subString = Long.toBinaryString(Long.valueOf(subString, 16) & binaryLengthFixer);
|
||||
subString = StringUtils.leftPad(subString, binaryLength, "0");
|
||||
StringBuilder sbBuilder = new StringBuilder();
|
||||
for (int j = 0; j < 6; j++) {
|
||||
String subString2 = StringUtils.substring(subString, j * 6, (j + 1) * 6);
|
||||
int charIndex = Integer.valueOf(subString2, BINARY) & NUMBER_61;
|
||||
sbBuilder.append(DIGITS[charIndex]);
|
||||
}
|
||||
String shortUrl = sbBuilder.toString();
|
||||
if (shortUrl != null) {
|
||||
return shortUrl;
|
||||
}
|
||||
}
|
||||
// if all 4 possibilities are already exists
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static void print(Object messagr){
|
||||
System.out.println(messagr);
|
||||
}
|
||||
private static void print(Object messagr) {
|
||||
System.out.println(messagr);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成随机字符串
|
||||
* @param targetStringLength
|
||||
* @return
|
||||
*/
|
||||
public static String generatingRandomAlphanumericString(final int targetStringLength) {
|
||||
int leftLimit = 48; // numeral '0'
|
||||
int rightLimit = 122; // letter 'z'
|
||||
Random random = new Random();
|
||||
|
||||
String generatedString = random.ints(leftLimit, rightLimit + 1)
|
||||
.filter(i -> (i <= 57 || i >= 65) && (i <= 90 || i >= 97))
|
||||
.limit(targetStringLength)
|
||||
.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
|
||||
.toString();
|
||||
|
||||
return generatedString;
|
||||
}
|
||||
}
|
@ -138,6 +138,24 @@ CREATE TABLE `cs_fb_otn_follow` (
|
||||
-- Records of cs_fb_otn_follow
|
||||
-- ----------------------------
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for cs_metakv
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `cs_metakv`;
|
||||
CREATE TABLE `cs_metakv` (
|
||||
`metakey` varchar(512) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '元数据字段名,唯一标识',
|
||||
`metavalue` text COLLATE utf8mb4_unicode_ci COMMENT '元数据值',
|
||||
`createtime` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updatetime` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`datatype` varchar(256) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'string' COMMENT '数据类型',
|
||||
`comment` varchar(1024) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '字段备注描述',
|
||||
PRIMARY KEY (`metakey`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='系统内置元数据';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of cs_metakv
|
||||
-- ----------------------------
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for cs_organ_user
|
||||
-- ----------------------------
|
||||
|
@ -0,0 +1,14 @@
|
||||
USE `cosinee`;
|
||||
-- -----------------
|
||||
-- prepare variables
|
||||
-- -----------------
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `cs_metakey` (
|
||||
`metakey` varchar(512) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '元数据字段名,唯一标识',
|
||||
`metavalue` text COLLATE utf8mb4_unicode_ci COMMENT '元数据值',
|
||||
`createtime` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updatetime` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`datatype` varchar(256) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'string' COMMENT '数据类型',
|
||||
`comment` varchar(1024) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '字段备注描述',
|
||||
PRIMARY KEY (`metakey`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='系统内置元数据';
|
@ -400,11 +400,18 @@
|
||||
<artifactId>compose4j</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.chatopera.bot</groupId>
|
||||
<artifactId>sdk</artifactId>
|
||||
<version>3.5.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.chatopera.store</groupId>
|
||||
<artifactId>store-sdk</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Required for Java 11 https://github.com/cskefu/cskefu/issues/714 -->
|
||||
<dependency>
|
||||
|
Loading…
x
Reference in New Issue
Block a user