1
0
mirror of https://github.com/chatopera/cosin.git synced 2025-08-01 16:38:02 +08:00

#75 支持和问答引擎的初步集成

This commit is contained in:
Hai Liang Wang 2018-09-13 03:24:49 +08:00
parent a0d26b1dbf
commit 87f4055b53
10 changed files with 1544 additions and 1477 deletions

View File

@ -21,6 +21,7 @@ import java.util.concurrent.Executors;
import com.chatopera.cc.concurrent.chatbot.ChatbotDisruptorExceptionHandler;
import com.chatopera.cc.concurrent.chatbot.ChatbotEventFactory;
import com.chatopera.cc.concurrent.chatbot.ChatbotEventHandler;
import com.chatopera.cc.concurrent.multiupdate.MultiUpdateEventFactory;
import com.chatopera.cc.concurrent.multiupdate.MultiUpdateEventHandler;
import org.springframework.context.annotation.Bean;
@ -65,7 +66,7 @@ public class DisruptorConfigure {
Executor executor = Executors.newCachedThreadPool();
ChatbotEventFactory factory = new ChatbotEventFactory();
Disruptor<UserDataEvent> disruptor = new Disruptor<UserDataEvent>(factory, 1024, executor, ProducerType.SINGLE , new SleepingWaitStrategy());
disruptor.handleEventsWith(new MultiUpdateEventHandler());
disruptor.handleEventsWith(new ChatbotEventHandler());
disruptor.setDefaultExceptionHandler(new ChatbotDisruptorExceptionHandler());
disruptor.start();
return disruptor;

View File

@ -17,21 +17,23 @@ package com.chatopera.cc.app.im.handler;
import com.chatopera.cc.app.algorithm.AutomaticServiceDist;
import com.chatopera.cc.app.basic.MainContext;
import com.chatopera.cc.app.im.util.ChatbotUtils;
import com.chatopera.cc.util.IP;
import com.chatopera.cc.util.IPTools;
import com.chatopera.cc.app.basic.MainUtils;
import com.chatopera.cc.app.im.client.NettyClients;
import com.chatopera.cc.app.model.*;
import com.chatopera.cc.app.cache.CacheHelper;
import com.chatopera.cc.app.persistence.repository.AgentUserRepository;
import com.chatopera.cc.app.persistence.repository.ConsultInviteRepository;
import com.chatopera.cc.app.persistence.repository.OnlineUserRepository;
import com.chatopera.cc.app.im.router.OutMessageRouter;
import com.chatopera.cc.util.OnlineUserUtils;
import com.chatopera.cc.app.im.client.NettyClients;
import com.chatopera.cc.app.im.message.AgentStatusMessage;
import com.chatopera.cc.app.im.message.ChatMessage;
import com.chatopera.cc.app.im.message.NewRequestMessage;
import com.chatopera.cc.app.im.util.ChatbotUtils;
import com.chatopera.cc.app.model.*;
import com.chatopera.cc.app.persistence.repository.AgentUserRepository;
import com.chatopera.cc.app.persistence.repository.ChatbotRepository;
import com.chatopera.cc.app.persistence.repository.ConsultInviteRepository;
import com.chatopera.cc.app.persistence.repository.OnlineUserRepository;
import com.chatopera.cc.concurrent.chatbot.ChatbotEvent;
import com.chatopera.cc.util.Constants;
import com.chatopera.cc.util.IP;
import com.chatopera.cc.util.IPTools;
import com.chatopera.cc.util.OnlineUserUtils;
import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
@ -53,6 +55,7 @@ public class ChatbotEventHandler {
private AgentUserRepository agentUserRes;
private OnlineUserRepository onlineUserRes;
private ChatbotRepository chatbotRes;
@Autowired
public ChatbotEventHandler(SocketIOServer server) {
@ -73,9 +76,9 @@ public class ChatbotEventHandler {
Date now = new Date();
if (StringUtils.isNotBlank(user)) {
// /**
// * 加入到 缓存列表
// */
/**
* 加入到 缓存列表
*/
NettyClients.getInstance().putChatbotEventClient(user, client);
MessageOutContent outMessage = new MessageOutContent();
CousultInvite invite = OnlineUserUtils.cousult(appid, orgi, MainContext.getContext().getBean(ConsultInviteRepository.class));
@ -195,62 +198,71 @@ public class ChatbotEventHandler {
String orgi = client.getHandshakeData().getSingleUrlParam("orgi");
String aiid = client.getHandshakeData().getSingleUrlParam("aiid");
String user = client.getHandshakeData().getSingleUrlParam("userid");
if (data.getType() == null) {
data.setType("message");
logger.info("[chatbot] onEvent message: orgi {}, aiid {}, userid {}, dataType {}", orgi, aiid, user, data.getType());
// ignore event if dataType is not message.
if (!StringUtils.equals(data.getType(), Constants.IM_MESSAGE_TYPE_MESSAGE)) {
return;
}
AgentUser agentUser = (AgentUser) CacheHelper.getAgentUserCacheBean().getCacheObject(user, orgi);
// ignore event if no agentUser found.
if (agentUser == null)
return;
/**
* 以下代码主要用于检查 访客端的字数限制
*/
CousultInvite invite = OnlineUserUtils.cousult(data.getAppid(), data.getOrgi(), MainContext.getContext().getBean(ConsultInviteRepository.class));
if (invite != null && invite.getMaxwordsnum() > 0) {
if (!StringUtils.isBlank(data.getMessage()) && data.getMessage().length() > invite.getMaxwordsnum()) {
// 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.isBlank(data.getMessage()) && data.getMessage().length() > 300) {
} else if (StringUtils.isNotBlank(data.getMessage()) && data.getMessage().length() > 300) {
data.setMessage(data.getMessage().substring(0, 300));
}
data.setSessionid(MainUtils.getContextID(client.getSessionId().toString()));
/**
* 处理表情
*/
data.setMessage(MainUtils.processEmoti(data.getMessage()));
data.setMessage(MainUtils.processEmoti(data.getMessage())); // 处理表情
data.setTousername(invite.getAiname());
data.setAiid(aiid);
data.setAgentserviceid(agentUser.getAgentserviceid());
data.setChannel(agentUser.getChannel());
data.setContextid(agentUser.getAgentserviceid()); // 一定要设置 ContextID
data.setCalltype(MainContext.CallTypeEnum.IN.toString());
AgentUser agentUser = (AgentUser) CacheHelper.getAgentUserCacheBean().getCacheObject(user, orgi);
if (agentUser != null) {
data.setAgentserviceid(agentUser.getAgentserviceid());
data.setChannel(agentUser.getChannel());
/**
* 一定要设置 ContextID
*/
data.setContextid(agentUser.getAgentserviceid());
}
MessageOutContent outMessage = ChatbotUtils.createTextMessage(data, data.getAppid(), data.getChannel(), MainContext.CallTypeEnum.IN.toString(), MainContext.ChatbotItemType.USERINPUT.toString(), data.getUserid());
if (StringUtils.isNotBlank(data.getUserid()) && MainContext.MessageTypeEnum.MESSAGE.toString().equals(data.getType())) {
if (!StringUtils.isBlank(data.getTouser())) {
OutMessageRouter router = null;
router = (OutMessageRouter) MainContext.getContext().getBean(data.getChannel());
if (router != null) {
router.handler(data.getTouser(), MainContext.MessageTypeEnum.MESSAGE.toString(), data.getAppid(), outMessage);
}
}
if (agentUser != null) {
Date now = new Date();
agentUser.setUpdatetime(now);
agentUser.setLastmessage(now);
agentUser.setLastmsg(data.getMessage());
CacheHelper.getAgentUserCacheBean().put(user, agentUser, MainContext.SYSTEM_ORGI);
}
}
ChatbotUtils.createTextMessage(data,
MainContext.CallTypeEnum.IN.toString(),
MainContext.ChatbotItemType.USERINPUT.toString());
// 更新访客咨询记录
agentUser.setUpdatetime(now);
agentUser.setLastmessage(now);
agentUser.setLastmsg(data.getMessage());
CacheHelper.getAgentUserCacheBean().put(user, agentUser, orgi);
getAgentUserRes().save(agentUser);
// 发送消息给Bot
MainUtils.chatbot(new ChatbotEvent<ChatMessage>(data,
getChatbotRes(),
Constants.CHATBOT_EVENT_TYPE_CHAT));
}
/**
* Lazy load
*
* @return
*/
public AgentUserRepository getAgentUserRes() {
private AgentUserRepository getAgentUserRes() {
if (agentUserRes == null)
agentUserRes = MainContext.getContext().getBean(AgentUserRepository.class);
return agentUserRes;
@ -258,11 +270,19 @@ public class ChatbotEventHandler {
/**
* Lazy load
*
* @return
*/
public OnlineUserRepository getOnlineUserRes() {
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

@ -16,18 +16,23 @@
package com.chatopera.cc.app.im.util;
import com.chatopera.cc.app.basic.MainContext;
import com.chatopera.cc.app.cache.CacheHelper;
import com.chatopera.cc.app.im.client.NettyClients;
import com.chatopera.cc.app.im.message.ChatMessage;
import com.chatopera.cc.app.model.AgentUser;
import com.chatopera.cc.app.model.Chatbot;
import com.chatopera.cc.app.model.MessageOutContent;
import com.chatopera.cc.app.cache.CacheHelper;
import com.chatopera.cc.app.persistence.repository.ChatMessageRepository;
import com.chatopera.cc.app.persistence.repository.ChatbotRepository;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.HashSet;
public class ChatbotUtils {
private final static Logger logger = LoggerFactory.getLogger(ChatbotUtils.class);
public static final HashSet<String> VALID_LANGS = new HashSet<String>(Arrays.asList(new String[]{"zh_CN", "en_US"}));
public static final String CHATBOT_FIRST = "机器人客服优先";
public static final String HUMAN_FIRST = "人工客服优先";
@ -35,6 +40,9 @@ public class ChatbotUtils {
public static final String SNS_TYPE_WEBIM = "app";
private static ChatbotRepository chatbotRes;
private static ChatMessageRepository chatMessageRes;
/**
* 使用snsid得到ChatbotID
@ -56,6 +64,48 @@ public class ChatbotUtils {
return StringUtils.remove(chatbotID, clientId.toLowerCase() + "_");
}
/**
* @param data
* @param direction
* @param chatype
* @param msgtype
* @return
*/
private static MessageOutContent createMessage(ChatMessage data, String direction, String chatype, String msgtype) {
AgentUser agentUser = (AgentUser) CacheHelper.getAgentUserCacheBean().getCacheObject(data.getUserid(), data.getOrgi());
if (agentUser == null)
return null;
// 设置发送消息体
MessageOutContent outMessage = new MessageOutContent();
outMessage.setMessage(data.getMessage());
outMessage.setMessageType(msgtype);
outMessage.setCalltype(direction);
outMessage.setAgentUser(null);
outMessage.setSnsAccount(null);
if (StringUtils.isNotBlank(data.getSuggestmsg())) {
outMessage.setSuggest(data.getSuggest());
}
outMessage.setContextid(data.getUserid());
outMessage.setFromUser(data.getUserid());
outMessage.setToUser(data.getTouser());
outMessage.setChannelMessage(data);
outMessage.setNickName(data.getUsername());
outMessage.setCreatetime(data.getCreatetime());
/**
* 保存消息
*/
getChatMessageRes().save(data);
//将消息发送给 访客
NettyClients.getInstance().sendChatbotEventMessage(data.getUserid(), MainContext.MessageTypeEnum.MESSAGE.toString(), data);
return outMessage;
}
/**
* 发送聊天机器人消息
*
@ -68,81 +118,53 @@ public class ChatbotUtils {
* @param userid
* @return
*/
protected static MessageOutContent createMessage(ChatMessage data, String appid, String channel, String direction, String chatype, String msgtype, String userid) {
MessageOutContent outMessage = new MessageOutContent();
protected static MessageOutContent 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) {
Chatbot c = getChatbotRes().findBySnsAccountIdentifierAndOrgi(appid, orgi);
if (c == null) // ignore event if chatbot not exist.
return null;
outMessage.setMessage(data.getMessage());
outMessage.setMessageType(msgtype);
outMessage.setCalltype(direction);
outMessage.setAgentUser(null);
outMessage.setSnsAccount(null);
{
data.setUserid(userid);
data.setUsername(data.getUsername());
data.setTouser(userid);
data.setAgentuser(userid);
data.setAgentserviceid(data.getContextid());
data.setChatype(chatype);
data.setChannel(channel);
data.setAppid(data.getAppid());
data.setOrgi(data.getOrgi());
data.setMsgtype(msgtype);
data.setUsername(data.getUsername());
data.setUsession(data.getUserid()); //agentUser作为 session id
data.setContextid(data.getContextid());
data.setCalltype(direction);
outMessage.setContextid(data.getContextid());
outMessage.setFromUser(data.getUserid());
outMessage.setToUser(data.getTouser());
outMessage.setChannelMessage(data);
outMessage.setNickName(data.getUsername());
outMessage.setCreatetime(data.getCreatetime());
if (!StringUtils.isBlank(data.getSuggestmsg())) {
outMessage.setSuggest(data.getSuggest());
}
data.setUpdatetime(System.currentTimeMillis());
/**
* 保存消息
*/
if (MainContext.MessageTypeEnum.MESSAGE.toString().equals(data.getType())) {
MainContext.getContext().getBean(ChatMessageRepository.class).save(data);
}
AgentUser agentUser = (AgentUser) CacheHelper.getAgentUserCacheBean().getCacheObject(userid, MainContext.SYSTEM_ORGI);
if (agentUser != null && !StringUtils.isBlank(agentUser.getAgentno())) {
//将消息发送给 坐席
if (MainContext.CallTypeEnum.OUT.toString().equals(direction)) {
data.setUserid(agentUser.getAgentno());
}
NettyClients.getInstance().sendAgentEventMessage(agentUser.getAgentno(), MainContext.MessageTypeEnum.MESSAGE.toString(), data);
}
}
return outMessage;
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, chatype, msgtype);
}
/**
* 发送文字消息
*
* @param data
* @param appid
* @param channel
* @param direction
* @param chatype
* @param userid
* @return
*/
public static MessageOutContent createTextMessage(ChatMessage data, String appid, String channel, String direction, String chatype, String userid) {
return createMessage(data, appid, channel, direction, chatype, MainContext.MediaTypeEnum.TEXT.toString(), userid);
public static MessageOutContent createTextMessage(ChatMessage data, String direction, String chatype) {
return createMessage(data, direction, chatype, MainContext.MediaTypeEnum.TEXT.toString());
}
private static ChatbotRepository getChatbotRes() {
if (chatbotRes == null)
chatbotRes = MainContext.getContext().getBean(ChatbotRepository.class);
return chatbotRes;
}
private static ChatMessageRepository getChatMessageRes() {
if (chatMessageRes == null)
chatMessageRes = MainContext.getContext().getBean(ChatMessageRepository.class);
return chatMessageRes;
}
}

View File

@ -147,7 +147,7 @@ public class RichMediaUtils {
if (StringUtils.isNotBlank(userid)) {
AgentUser agentUser = (AgentUser) CacheHelper.getAgentUserCacheBean().getCacheObject(userid, MainContext.SYSTEM_ORGI);
if ((agentUser != null) && StringUtils.equals(agentUser.getOpttype(), MainContext.OptTypeEnum.CHATBOT.toString())) {
ChatbotUtils.createMessage(data, appid, channel, MainContext.CallTypeEnum.IN.toString(), MainContext.ChatbotItemType.USERINPUT.toString(), msgtype, data.getUserid());
ChatbotUtils.createMessage(data, appid, channel, MainContext.CallTypeEnum.IN.toString(), MainContext.ChatbotItemType.USERINPUT.toString(), msgtype, data.getUserid(), orgi);
} else {
HumanUtils.createMessage(data, msgtype, userid);
}

View File

@ -17,6 +17,8 @@ public abstract interface ChatbotRepository extends JpaRepository<Chatbot, Strin
public abstract List<Chatbot> findByIdAndOrgi(String id, String orgi);
public abstract Chatbot findBySnsAccountIdentifierAndOrgi(String snsid, String orgi);
@Query(value = "select c from Chatbot c where " +
"(:myorgans is null or c.organ IN :myorgans)")
public Page<Chatbot> findByOrgans(@Param("myorgans") List<String> myorgans, Pageable pageRequest);

View File

@ -16,17 +16,18 @@
package com.chatopera.cc.concurrent.chatbot;
import com.chatopera.cc.app.persistence.hibernate.BaseService;
import com.chatopera.cc.app.persistence.repository.ChatbotRepository;
import com.chatopera.cc.exchange.UserEvent;
public class ChatbotEvent<S> implements UserEvent {
private S data;
private BaseService<?> crudRes;
private ChatbotRepository chatbotRes;
private String eventype;
public ChatbotEvent(S data, BaseService<?> crudRes, String eventype) {
public ChatbotEvent(S data, ChatbotRepository chatbotRes, String eventype) {
this.data = data;
this.crudRes = crudRes;
this.chatbotRes = chatbotRes;
this.eventype = eventype;
}
@ -38,12 +39,12 @@ public class ChatbotEvent<S> implements UserEvent {
this.data = data;
}
public BaseService<?> getCrudRes() {
return crudRes;
public ChatbotRepository getChatbotRes() {
return chatbotRes;
}
public void setCrudRes(BaseService<?> crudRes) {
this.crudRes = crudRes;
public void setChatbotRes(ChatbotRepository chatbotRes) {
this.chatbotRes = chatbotRes;
}
public String getEventype() {

View File

@ -15,22 +15,69 @@
*/
package com.chatopera.cc.concurrent.chatbot;
import com.chatopera.cc.app.basic.MainContext;
import com.chatopera.cc.app.handler.api.request.RestUtils;
import com.chatopera.cc.app.im.client.NettyClients;
import com.chatopera.cc.app.im.message.ChatMessage;
import com.chatopera.cc.app.model.Chatbot;
import com.chatopera.cc.concurrent.user.UserDataEvent;
import com.chatopera.cc.util.Constants;
import com.chatopera.chatbot.ChatbotAPIRuntimeException;
import com.lmax.disruptor.EventHandler;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.MalformedURLException;
@SuppressWarnings("rawtypes")
public class ChatbotEventHandler implements EventHandler<UserDataEvent> {
private final static Logger logger = LoggerFactory.getLogger(ChatbotEventHandler.class);
private void chat(final ChatbotEvent payload) throws MalformedURLException, ChatbotAPIRuntimeException {
ChatMessage request = (ChatMessage) payload.getData();
Chatbot c = payload.getChatbotRes()
.findOne(request.getAiid());
logger.info("[chatbot disruptor] chat request baseUrl {}, chatbotID {}, fromUserId {}, textMessage {}", c.getBaseUrl(), c.getChatbotID(), request.getUserid(), request.getMessage());
// Get response from Conversational Engine.
JSONObject result = c.getApi()
.conversation(c.getChatbotID(), request.getUserid(), request.getMessage(), false);
// parse response
logger.info("[chatbot disruptor] chat response {}", result.toString());
if(result.getInt(RestUtils.RESP_KEY_RC) == 0){
// reply
ChatMessage resp = new ChatMessage();
resp.setCalltype(MainContext.CallTypeEnum.OUT.toString());
resp.setOrgi(request.getOrgi());
resp.setAiid(request.getAiid());
resp.setMessage(result.getJSONObject("data").getString("string"));
resp.setTouser(request.getUsername());
resp.setMsgtype(request.getMsgtype());
resp.setUserid(request.getUserid());
resp.setChannel(request.getChannel());
resp.setContextid(resp.getContextid());
resp.setSessionid(resp.getSessionid());
resp.setUsername(c.getName());
NettyClients.getInstance().sendChatbotEventMessage(request.getUserid(), MainContext.MessageTypeEnum.MESSAGE.toString(), resp);
} else {
// TODO handle exceptions
}
}
@Override
public void onEvent(UserDataEvent event, long arg1, boolean arg2)
throws Exception {
if (event.getEvent() != null) {
ChatbotEvent x = (ChatbotEvent) event.getEvent();
// TODO #75
logger.info("onEvent");
ChatbotEvent payload = (ChatbotEvent) event.getEvent();
switch (payload.getEventype()) {
case Constants
.CHATBOT_EVENT_TYPE_CHAT:
chat(payload);
break;
default:
logger.warn("[chatbot disruptor] onEvent unknown.");
}
}

View File

@ -27,6 +27,9 @@ public class Constants {
public final static String MINIO_BUCKET = "chatopera";
public final static String IM_MESSAGE_TYPE_MESSAGE = "message";
public final static String CHATBOT_EVENT_TYPE_CHAT = "chat";
/**
* Formatter
*/

View File

@ -62,16 +62,17 @@
if(this.count("text") == 0){
strValue= "" ;
}
if(words != this.count("text")){
socket.emit('message', {
appid : "${appid!''}",
userid:"${userid!''}",
type:"writing",
session:"${sessionid!''}",
orgi:"${orgi!''}",
message : strValue
});
}
// ignore writting in Chatbot Service
// if(words != this.count("text")){
// socket.emit('message', {
// appid : "${appid!''}",
// userid:"${userid!''}",
// type:"writing",
// session:"${sessionid!''}",
// orgi:"${orgi!''}",
// message : strValue
// });
// }
words = this.count("text") ;
document.getElementById('surplus').innerHTML = count+"/"+limitNum+" , " + pattern; //输入显示
////////
@ -488,6 +489,7 @@
}
})
socket.on('message', function(data) {
console.log("[chatbot io] message ", data);
var chat=document.getElementsByClassName('chatting-left').innerText;
chat = data.message;
if(data.messageType == "image"){
@ -496,9 +498,9 @@
chat = "<div class='ukefu-message-file'><div class='ukefu-file-icon'><img src='/im/img/file.png'></div><div class='ukefu-file-desc'><a href='"+data.message+"' target='_blank'><div>"+data.filename+"</div><div>"+(data.filesize/1024).toFixed(3)+"Kb</div></a></div></div>" ;
}
if(data.calltype == "呼入"){
output('<div class="chat-right"> <img class="user-img" src="/im/img/user.png" alt=""><div class="chat-message"><label class="time">'+data.createtime+'</label><label class="user">'+data.nickName+'</label> </div><div class="chatting-right"><i class="arrow arrow${inviteData.consult_dialog_color!''}"></i><div class="chat-content theme${inviteData.consult_dialog_color!''}">'+chat+'</div></div>' , "chat-block");
output('<div class="chat-right"> <img class="user-img" src="/im/img/user.png" alt=""><div class="chat-message"><label class="time">'+data.createtime+'</label> </div><div class="chatting-right"><i class="arrow arrow${inviteData.consult_dialog_color!''}"></i><div class="chat-content theme${inviteData.consult_dialog_color!''}">'+chat+'</div></div>' , "chat-block");
}else if(data.calltype == "呼出"){
output('<div class="chat-left"> <img class="user-img" src="<#if inviteData?? && inviteData.consult_dialog_headimg??>/res/image.html?id=${inviteData.consult_dialog_headimg?url}<#else>/images/agent.png</#if>" alt=""><div class="chat-message"><label class="user">'+data.nickName+'</label><label class="time">'+data.createtime+'</label> </div><div class="chatting-left"><i class="arrow"></i><div class="chat-content">'+chat+'</div></div>' , "chat-block");
output('<div class="chat-left"> <img class="user-img" src="<#if inviteData?? && inviteData.consult_dialog_headimg??>/res/image.html?id=${inviteData.consult_dialog_headimg?url}<#else>/images/agent.png</#if>" alt=""><div class="chat-message"><label class="user">'+data.username+'</label><label class="time">'+data.createtime+'</label> </div><div class="chatting-left"><i class="arrow"></i><div class="chat-content">'+chat+'</div></div>' , "chat-block");
R3Ajax.audioplayer('audioplane', newmessage, false); // 播放
}
});