From b0365f1a9b1b24fb7d8ca07c19aa33282f723677 Mon Sep 17 00:00:00 2001 From: Hai Liang Wang Date: Tue, 11 Sep 2018 00:18:13 +0800 Subject: [PATCH] =?UTF-8?q?#74=20=E6=94=AF=E6=8C=81=E6=A0=B9=E6=8D=AE?= =?UTF-8?q?=E7=BD=91=E7=AB=99=E6=B8=A0=E9=81=93=E6=A0=87=E8=AF=86=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E8=81=8A=E5=A4=A9=E6=9C=BA=E5=99=A8=E4=BA=BAAPI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cc-chatbot/app/pom.xml | 6 +- .../java/com/chatopera/chatbot/Chatbot.java | 159 ----------- .../com/chatopera/chatbot/ChatbotAPI.java | 237 +++++++++++++++ ...n.java => ChatbotAPIRuntimeException.java} | 4 +- .../{ChatbotTest.java => ChatbotAPITest.java} | 64 ++++- contact-center/app/pom.xml | 8 +- .../service/repository/ChatbotRepository.java | 10 + .../repository/SNSAccountRepository.java | 4 +- .../api/rest/ApiChatbotController.java | 269 ++++++++++++++++++ .../chatopera/cc/webim/web/model/Chatbot.java | 206 ++++++++++++++ .../Chatopera_cc_v1.postman_collection.json | 33 +++ .../config/sql/cskefu-MySQL-slim.sql | 29 ++ 12 files changed, 853 insertions(+), 176 deletions(-) delete mode 100644 cc-chatbot/app/src/main/java/com/chatopera/chatbot/Chatbot.java create mode 100644 cc-chatbot/app/src/main/java/com/chatopera/chatbot/ChatbotAPI.java rename cc-chatbot/app/src/main/java/com/chatopera/chatbot/{ChatbotRuntimeException.java => ChatbotAPIRuntimeException.java} (52%) rename cc-chatbot/app/src/test/java/com/chatopera/chatbot/{ChatbotTest.java => ChatbotAPITest.java} (50%) create mode 100644 contact-center/app/src/main/java/com/chatopera/cc/webim/service/repository/ChatbotRepository.java create mode 100644 contact-center/app/src/main/java/com/chatopera/cc/webim/web/handler/api/rest/ApiChatbotController.java create mode 100644 contact-center/app/src/main/java/com/chatopera/cc/webim/web/model/Chatbot.java diff --git a/cc-chatbot/app/pom.xml b/cc-chatbot/app/pom.xml index 9b2c6a0b..67a195a1 100644 --- a/cc-chatbot/app/pom.xml +++ b/cc-chatbot/app/pom.xml @@ -6,7 +6,7 @@ com.chatopera.chatbot sdk - 1.0-SNAPSHOT + 1.0.1 jar sdk @@ -121,6 +121,10 @@ chatopera http://192.168.2.217:8029/repository/maven-snapshots/ + + chatopera + http://192.168.2.217:8029/repository/maven-releases/ + diff --git a/cc-chatbot/app/src/main/java/com/chatopera/chatbot/Chatbot.java b/cc-chatbot/app/src/main/java/com/chatopera/chatbot/Chatbot.java deleted file mode 100644 index 28bdae01..00000000 --- a/cc-chatbot/app/src/main/java/com/chatopera/chatbot/Chatbot.java +++ /dev/null @@ -1,159 +0,0 @@ -package com.chatopera.chatbot; - -import com.mashape.unirest.http.exceptions.UnirestException; -import org.apache.commons.lang3.StringUtils; -import org.json.JSONObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.HashMap; - -public class Chatbot { - private static final Logger logger = LoggerFactory.getLogger(Chatbot.class); - private String schema; - private String hostname; - private int port; - private String baseUrl; - - private Chatbot() { - } - - public Chatbot(final String schema, final String hostname, final int port, final String version) { - this.schema = schema; - this.hostname = hostname; - this.port = port; - this.baseUrl = schema + "://" + hostname + ":" + Integer.toString(this.port) + "/api/" + version; - } - - public Chatbot(final String schema, final String hostname, final int port) { - this(schema, hostname, port, "v1"); - } - - public Chatbot(final String hostname, final int port) { - this("http", hostname, port); - } - - public String getSchema() { - return schema; - } - - public String getHostname() { - return hostname; - } - - public int getPort() { - return port; - } - - public String getBaseUrl() { - return baseUrl; - } - - /** - * 获取聊天机器人列表 - * - * @return - * @throws ChatbotRuntimeException - */ - public JSONObject getChatbots(final String fields, final String q, final int page, final int limit) throws ChatbotRuntimeException { - try { - HashMap queryString = new HashMap(); - if (StringUtils.isNotBlank(fields)) { - queryString.put("fields", fields); - } - - if (StringUtils.isNotBlank(q)) { - queryString.put("q", q); - } - - queryString.put("page", page); - - if (limit > 0) { - queryString.put("limit", limit); - } - - return RestAPI.get(this.getBaseUrl() + "/chatbot", queryString); - } catch (UnirestException e) { - throw new ChatbotRuntimeException(e.toString()); - } - } - - /** - * 获取聊天机器人详情 - * - * @param chatbotID - * @return - * @throws ChatbotRuntimeException - */ - public JSONObject getChatbot(final String chatbotID) throws ChatbotRuntimeException { - try { - return RestAPI.get(this.getBaseUrl() + "/chatbot/" + chatbotID); - } catch (UnirestException e) { - throw new ChatbotRuntimeException(e.toString()); - } - } - - /** - * validate params - * @param chatbotID - * @param fromUserId - * @param textMessage - */ - private void v(final String chatbotID, final String fromUserId, final String textMessage) throws ChatbotRuntimeException { - if(StringUtils.isBlank(chatbotID)) - throw new ChatbotRuntimeException("[conversation] 不合法的聊天机器人标识。"); - - if(StringUtils.isBlank(fromUserId)) - throw new ChatbotRuntimeException("[conversation] 不合法的用户标识。"); - - if(StringUtils.isBlank(textMessage)) - throw new ChatbotRuntimeException("[conversation] 不合法的消息内容。"); - } - - /** - * 与聊天机器人进行多轮对话 - * @param fromUserId - * @param textMessage - * @param debug - * @return - */ - public JSONObject conversation(final String chatbotID, final String fromUserId, final String textMessage, boolean debug) throws ChatbotRuntimeException { - v(chatbotID, fromUserId, textMessage); - HashMap body = new HashMap(); - body.put("fromUserId", fromUserId); - body.put("textMessage", textMessage); - body.put("isDebug", debug); - - logger.info("conversation body {}", body); - - try { - JSONObject resp = RestAPI.post(this.getBaseUrl() + "/chatbot/" + chatbotID + "/conversation/query", body); - return resp; - } catch (UnirestException e) { - throw new ChatbotRuntimeException(e.toString()); - } - } - - /** - * 检索知识库 - * @param chatbotID - * @param fromUserId - * @param textMessage - * @param isDebug - * @return - */ - public JSONObject faq(final String chatbotID, final String fromUserId, final String textMessage, final boolean isDebug) throws ChatbotRuntimeException { - v(chatbotID, fromUserId, textMessage); - HashMap body = new HashMap(); - body.put("fromUserId", fromUserId); - body.put("query", textMessage); - body.put("isDebug", isDebug); - try { - JSONObject resp = RestAPI.post(this.getBaseUrl() + "/chatbot/" + chatbotID + "/faq/query", body); - return resp; - } catch (UnirestException e) { - throw new ChatbotRuntimeException(e.toString()); - } - } - -} diff --git a/cc-chatbot/app/src/main/java/com/chatopera/chatbot/ChatbotAPI.java b/cc-chatbot/app/src/main/java/com/chatopera/chatbot/ChatbotAPI.java new file mode 100644 index 00000000..6a356349 --- /dev/null +++ b/cc-chatbot/app/src/main/java/com/chatopera/chatbot/ChatbotAPI.java @@ -0,0 +1,237 @@ +package com.chatopera.chatbot; + +import com.mashape.unirest.http.exceptions.UnirestException; +import org.apache.commons.lang3.StringUtils; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashMap; + +public class ChatbotAPI { + private static final Logger logger = LoggerFactory.getLogger(ChatbotAPI.class); + private String schema; + private String hostname; + private int port; + private String baseUrl; + + private ChatbotAPI() { + } + + + public ChatbotAPI(final String baseUrl) throws ChatbotAPIRuntimeException, MalformedURLException { + if (StringUtils.isBlank(baseUrl)) + throw new ChatbotAPIRuntimeException("智能问答引擎URL不能为空。"); + + URL url = new URL(baseUrl); + this.schema = url.getProtocol(); + this.hostname = url.getHost(); + this.port = url.getPort(); + + if (port == -1) { + this.baseUrl = this.schema + "://" + this.hostname + "/api/v1"; + } else { + this.baseUrl = this.schema + "://" + this.hostname + ":" + this.port + "/api/v1"; + } + + } + + public ChatbotAPI(final String schema, final String hostname, final int port, final String version) { + this.schema = schema; + this.hostname = hostname; + this.port = port; + this.baseUrl = schema + "://" + hostname + ":" + Integer.toString(this.port) + "/api/" + version; + } + + public ChatbotAPI(final String schema, final String hostname, final int port) { + this(schema, hostname, port, "v1"); + } + + public ChatbotAPI(final String hostname, final int port) { + this("http", hostname, port); + } + + public String getSchema() { + return schema; + } + + public String getHostname() { + return hostname; + } + + public int getPort() { + return port; + } + + public String getBaseUrl() { + return baseUrl; + } + + /** + * 获取聊天机器人列表 + * + * @return + * @throws ChatbotAPIRuntimeException + */ + public JSONObject getChatbots(final String fields, final String q, final int page, final int limit) throws ChatbotAPIRuntimeException { + try { + HashMap queryString = new HashMap(); + if (StringUtils.isNotBlank(fields)) { + queryString.put("fields", fields); + } + + if (StringUtils.isNotBlank(q)) { + queryString.put("q", q); + } + + queryString.put("page", page); + + if (limit > 0) { + queryString.put("limit", limit); + } + + return RestAPI.get(this.getBaseUrl() + "/chatbot", queryString); + } catch (UnirestException e) { + throw new ChatbotAPIRuntimeException(e.toString()); + } + } + + /** + * 通过ChatbotID检查一个聊天机器人是否存在 + * + * @param chatbotID + * @return + */ + public boolean exists(final String chatbotID) throws ChatbotAPIRuntimeException { + try { + JSONObject result = this.getChatbot(chatbotID); + int rc = result.getInt("rc"); + if (rc == 0) { + return true; + } else if (rc == 3) { + return false; + } else { + throw new ChatbotAPIRuntimeException("查询聊天机器人异常返回。"); + } + } catch (Exception e) { + throw new ChatbotAPIRuntimeException(e.toString()); + } + } + + + /** + * 创建聊天机器人 + * @param chatbotID 聊天机器人标识,由[a-zA-Z0-9-]组成,字母开头 + * @param name 拟人化的名字 + * @param primaryLanguage 首选语言,支持 [zh_CN|en_US] + * @param fallback 兜底回复 + * @param description 描述 + * @param welcome 欢迎语 + * @return + */ + public JSONObject createBot(final String chatbotID, + final String name, + final String primaryLanguage, + final String fallback, + final String description, + final String welcome) throws ChatbotAPIRuntimeException { + HashMap body = new HashMap(); + body.put("chatbotID", chatbotID); + body.put("name", name); + body.put("primaryLanguage", primaryLanguage); + body.put("description", description); + body.put("fallback", fallback); + body.put("welcome", welcome); + + try { + return RestAPI.post(this.getBaseUrl() + "/chatbot/" + chatbotID, body); + } catch (UnirestException e) { + throw new ChatbotAPIRuntimeException(e.toString()); + } + } + + + /** + * 获取聊天机器人详情 + * + * @param chatbotID + * @return + * @throws ChatbotAPIRuntimeException + */ + public JSONObject getChatbot(final String chatbotID) throws ChatbotAPIRuntimeException { + try { + return RestAPI.get(this.getBaseUrl() + "/chatbot/" + chatbotID); + } catch (UnirestException e) { + throw new ChatbotAPIRuntimeException(e.toString()); + } + } + + /** + * validate params + * + * @param chatbotID + * @param fromUserId + * @param textMessage + */ + private void v(final String chatbotID, final String fromUserId, final String textMessage) throws ChatbotAPIRuntimeException { + if (StringUtils.isBlank(chatbotID)) + throw new ChatbotAPIRuntimeException("[conversation] 不合法的聊天机器人标识。"); + + if (StringUtils.isBlank(fromUserId)) + throw new ChatbotAPIRuntimeException("[conversation] 不合法的用户标识。"); + + if (StringUtils.isBlank(textMessage)) + throw new ChatbotAPIRuntimeException("[conversation] 不合法的消息内容。"); + } + + /** + * 与聊天机器人进行多轮对话 + * + * @param fromUserId + * @param textMessage + * @param debug + * @return + */ + public JSONObject conversation(final String chatbotID, final String fromUserId, final String textMessage, boolean debug) throws ChatbotAPIRuntimeException { + v(chatbotID, fromUserId, textMessage); + HashMap body = new HashMap(); + body.put("fromUserId", fromUserId); + body.put("textMessage", textMessage); + body.put("isDebug", debug); + + logger.info("conversation body {}", body); + + try { + JSONObject resp = RestAPI.post(this.getBaseUrl() + "/chatbot/" + chatbotID + "/conversation/query", body); + return resp; + } catch (UnirestException e) { + throw new ChatbotAPIRuntimeException(e.toString()); + } + } + + /** + * 检索知识库 + * + * @param chatbotID + * @param fromUserId + * @param textMessage + * @param isDebug + * @return + */ + public JSONObject faq(final String chatbotID, final String fromUserId, final String textMessage, final boolean isDebug) throws ChatbotAPIRuntimeException { + v(chatbotID, fromUserId, textMessage); + HashMap body = new HashMap(); + body.put("fromUserId", fromUserId); + body.put("query", textMessage); + body.put("isDebug", isDebug); + try { + JSONObject resp = RestAPI.post(this.getBaseUrl() + "/chatbot/" + chatbotID + "/faq/query", body); + return resp; + } catch (UnirestException e) { + throw new ChatbotAPIRuntimeException(e.toString()); + } + } + +} diff --git a/cc-chatbot/app/src/main/java/com/chatopera/chatbot/ChatbotRuntimeException.java b/cc-chatbot/app/src/main/java/com/chatopera/chatbot/ChatbotAPIRuntimeException.java similarity index 52% rename from cc-chatbot/app/src/main/java/com/chatopera/chatbot/ChatbotRuntimeException.java rename to cc-chatbot/app/src/main/java/com/chatopera/chatbot/ChatbotAPIRuntimeException.java index 76fe8c3f..6be8bc30 100644 --- a/cc-chatbot/app/src/main/java/com/chatopera/chatbot/ChatbotRuntimeException.java +++ b/cc-chatbot/app/src/main/java/com/chatopera/chatbot/ChatbotAPIRuntimeException.java @@ -2,8 +2,8 @@ package com.chatopera.chatbot; import com.mashape.unirest.http.exceptions.UnirestException; -public class ChatbotRuntimeException extends Exception{ - public ChatbotRuntimeException(String msg) { +public class ChatbotAPIRuntimeException extends Exception{ + public ChatbotAPIRuntimeException(String msg) { super(msg); } } diff --git a/cc-chatbot/app/src/test/java/com/chatopera/chatbot/ChatbotTest.java b/cc-chatbot/app/src/test/java/com/chatopera/chatbot/ChatbotAPITest.java similarity index 50% rename from cc-chatbot/app/src/test/java/com/chatopera/chatbot/ChatbotTest.java rename to cc-chatbot/app/src/test/java/com/chatopera/chatbot/ChatbotAPITest.java index 90197a54..515ead54 100644 --- a/cc-chatbot/app/src/test/java/com/chatopera/chatbot/ChatbotTest.java +++ b/cc-chatbot/app/src/test/java/com/chatopera/chatbot/ChatbotAPITest.java @@ -7,20 +7,22 @@ import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.net.MalformedURLException; + /** * Unit test for simple App. */ -public class ChatbotTest +public class ChatbotAPITest extends TestCase { - private static final Logger logger = LoggerFactory.getLogger(ChatbotTest.class); - private Chatbot cb; + private static final Logger logger = LoggerFactory.getLogger(ChatbotAPITest.class); + private ChatbotAPI cb; /** * Create the test case * * @param testName name of the test case */ - public ChatbotTest(String testName) { + public ChatbotAPITest(String testName) { super(testName); } @@ -28,11 +30,11 @@ public class ChatbotTest * @return the suite of tests being tested */ public static Test suite() { - return new TestSuite(ChatbotTest.class); + return new TestSuite(ChatbotAPITest.class); } public void setUp() { - this.cb = new Chatbot("http", "lhc-dev", 8003, "v1"); + this.cb = new ChatbotAPI("http", "lhc-dev", 8003, "v1"); } /** @@ -46,7 +48,7 @@ public class ChatbotTest try { JSONObject resp = this.cb.getChatbot("co_bot_1"); logger.info("[testGetChatbot] {}", resp.toString()); - } catch (ChatbotRuntimeException e) { + } catch (ChatbotAPIRuntimeException e) { e.printStackTrace(); } } @@ -55,27 +57,65 @@ public class ChatbotTest try { JSONObject resp = this.cb.getChatbots("name chatbotID", null, 0, 10); logger.info("[testGetChatbots] resp {}", resp.toString()); - } catch (ChatbotRuntimeException e) { + } catch (ChatbotAPIRuntimeException e) { e.printStackTrace(); } } - public void testConversation(){ + public void testConversation() { try { JSONObject resp = this.cb.conversation("co_bot_1", "sdktest", "华夏春松在哪里", false); logger.info("[testConversation] resp {}", resp.toString()); - } catch (ChatbotRuntimeException e) { + } catch (ChatbotAPIRuntimeException e) { e.printStackTrace(); } } - public void testFaq(){ + public void testFaq() { try { JSONObject resp = this.cb.faq("co_bot_1", "sdktest", "华夏春松在哪里", false); logger.info("[testFaq] resp {}", resp.toString()); - } catch (ChatbotRuntimeException e) { + } catch (ChatbotAPIRuntimeException e) { e.printStackTrace(); } } + public void testParseUrl() { + try { + ChatbotAPI c = new ChatbotAPI("https://local:8000/"); + logger.info("chatbot baseUrl {}", c.getBaseUrl()); + assertEquals("https://local:8000/api/v1", c.getBaseUrl()); + } catch (ChatbotAPIRuntimeException e) { + e.printStackTrace(); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + } + + public void testExists() { + JSONObject profile = null; + try { + assertTrue(this.cb.exists("co_bot_1")); + } catch (ChatbotAPIRuntimeException e) { + e.printStackTrace(); + } + } + + public void testCreateBot() { + try { + JSONObject j = this.cb.createBot("cc_bot_2", + "小云2", + "zh_CN", + "我不了解。", + "小云机器人", + "你好,我是小云。"); + logger.info("[testCreateBot] {}", j); + } catch (ChatbotAPIRuntimeException e) { + e.printStackTrace(); + } + } + + } + + diff --git a/contact-center/app/pom.xml b/contact-center/app/pom.xml index 1b3a2dad..81f6eab9 100644 --- a/contact-center/app/pom.xml +++ b/contact-center/app/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 com.chatopera.cc contact-center @@ -308,6 +309,11 @@ aliyun-java-sdk-dysmsapi 1.0.0 + + com.chatopera.chatbot + sdk + 1.0.1 + diff --git a/contact-center/app/src/main/java/com/chatopera/cc/webim/service/repository/ChatbotRepository.java b/contact-center/app/src/main/java/com/chatopera/cc/webim/service/repository/ChatbotRepository.java new file mode 100644 index 00000000..b9db054c --- /dev/null +++ b/contact-center/app/src/main/java/com/chatopera/cc/webim/service/repository/ChatbotRepository.java @@ -0,0 +1,10 @@ +package com.chatopera.cc.webim.service.repository; + +import com.chatopera.cc.webim.web.model.Chatbot; +import org.springframework.data.jpa.repository.JpaRepository; + +public abstract interface ChatbotRepository extends JpaRepository { + + public abstract boolean existsByChatbotIDAndOrgi(String chatbotID, String orgi); + public abstract boolean existsBySnsAccountIdentifierAndOrgi(String snsid, String orgi); +} diff --git a/contact-center/app/src/main/java/com/chatopera/cc/webim/service/repository/SNSAccountRepository.java b/contact-center/app/src/main/java/com/chatopera/cc/webim/service/repository/SNSAccountRepository.java index 01105550..1481f5f7 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/webim/service/repository/SNSAccountRepository.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/webim/service/repository/SNSAccountRepository.java @@ -27,7 +27,9 @@ public abstract interface SNSAccountRepository extends JpaRepository { public abstract SNSAccount findByIdAndOrgi(String paramString, String orgi); - + + public abstract boolean existsBySnsidAndSnstypeAndOrgi(String snsid, String snsType, String orgi); + public abstract SNSAccount findBySnsid(String snsid); public abstract SNSAccount findBySnsidAndOrgi(String snsid, String orgi); diff --git a/contact-center/app/src/main/java/com/chatopera/cc/webim/web/handler/api/rest/ApiChatbotController.java b/contact-center/app/src/main/java/com/chatopera/cc/webim/web/handler/api/rest/ApiChatbotController.java new file mode 100644 index 00000000..28a3fc29 --- /dev/null +++ b/contact-center/app/src/main/java/com/chatopera/cc/webim/web/handler/api/rest/ApiChatbotController.java @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2018 Chatopera Inc, + * + * 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.webim.web.handler.api.rest; + +import com.chatopera.cc.util.Menu; +import com.chatopera.cc.util.UKTools; +import com.chatopera.cc.util.exception.CallOutRecordException; +import com.chatopera.cc.webim.service.repository.ChatbotRepository; +import com.chatopera.cc.webim.service.repository.SNSAccountRepository; +import com.chatopera.cc.webim.web.handler.Handler; +import com.chatopera.cc.webim.web.handler.api.request.RestUtils; +import com.chatopera.cc.webim.web.model.Chatbot; +import com.chatopera.cc.webim.web.model.User; +import com.chatopera.chatbot.ChatbotAPI; +import com.chatopera.chatbot.ChatbotAPIRuntimeException; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +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.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.Arrays; +import java.util.Date; +import java.util.HashSet; + +@RestController +@RequestMapping("/api/chatbot") +@Api(value = "聊天机器人", description = "请求聊天机器人服务") +public class ApiChatbotController extends Handler { + private final static Logger logger = LoggerFactory.getLogger(ApiChatbotController.class); + private final HashSet VALID_LANGS = new HashSet(Arrays.asList(new String[]{"zh_CN", "en_US"})); + private final HashSet VALID_WORKMODELS = new HashSet(Arrays.asList(new String[]{"客服机器人优先", "人工客服优先"})); + private final String SNS_TYPE_WEBIM = "webim"; + + @Value("${license.client.id}") + private String clientId; + + @Autowired + private ChatbotRepository chatbotRes; + + @Autowired + private SNSAccountRepository snsAccountRes; + + @RequestMapping(method = RequestMethod.POST) + @Menu(type = "apps", subtype = "chatbot", access = true) + @ApiOperation("聊天机器人") + public ResponseEntity operations(HttpServletRequest request, @RequestBody final String body) throws CallOutRecordException { + final JsonObject j = (new JsonParser()).parse(body).getAsJsonObject(); + logger.info("[chatbot] operations payload {}", j.toString()); + JsonObject json = new JsonObject(); + HttpHeaders headers = RestUtils.header(); + User curruser = super.getUser(request); + + 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": + json = create(j, curruser.getId(), curruser.getOrgan(), curruser.getOrgi()); + break; + default: + json.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_2); + json.addProperty(RestUtils.RESP_KEY_ERROR, "不合法的操作。"); + } + } + return new ResponseEntity(json.toString(), headers, HttpStatus.OK); + } + + /** + * 使用snsid得到ChatbotID + * + * @param snsid + * @return + */ + private String resolveChatbotIDWithSnsid(String snsid) { + return clientId + "_" + snsid; + } + + + /** + * 使用chatbotID得到snsid + * + * @param chatbotID + * @return + */ + private String resolveSnsidWithChatbotID(String chatbotID) { + return StringUtils.remove(chatbotID, clientId + "_"); + } + + /** + * 创建聊天机器人 + * + * @param j + * @param creater + * @param organ + * @param orgi + * @return + */ + private JsonObject create(JsonObject j, String creater, String organ, String orgi) { + JsonObject resp = new JsonObject(); + String baseUrl = null; + String chatbotID = null; + String name = null; + String description = null; + String fallback = null; + String welcome = null; + String primaryLanguage = null; + String snsid = null; + String workmode = null; + + // 验证数据: 必须字段 + if ((!j.has("baseUrl")) || StringUtils.isBlank(j.get("baseUrl").getAsString())) { + resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_3); + resp.addProperty(RestUtils.RESP_KEY_ERROR, "不合法的参数,未传入【baseUrl】。"); + return resp; + } else { + baseUrl = j.get("baseUrl").getAsString(); + } + + if ((!j.has("name")) || StringUtils.isBlank(j.get("name").getAsString())) { + resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_3); + resp.addProperty(RestUtils.RESP_KEY_ERROR, "不合法的参数,未传入【name】。"); + return resp; + } else { + name = j.get("name").getAsString(); + } + + if (!(j.has("primaryLanguage") && VALID_LANGS.contains(j.get("primaryLanguage").getAsString()))) { + resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_3); + resp.addProperty(RestUtils.RESP_KEY_ERROR, "不合法的参数,未传入有效【primaryLanguage】。"); + return resp; + } else { + primaryLanguage = j.get("primaryLanguage").getAsString(); + } + + if (!(j.has("workmode") && 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, SNS_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; + } + } + + chatbotID = resolveChatbotIDWithSnsid(snsid); + if (chatbotRes.existsByChatbotIDAndOrgi(chatbotID, orgi)) { + resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_3); + resp.addProperty(RestUtils.RESP_KEY_ERROR, "不合法的参数,数据库中存在该聊天机器人。"); + return resp; + } + + if ((!j.has("fallback")) || StringUtils.isBlank(j.get("fallback").getAsString())) { + resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_3); + resp.addProperty(RestUtils.RESP_KEY_ERROR, "不合法的参数,未传入【fallback】。"); + return resp; + } else { + fallback = j.get("fallback").getAsString(); + } + + // 可选字段 + if (j.has("description")) + description = j.get("description").getAsString(); + + if (j.has("welcome")) + welcome = j.get("welcome").getAsString(); + + try { + ChatbotAPI capi = new ChatbotAPI(baseUrl); + JSONObject result = capi.createBot(chatbotID, + name, + primaryLanguage, + fallback, + description, + welcome); + + if (result.getInt("rc") == 0) { + // 创建成功 + Chatbot c = new Chatbot(); + c.setId(UKTools.getUUID()); + c.setBaseUrl(capi.getBaseUrl()); + c.setChatbotID(chatbotID); + c.setDescription(description); + c.setFallback(fallback); + c.setPrimaryLanguage(primaryLanguage); + c.setWelcome(welcome); + c.setName(name); + + // 默认不开启 + c.setEnabled(false); + c.setCreater(creater); + c.setOrgan(organ); + c.setOrgi(orgi); + c.setChannel(SNS_TYPE_WEBIM); + c.setSnsAccountIdentifier(snsid); + Date dt = new Date(); + c.setCreatetime(dt); + c.setUpdatetime(dt); + c.setWorkmode(workmode); + + chatbotRes.save(c); + + resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_SUCC); + resp.addProperty(RestUtils.RESP_KEY_DATA, "创建成功。"); + return resp; + } else { + // 创建失败 + resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_6); + resp.addProperty(RestUtils.RESP_KEY_ERROR, "创建失败,失败原因 [" + result.getString("error") + "]"); + return resp; + } + } catch (ChatbotAPIRuntimeException e) { + resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_5); + resp.addProperty(RestUtils.RESP_KEY_ERROR, "智能问答引擎服务异常。" + e.toString()); + return resp; + } catch (MalformedURLException e) { + resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_FAIL_4); + resp.addProperty(RestUtils.RESP_KEY_ERROR, "不合法的智能问答引擎服务URL。"); + return resp; + } + } + +} diff --git a/contact-center/app/src/main/java/com/chatopera/cc/webim/web/model/Chatbot.java b/contact-center/app/src/main/java/com/chatopera/cc/webim/web/model/Chatbot.java new file mode 100644 index 00000000..38a681e0 --- /dev/null +++ b/contact-center/app/src/main/java/com/chatopera/cc/webim/web/model/Chatbot.java @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2018 Chatopera Inc, + * + * 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.webim.web.model; + +import com.chatopera.chatbot.ChatbotAPI; +import com.chatopera.chatbot.ChatbotAPIRuntimeException; +import org.hibernate.annotations.GenericGenerator; + +import javax.persistence.*; +import java.net.MalformedURLException; +import java.util.Date; + +@Entity +@Table(name = "cs_chatbot") +@org.hibernate.annotations.Proxy(lazy = false) +public class Chatbot { + + private String id; + private String name; + + @Column(unique=true) + private String chatbotID; + + private String description; + private String primaryLanguage; + private String fallback; + private String welcome; + private String baseUrl; // 智能问答引擎服务地址 + private String orgi; // 租户标识 + private String organ; // 组织机构 + private String creater; // 创建者 + private String channel; // 渠道类型 + + @Column(unique=true) + private String snsAccountIdentifier; // 渠道唯一标识 + private boolean enabled; // 当前是否被启用 + private String workmode; // 工作模式, 机器人优先还是人工客服优先 + + private ChatbotAPI api; + private Date createtime; + private Date updatetime; + + + @Id + @Column(length = 32) + @GeneratedValue(generator = "system-uuid") + @GenericGenerator(name = "system-uuid", strategy = "assigned") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getChatbotID() { + return chatbotID; + } + + public void setChatbotID(String chatbotID) { + this.chatbotID = chatbotID; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public String getPrimaryLanguage() { + return primaryLanguage; + } + + public void setPrimaryLanguage(String primaryLanguage) { + this.primaryLanguage = primaryLanguage; + } + + public String getFallback() { + return fallback; + } + + public void setFallback(String fallback) { + this.fallback = fallback; + } + + public String getWelcome() { + return welcome; + } + + public void setWelcome(String welcome) { + this.welcome = welcome; + } + + public String getBaseUrl() { + return baseUrl; + } + + public void setBaseUrl(String baseUrl) { + this.baseUrl = baseUrl; + } + + public String getOrgi() { + return orgi; + } + + public void setOrgi(String orgi) { + this.orgi = orgi; + } + + public String getOrgan() { + return organ; + } + + public void setOrgan(String organ) { + this.organ = organ; + } + + public String getCreater() { + return creater; + } + + public void setCreater(String creater) { + this.creater = creater; + } + + public String getChannel() { + return channel; + } + + public void setChannel(String channel) { + this.channel = channel; + } + + public String getSnsAccountIdentifier() { + return snsAccountIdentifier; + } + + public void setSnsAccountIdentifier(String snsAccountIdentifier) { + this.snsAccountIdentifier = snsAccountIdentifier; + } + + public String getWorkmode() { + return workmode; + } + + public void setWorkmode(String workmode) { + this.workmode = workmode; + } + + @Transient + public ChatbotAPI getApi() throws MalformedURLException, ChatbotAPIRuntimeException { + if(api == null){ + api = new ChatbotAPI(this.baseUrl); + } + return api; + } + + public Date getCreatetime() { + return createtime; + } + + public void setCreatetime(Date createtime) { + this.createtime = createtime; + } + + public Date getUpdatetime() { + return updatetime; + } + + public void setUpdatetime(Date updatetime) { + this.updatetime = updatetime; + } +} + + diff --git a/contact-center/config/postman/Chatopera_cc_v1.postman_collection.json b/contact-center/config/postman/Chatopera_cc_v1.postman_collection.json index 84631c88..8cf9a428 100644 --- a/contact-center/config/postman/Chatopera_cc_v1.postman_collection.json +++ b/contact-center/config/postman/Chatopera_cc_v1.postman_collection.json @@ -525,6 +525,39 @@ } }, "response": [] + }, + { + "name": "机器人客服:创建", + "request": { + "method": "POST", + "header": [ + { + "key": "authorization", + "value": "00bf99785103475c896435ef7216ebd1" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"ops\": \"create\",\n \"primaryLanguage\": \"zh_CN\",\n \"snsid\": \"104EAc\",\n \"name\": \"小C\",\n \"baseUrl\": \"http://lhc-dev:8003\",\n \"description\": \"描述\",\n \"fallback\": \"我不理解。\",\n \"welcome\": \"你好\",\n \"workmode\": \"客服机器人优先\"\n}" + }, + "url": { + "raw": "http://{{IP}}:{{PORT}}/api/chatbot", + "protocol": "http", + "host": [ + "{{IP}}" + ], + "port": "{{PORT}}", + "path": [ + "api", + "chatbot" + ] + } + }, + "response": [] } ], "event": [ diff --git a/contact-center/config/sql/cskefu-MySQL-slim.sql b/contact-center/config/sql/cskefu-MySQL-slim.sql index 5e207bb9..3c7ec841 100644 --- a/contact-center/config/sql/cskefu-MySQL-slim.sql +++ b/contact-center/config/sql/cskefu-MySQL-slim.sql @@ -3349,6 +3349,35 @@ CREATE TABLE `uk_skill` ( PRIMARY KEY (`ID`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='技能组表'; + +-- ---------------------------- +-- Table structure for cs_chatbot +-- ---------------------------- +DROP TABLE IF EXISTS `cs_chatbot`; +CREATE TABLE `cs_chatbot` ( + `ID` varchar(32) NOT NULL COMMENT '主键ID', + `base_url` varchar(255) NOT NULL COMMENT '基础URL', + `creater` varchar(32) NOT NULL COMMENT '创建人', + `orgi` varchar(255) NOT NULL COMMENT '租户ID', + `organ` varchar(32) NOT NULL COMMENT '部门ID', + `createtime` datetime NOT NULL COMMENT '创建时间', + `updatetime` datetime NOT NULL COMMENT '更新时间', + `chatbotid` varchar(255) NOT NULL COMMENT '聊天机器人ID', + `name` varchar(255) NOT NULL COMMENT '聊天机器人名字', + `description` varchar(255) NOT NULL COMMENT '描述', + `primary_language` varchar(20) NOT NULL COMMENT '首选语言', + `fallback` varchar(255) DEFAULT NULL COMMENT '兜底回复', + `welcome` varchar(255) DEFAULT NULL COMMENT '欢迎语', + `channel` varchar(32) NOT NULL COMMENT '渠道类型', + `sns_account_identifier` varchar(255) NOT NULL COMMENT '渠道标识', + `enabled` tinyint(1) DEFAULT '0' COMMENT '是否开启', + `workmode` varchar(32) NOT NULL COMMENT '工作模式', + PRIMARY KEY (`ID`) USING BTREE, + UNIQUE KEY `snsid` (`sns_account_identifier`,`orgi`) USING BTREE COMMENT '按照渠道标识唯一', + UNIQUE KEY `chatbotID` (`chatbotid`,`orgi`) USING BTREE COMMENT '按照ChatbotID唯一' +) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='机器人客服表'; + + -- ---------------------------- -- Table structure for uk_snsaccount -- ----------------------------