feature:
1.修改:整合工作模式和闲聊模式。
2.新增:添加交互log。
3.新增:语义完整度相关接口。
4.修改:拆分意图识别服务。
tags/B.2.8.1_20240312_base
| @@ -0,0 +1,21 @@ | |||||
| package com.xueyi.nlt.api.nlt.feign; | |||||
| import com.alibaba.fastjson2.JSONObject; | |||||
| import com.xueyi.nlt.api.nlt.feign.factory.RemoteBaiduNLPFallbackFactory; | |||||
| import org.springframework.cloud.openfeign.FeignClient; | |||||
| import org.springframework.web.bind.annotation.PostMapping; | |||||
| import org.springframework.web.bind.annotation.RequestBody; | |||||
| import org.springframework.web.bind.annotation.RequestHeader; | |||||
| import org.springframework.web.bind.annotation.RequestParam; | |||||
| @FeignClient(url = "${notification.baidunlp.url}",name = "baidu-nlp", fallbackFactory = RemoteBaiduNLPFallbackFactory.class) | |||||
| public interface RemoteBaiduNLPService { | |||||
| @PostMapping(path = "/oauth/2.0/token", consumes = "application/x-www-form-urlencoded") | |||||
| JSONObject getAccessToken(@RequestBody String body); | |||||
| @PostMapping(path = "/rpc/2.0/nlp/v2/dnnlm_cn", consumes = "application/json") | |||||
| JSONObject detected(@RequestParam(value = "charset", defaultValue = "UTF-8") String charset, | |||||
| @RequestParam("access_token") String accessToken, | |||||
| @RequestBody String body); | |||||
| } | |||||
| @@ -0,0 +1,15 @@ | |||||
| package com.xueyi.nlt.api.nlt.feign.factory; | |||||
| import com.xueyi.nlt.api.nlt.feign.RemoteBaiduNLPService; | |||||
| import lombok.extern.slf4j.Slf4j; | |||||
| import org.springframework.cloud.openfeign.FallbackFactory; | |||||
| import org.springframework.stereotype.Component; | |||||
| @Slf4j | |||||
| @Component | |||||
| public class RemoteBaiduNLPFallbackFactory implements FallbackFactory<RemoteBaiduNLPService> { | |||||
| @Override | |||||
| public RemoteBaiduNLPService create(Throwable cause) { | |||||
| return null; | |||||
| } | |||||
| } | |||||
| @@ -2,9 +2,27 @@ package com.xueyi.nlt.netty.server.handler; | |||||
| import com.alibaba.fastjson2.JSONObject; | import com.alibaba.fastjson2.JSONObject; | ||||
| import com.baomidou.mybatisplus.core.toolkit.StringUtils; | import com.baomidou.mybatisplus.core.toolkit.StringUtils; | ||||
| import com.xueyi.common.core.utils.core.ObjectUtil; | |||||
| import com.xueyi.common.core.utils.core.SpringUtils; | |||||
| import com.xueyi.common.core.web.result.AjaxResult; | |||||
| import com.xueyi.common.core.web.result.R; | |||||
| import com.xueyi.nlt.api.nlt.domain.vo.DmIntentVo; | |||||
| import com.xueyi.nlt.api.nlt.domain.vo.response.DmIntentResponse; | |||||
| import com.xueyi.nlt.netty.server.config.ServerConfig; | import com.xueyi.nlt.netty.server.config.ServerConfig; | ||||
| import com.xueyi.nlt.nlt.service.IDmIntentService; | |||||
| import com.xueyi.nlt.nlt.service.IDmRegularService; | |||||
| import com.xueyi.nlt.nlt.service.impl.LogServiceImpl; | |||||
| import com.xueyi.nlt.nlt.template.FreeChatTemplate; | import com.xueyi.nlt.nlt.template.FreeChatTemplate; | ||||
| import com.xueyi.nlt.nlt.template.MovieChatTemplate; | import com.xueyi.nlt.nlt.template.MovieChatTemplate; | ||||
| import com.xueyi.system.api.digitalmans.domain.dto.DmManDeviceDto; | |||||
| import com.xueyi.system.api.digitalmans.feign.RemoteManDeviceService; | |||||
| import com.xueyi.system.api.organize.domain.dto.SysEnterpriseDto; | |||||
| import com.xueyi.system.api.organize.feign.RemoteEnterpriseService; | |||||
| import com.yomahub.tlog.context.TLogContext; | |||||
| import com.yomahub.tlog.core.annotation.TLogAspect; | |||||
| import com.yomahub.tlog.core.rpc.TLogLabelBean; | |||||
| import com.yomahub.tlog.core.rpc.TLogRPCHandler; | |||||
| import com.yomahub.tlog.spring.TLogSpringAware; | |||||
| import io.netty.channel.Channel; | import io.netty.channel.Channel; | ||||
| import io.netty.channel.ChannelHandlerContext; | import io.netty.channel.ChannelHandlerContext; | ||||
| import io.netty.channel.SimpleChannelInboundHandler; | import io.netty.channel.SimpleChannelInboundHandler; | ||||
| @@ -24,23 +42,46 @@ import java.time.LocalDateTime; | |||||
| public class ChatServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> { | public class ChatServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> { | ||||
| // 添加log | // 添加log | ||||
| private static final Logger log = LoggerFactory.getLogger(ChatServerHandler.class); | private static final Logger log = LoggerFactory.getLogger(ChatServerHandler.class); | ||||
| private static final Logger intentLog = LoggerFactory.getLogger("intentLog"); | |||||
| public static ChatServerHandler INSTANCE; | public static ChatServerHandler INSTANCE; | ||||
| @Autowired | |||||
| private RemoteManDeviceService manDeviceService; | |||||
| @Autowired | @Autowired | ||||
| private FreeChatTemplate freeChatTemplate; | private FreeChatTemplate freeChatTemplate; | ||||
| @Autowired | |||||
| private RemoteEnterpriseService remoteEnterpriseService; | |||||
| @Autowired | |||||
| private IDmIntentService intentService; | |||||
| @Autowired | @Autowired | ||||
| private MovieChatTemplate movieChatTemplate; | private MovieChatTemplate movieChatTemplate; | ||||
| @Autowired | |||||
| private LogServiceImpl logService; | |||||
| private final TLogRPCHandler tLogRPCHandler = new TLogRPCHandler(); | |||||
| @PostConstruct | @PostConstruct | ||||
| public void init() { | public void init() { | ||||
| INSTANCE = this; | INSTANCE = this; | ||||
| INSTANCE.freeChatTemplate = this.freeChatTemplate; | INSTANCE.freeChatTemplate = this.freeChatTemplate; | ||||
| INSTANCE.movieChatTemplate = this.movieChatTemplate; | INSTANCE.movieChatTemplate = this.movieChatTemplate; | ||||
| INSTANCE.intentService = this.intentService; | |||||
| INSTANCE.manDeviceService = this.manDeviceService; | |||||
| INSTANCE.remoteEnterpriseService = this.remoteEnterpriseService; | |||||
| INSTANCE.logService = this.logService; | |||||
| } | } | ||||
| @Override | @Override | ||||
| protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) throws Exception { | protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) throws Exception { | ||||
| tLogRPCHandler.processProviderSide(new TLogLabelBean()); | |||||
| Channel channel = channelHandlerContext.channel(); | Channel channel = channelHandlerContext.channel(); | ||||
| // 判断textWebSocketFrame.text()是否为空,如果为空,则直接返回 | // 判断textWebSocketFrame.text()是否为空,如果为空,则直接返回 | ||||
| if (StringUtils.isEmpty(textWebSocketFrame.text()) || StringUtils.isEmpty(textWebSocketFrame.text().trim()) ) { | if (StringUtils.isEmpty(textWebSocketFrame.text()) || StringUtils.isEmpty(textWebSocketFrame.text().trim()) ) { | ||||
| @@ -57,8 +98,27 @@ public class ChatServerHandler extends SimpleChannelInboundHandler<TextWebSocket | |||||
| } | } | ||||
| // 获取到发送人的设备号 | // 获取到发送人的设备号 | ||||
| String devId = jsonObject.getString("devId"); | String devId = jsonObject.getString("devId"); | ||||
| Long operatorId = jsonObject.getLong("operatorId"); | |||||
| // 获取到发送人的用户id | // 获取到发送人的用户id | ||||
| String msg = jsonObject.getString("msg"); | String msg = jsonObject.getString("msg"); | ||||
| // 判断当前数字人所属租户 | |||||
| R<DmManDeviceDto> manDeviceDtoR = INSTANCE.manDeviceService.manDeviceInfoInner(devId); | |||||
| if (!manDeviceDtoR.isOk() || manDeviceDtoR.getData() == null) { | |||||
| JSONObject jo = new JSONObject(); | |||||
| jo.put("action",""); | |||||
| jo.put("motion","idle"); | |||||
| jo.put("traceId",""); | |||||
| jo.put("status",2); | |||||
| jo.put("code",1); | |||||
| jo.put("tts","设备号未激活或已过期。"); | |||||
| channel.writeAndFlush(new TextWebSocketFrame(jo.toJSONString())); | |||||
| } | |||||
| String enterpriseName = ""; | |||||
| // 获取当前数字人租户信息 | |||||
| R<SysEnterpriseDto> enterpriseDtoR = INSTANCE.remoteEnterpriseService.getInfo(manDeviceDtoR.getData().getTId()); | |||||
| if (enterpriseDtoR.isOk()) { | |||||
| enterpriseName = enterpriseDtoR.getData().getName(); | |||||
| } | |||||
| synchronized (ServerConfig.class) { | synchronized (ServerConfig.class) { | ||||
| @@ -70,7 +130,36 @@ public class ChatServerHandler extends SimpleChannelInboundHandler<TextWebSocket | |||||
| update(devId, channel); | update(devId, channel); | ||||
| } | } | ||||
| try { | try { | ||||
| sendMsg(devId, msg); | |||||
| DmIntentVo dmIntentVo = new DmIntentVo(); | |||||
| dmIntentVo.setContent(msg); | |||||
| dmIntentVo.setDevId(devId); | |||||
| // 调用正则判断,如果正则匹配到了,则返回正则匹配到的结果 | |||||
| DmIntentResponse dmIntentResponse = INSTANCE.intentService.regularAnswer(dmIntentVo); | |||||
| if (ObjectUtil.isNotNull(dmIntentResponse) && StringUtils.isNotEmpty(dmIntentResponse.getSkillCode())) { | |||||
| JSONObject jo = formatResult(dmIntentResponse,2); | |||||
| INSTANCE.reply(channel,msg, jo,enterpriseName,"正则"); | |||||
| return; | |||||
| } | |||||
| // 调用知识库,如果知识库匹配到了,则返回知识库匹配到的结果 | |||||
| dmIntentResponse = INSTANCE.intentService.knowledgeAnswer(dmIntentVo); | |||||
| if (ObjectUtil.isNotNull(dmIntentResponse) && StringUtils.isNotEmpty(dmIntentResponse.getSkillCode()) && ObjectUtil.isNotNull(dmIntentResponse.getH5()) && dmIntentResponse.getH5().getInteger("accurate") == 1) { | |||||
| JSONObject jo = formatResult(dmIntentResponse,2); | |||||
| INSTANCE.reply(channel,msg, jo,enterpriseName,"知识库"); | |||||
| return; | |||||
| } | |||||
| else { | |||||
| JSONObject jo = new JSONObject(); | |||||
| jo.put("action","chat"); | |||||
| jo.put("motion","idle"); | |||||
| jo.put("traceId",""); | |||||
| jo.put("status",0); | |||||
| jo.put("tts","请稍等一下,我要查询一下功能。"); | |||||
| channel.writeAndFlush(new TextWebSocketFrame(jo.toJSONString())); | |||||
| INSTANCE.logService.record(jo,msg,enterpriseName,"大模型"); | |||||
| sendMsg(devId, msg); | |||||
| } | |||||
| } catch (Exception e) { | } catch (Exception e) { | ||||
| JSONObject jo = new JSONObject(); | JSONObject jo = new JSONObject(); | ||||
| jo.put("action","chat"); | jo.put("action","chat"); | ||||
| @@ -129,7 +218,9 @@ public class ChatServerHandler extends SimpleChannelInboundHandler<TextWebSocket | |||||
| * @param msg | * @param msg | ||||
| * @param userId | * @param userId | ||||
| */ | */ | ||||
| @TLogAspect(str = "DGMAN-NLT") | |||||
| private void sendMsg(String userId, Object msg ) { | private void sendMsg(String userId, Object msg ) { | ||||
| String appName = TLogSpringAware.getProperty("spring.application.name"); | |||||
| log.info("设备:{},收到消息:{}", userId,msg); | log.info("设备:{},收到消息:{}", userId,msg); | ||||
| Channel channel1 = ServerConfig.sessionMap.get(userId); | Channel channel1 = ServerConfig.sessionMap.get(userId); | ||||
| if (channel1 != null) { | if (channel1 != null) { | ||||
| @@ -138,6 +229,22 @@ public class ChatServerHandler extends SimpleChannelInboundHandler<TextWebSocket | |||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * 向客户端发送消息,同时记录日志 | |||||
| * | |||||
| * @param jo | |||||
| * @param enterpriseName | |||||
| * @param type | |||||
| * | |||||
| */ | |||||
| @TLogAspect(str = "testtest") | |||||
| // @TLogAspect({"enterpriseName","type"}) | |||||
| private void reply(Channel channel, String msg, JSONObject jo,String enterpriseName,String type ) { | |||||
| INSTANCE.logService.record(jo,msg,enterpriseName,type); | |||||
| channel.writeAndFlush(new TextWebSocketFrame(jo.toJSONString())); | |||||
| } | |||||
| /** | /** | ||||
| * 一旦客户端连接上来,该方法被执行 | * 一旦客户端连接上来,该方法被执行 | ||||
| * | * | ||||
| @@ -201,4 +308,16 @@ public class ChatServerHandler extends SimpleChannelInboundHandler<TextWebSocket | |||||
| ctx.close(); | ctx.close(); | ||||
| } | } | ||||
| private JSONObject formatResult(DmIntentResponse dmIntentResponse,Integer status) { | |||||
| JSONObject resultJson = new JSONObject(); | |||||
| resultJson.put("action",dmIntentResponse.getAction()); | |||||
| resultJson.put("motion",dmIntentResponse.getMotion()); | |||||
| resultJson.put("traceId",""); | |||||
| resultJson.put("skillCode",dmIntentResponse.getSkillCode()); | |||||
| resultJson.put("status",status); | |||||
| resultJson.put("tts",""); | |||||
| resultJson.put("h5",dmIntentResponse.getH5()); | |||||
| return resultJson; | |||||
| } | |||||
| } | } | ||||
| @@ -0,0 +1,15 @@ | |||||
| package com.xueyi.nlt.nlt.config; | |||||
| import lombok.Data; | |||||
| import org.springframework.boot.context.properties.ConfigurationProperties; | |||||
| import org.springframework.context.annotation.Configuration; | |||||
| @Data | |||||
| @Configuration | |||||
| @ConfigurationProperties(prefix = "secret.baidu") | |||||
| public class BaiduConfig { | |||||
| private String apiKey; | |||||
| private String secretKey; | |||||
| private String authUrl; | |||||
| private String dnnUrl; | |||||
| } | |||||
| @@ -0,0 +1,12 @@ | |||||
| package com.xueyi.nlt.nlt.domain.model; | |||||
| import com.xueyi.common.core.web.entity.model.BaseConverter; | |||||
| import com.xueyi.nlt.nlt.domain.dto.DmRegularDto; | |||||
| import com.xueyi.nlt.nlt.domain.po.DmRegularPo; | |||||
| import com.xueyi.nlt.nlt.domain.query.DmRegularQuery; | |||||
| import org.mapstruct.Mapper; | |||||
| import org.mapstruct.MappingConstants; | |||||
| @Mapper(componentModel = MappingConstants.ComponentModel.SPRING) | |||||
| public interface DmRegularConverter extends BaseConverter<DmRegularQuery, DmRegularDto, DmRegularPo> { | |||||
| } | |||||
| @@ -0,0 +1,9 @@ | |||||
| package com.xueyi.nlt.nlt.domain.vo; | |||||
| import lombok.Data; | |||||
| @Data | |||||
| public class WordProcessVo{ | |||||
| private String metadata; | |||||
| private String processedResult; | |||||
| } | |||||
| @@ -0,0 +1,10 @@ | |||||
| package com.xueyi.nlt.nlt.manager; | |||||
| import com.xueyi.common.web.entity.manager.IBaseManager; | |||||
| import com.xueyi.nlt.api.nlt.domain.vo.response.DmIntentResponse; | |||||
| import com.xueyi.nlt.nlt.domain.dto.DmRegularDto; | |||||
| import com.xueyi.nlt.nlt.domain.query.DmRegularQuery; | |||||
| public interface IDmRegularManager extends IBaseManager<DmRegularQuery, DmRegularDto> { | |||||
| public DmIntentResponse doRegularMatch(String content); | |||||
| } | |||||
| @@ -0,0 +1,41 @@ | |||||
| package com.xueyi.nlt.nlt.manager.impl; | |||||
| import com.xueyi.common.web.entity.manager.impl.BaseManagerImpl; | |||||
| import com.xueyi.nlt.api.nlt.domain.vo.response.DmIntentResponse; | |||||
| import com.xueyi.nlt.nlt.domain.model.DmRegularConverter; | |||||
| import com.xueyi.nlt.nlt.domain.po.DmRegularPo; | |||||
| import com.xueyi.nlt.nlt.domain.dto.DmRegularDto; | |||||
| import com.xueyi.nlt.nlt.domain.query.DmRegularQuery; | |||||
| import com.xueyi.nlt.nlt.manager.IDmRegularManager; | |||||
| import com.xueyi.nlt.nlt.mapper.DmRegularMapper; | |||||
| import org.springframework.stereotype.Component; | |||||
| import java.util.List; | |||||
| import java.util.regex.Matcher; | |||||
| import java.util.regex.Pattern; | |||||
| @Component | |||||
| public class DmRegularManager extends BaseManagerImpl<DmRegularQuery, DmRegularDto, DmRegularPo, DmRegularMapper, DmRegularConverter> implements IDmRegularManager { | |||||
| @Override | |||||
| public DmIntentResponse doRegularMatch(String content) { | |||||
| DmIntentResponse response = new DmIntentResponse(); | |||||
| List<DmRegularPo> regularPos =baseMapper.selectList(null); | |||||
| for (DmRegularPo regularPo : regularPos) { | |||||
| if (isMatchRegular(content, regularPo.getExpression())) { | |||||
| response.setMsg(regularPo.getText()); | |||||
| response.setSkillCode(regularPo.getSkillCode()); | |||||
| response.setAction(regularPo.getAction()); | |||||
| response.setH5(regularPo.getJson()); | |||||
| break; | |||||
| } | |||||
| } | |||||
| return response; | |||||
| } | |||||
| private boolean isMatchRegular(String str,String regular) { | |||||
| Pattern pattern = Pattern.compile(regular); | |||||
| Matcher matcher = pattern.matcher(str); | |||||
| return matcher.find(); | |||||
| } | |||||
| } | |||||
| @@ -1,8 +1,15 @@ | |||||
| package com.xueyi.nlt.nlt.service; | package com.xueyi.nlt.nlt.service; | ||||
| import com.xueyi.common.web.entity.service.IBaseService; | import com.xueyi.common.web.entity.service.IBaseService; | ||||
| import com.xueyi.nlt.api.nlt.domain.vo.DmIntentVo; | |||||
| import com.xueyi.nlt.api.nlt.domain.vo.response.DmIntentResponse; | |||||
| import com.xueyi.nlt.nlt.domain.dto.DmIntentDto; | import com.xueyi.nlt.nlt.domain.dto.DmIntentDto; | ||||
| import com.xueyi.nlt.nlt.domain.query.DmIntentQuery; | import com.xueyi.nlt.nlt.domain.query.DmIntentQuery; | ||||
| public interface IDmIntentService extends IBaseService<DmIntentQuery, DmIntentDto> { | public interface IDmIntentService extends IBaseService<DmIntentQuery, DmIntentDto> { | ||||
| DmIntentResponse regularAnswer(DmIntentVo intent); | |||||
| DmIntentResponse knowledgeAnswer(DmIntentVo intent); | |||||
| DmIntentResponse conversation(DmIntentVo intent); | |||||
| } | } | ||||
| @@ -0,0 +1,10 @@ | |||||
| package com.xueyi.nlt.nlt.service; | |||||
| import com.xueyi.common.web.entity.service.IBaseService; | |||||
| import com.xueyi.nlt.api.nlt.domain.vo.response.DmIntentResponse; | |||||
| import com.xueyi.nlt.nlt.domain.dto.DmRegularDto; | |||||
| import com.xueyi.nlt.nlt.domain.query.DmRegularQuery; | |||||
| public interface IDmRegularService extends IBaseService<DmRegularQuery, DmRegularDto> { | |||||
| DmIntentResponse doRegularMatch(String content); | |||||
| } | |||||
| @@ -0,0 +1,7 @@ | |||||
| package com.xueyi.nlt.nlt.service; | |||||
| import com.xueyi.nlt.nlt.domain.vo.WordProcessVo; | |||||
| public interface IDmWordProcessService { | |||||
| public WordProcessVo semanticIntegrityDetection (WordProcessVo dmWordProcessVo); | |||||
| } | |||||
| @@ -1,12 +1,312 @@ | |||||
| package com.xueyi.nlt.nlt.service.impl; | package com.xueyi.nlt.nlt.service.impl; | ||||
| import com.alibaba.fastjson2.JSONObject; | |||||
| import com.baomidou.mybatisplus.core.toolkit.StringUtils; | |||||
| import com.xueyi.common.cache.utils.SourceUtil; | |||||
| import com.xueyi.common.core.constant.basic.SecurityConstants; | |||||
| import com.xueyi.common.core.constant.digitalman.MessageConstants; | |||||
| import com.xueyi.common.core.constant.digitalman.SkillConstants; | |||||
| import com.xueyi.common.core.utils.core.IdUtil; | |||||
| import com.xueyi.common.core.web.result.AjaxResult; | |||||
| import com.xueyi.common.core.web.result.R; | |||||
| import com.xueyi.common.web.entity.service.impl.BaseServiceImpl; | import com.xueyi.common.web.entity.service.impl.BaseServiceImpl; | ||||
| import com.xueyi.nlt.api.nlt.domain.vo.CoversationSessionVo; | |||||
| import com.xueyi.nlt.api.nlt.domain.vo.DmIntentVo; | |||||
| import com.xueyi.nlt.api.nlt.domain.vo.KnowledgeVo; | |||||
| import com.xueyi.nlt.api.nlt.domain.vo.response.DmIntentResponse; | |||||
| import com.xueyi.nlt.api.nlt.domain.vo.response.DmKnowledgeResponse; | |||||
| import com.xueyi.nlt.api.nlt.feign.RemoteQAService; | |||||
| import com.xueyi.nlt.nlt.domain.dto.DmIntentDto; | import com.xueyi.nlt.nlt.domain.dto.DmIntentDto; | ||||
| import com.xueyi.nlt.nlt.domain.query.DmIntentQuery; | import com.xueyi.nlt.nlt.domain.query.DmIntentQuery; | ||||
| import com.xueyi.nlt.nlt.domain.vo.MarkRecordVo; | |||||
| import com.xueyi.nlt.nlt.manager.IDmIntentManager; | import com.xueyi.nlt.nlt.manager.IDmIntentManager; | ||||
| import com.xueyi.nlt.nlt.manager.impl.DmRegularManager; | |||||
| import com.xueyi.nlt.nlt.service.IDmIntentService; | import com.xueyi.nlt.nlt.service.IDmIntentService; | ||||
| import com.xueyi.nlt.nlt.template.DeliveryOrderTemplate; | |||||
| import com.xueyi.nlt.nlt.template.FlightMessageTemplate; | |||||
| import com.xueyi.nlt.nlt.template.FreeChatTemplate; | |||||
| import com.xueyi.nlt.nlt.template.MeetingOrderTemplate; | |||||
| import com.xueyi.system.api.digitalmans.domain.dto.DmManDeviceDto; | |||||
| import com.xueyi.system.api.digitalmans.domain.dto.DmSkillDto; | |||||
| import com.xueyi.system.api.digitalmans.feign.RemoteManDeviceService; | |||||
| import com.xueyi.system.api.digitalmans.feign.RemoteSkillService; | |||||
| import com.xueyi.system.api.model.Source; | |||||
| import com.xueyi.system.api.organize.domain.dto.SysEnterpriseDto; | |||||
| import com.xueyi.system.api.organize.feign.RemoteEnterpriseService; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.data.redis.core.RedisTemplate; | |||||
| import org.springframework.data.redis.core.StringRedisTemplate; | |||||
| import org.springframework.stereotype.Service; | import org.springframework.stereotype.Service; | ||||
| import java.time.LocalDateTime; | |||||
| import java.util.ArrayList; | |||||
| import java.util.List; | |||||
| @Service | @Service | ||||
| public class DmIntentServiceImpl extends BaseServiceImpl<DmIntentQuery, DmIntentDto, IDmIntentManager> implements IDmIntentService { | public class DmIntentServiceImpl extends BaseServiceImpl<DmIntentQuery, DmIntentDto, IDmIntentManager> implements IDmIntentService { | ||||
| private static final Logger log = LoggerFactory.getLogger(DmIntentServiceImpl.class); | |||||
| @Autowired | |||||
| RemoteEnterpriseService remoteEnterpriseService; | |||||
| @Autowired | |||||
| private DmRegularManager regularManager; | |||||
| @Autowired | |||||
| private RemoteManDeviceService manDeviceService; | |||||
| @Autowired | |||||
| private RemoteQAService remoteQAService; | |||||
| @Autowired | |||||
| private StringRedisTemplate redisTemplate; | |||||
| @Autowired | |||||
| private RedisTemplate<Object,Object> redisTemplate2; | |||||
| @Autowired | |||||
| private MeetingOrderTemplate meetingOrderTemplate; | |||||
| @Autowired | |||||
| private DeliveryOrderTemplate deliveryOrderTemplate; | |||||
| @Autowired | |||||
| private FreeChatTemplate freeChatTemplate; | |||||
| @Autowired | |||||
| private RemoteSkillService remoteskillService; | |||||
| @Autowired | |||||
| private FlightMessageTemplate flightMessageTemplate; | |||||
| @Override | |||||
| public DmIntentResponse conversation(DmIntentVo intent) { | |||||
| DmIntentResponse response = regularAnswer(intent); | |||||
| if (response != null && (StringUtils.isNotEmpty(response.getSkillCode()) || StringUtils.isNotEmpty(response.getMsg()))) { | |||||
| return response; | |||||
| } | |||||
| else{ | |||||
| response = knowledgeAnswer(intent); | |||||
| } | |||||
| if(StringUtils.isEmpty(response.getSkillCode()) && intent.getMode().equals(MessageConstants.MODE_FREE_CHAT)) { | |||||
| JSONObject joResult = freeChatTemplate.handle(intent.getDevId(),intent.getContent()); | |||||
| response.setH5(joResult); | |||||
| } | |||||
| return response; | |||||
| } | |||||
| @Override | |||||
| public DmIntentResponse regularAnswer(DmIntentVo intent) { | |||||
| MarkRecordVo recordVo = new MarkRecordVo(); | |||||
| recordVo.setDevId(intent.getDevId()); | |||||
| recordVo.setId(IdUtil.getSnowflakeNextId()); | |||||
| recordVo.setCreateTime(LocalDateTime.now()); | |||||
| recordVo.setQuestion(intent.getContent()); | |||||
| recordVo.setHit(1); | |||||
| R<DmManDeviceDto> manDeviceDtoR = manDeviceService.manDeviceInfoInner(intent.getDevId()); | |||||
| Long enterpriseId = manDeviceDtoR.getData().getTId(); | |||||
| String enterpriseName = ""; | |||||
| R<SysEnterpriseDto> enterpriseDtoR = remoteEnterpriseService.getInfo(enterpriseId); | |||||
| Source source = SourceUtil.getSourceCache(enterpriseDtoR.getData().getStrategyId()); | |||||
| if (enterpriseDtoR.isOk()) { | |||||
| enterpriseName = enterpriseDtoR.getData().getName(); | |||||
| } | |||||
| DmIntentResponse response = sessionHandle(intent); | |||||
| //已有session的处理 | |||||
| if(response != null && StringUtils.isNotEmpty(response.getSkillCode())){ | |||||
| return response; | |||||
| } | |||||
| // 正则技能匹配 | |||||
| response = regularManager.doRegularMatch(intent.getContent()); | |||||
| if (response != null && StringUtils.isNotEmpty(response.getSkillCode())){ | |||||
| sendToDashboard(intent, enterpriseName); | |||||
| // 判断是否有权限 | |||||
| R<List<DmSkillDto>> skilllistInner = remoteskillService.skilllistInner(intent.getDevId(),"1",enterpriseId, source.getMaster(), SecurityConstants.INNER); | |||||
| if(skilllistInner.isOk()){ | |||||
| List<DmSkillDto> skilllist=skilllistInner.getData(); | |||||
| for (DmSkillDto dmSkillDto : skilllist) { | |||||
| if (dmSkillDto.getSkillCode().equals(response.getSkillCode())) { | |||||
| if(dmSkillDto.getStatus().equals("0")){ | |||||
| response.clear(); | |||||
| response.setMsg("操作无权限"); | |||||
| return response; | |||||
| } | |||||
| // 为技能设置motion | |||||
| response.setMotion(dmSkillDto.getMotionName()); | |||||
| } | |||||
| } | |||||
| } | |||||
| switch (response.getSkillCode()) { | |||||
| case "1": | |||||
| // 做会议室处理 | |||||
| response.setH5(meetingOrderTemplate.handle(intent.getDevId(), intent.getContent(), enterpriseId)); | |||||
| break; | |||||
| case "33": | |||||
| // 做快递预约处理 | |||||
| response.setH5(deliveryOrderTemplate.handle(intent.getDevId(), intent.getContent(), enterpriseId)); | |||||
| break; | |||||
| case "34": | |||||
| // 做查询航班信息处理 | |||||
| response.setH5(flightMessageTemplate.handle(intent.getDevId(), intent.getContent(), enterpriseId)); | |||||
| break; | |||||
| default: | |||||
| break; | |||||
| } | |||||
| }else if(response != null && StringUtils.isNotEmpty(response.getMsg())){ | |||||
| // 记录log | |||||
| recordVo.setType(2); | |||||
| List answers = new ArrayList(); | |||||
| answers.add(response.getMsg()); | |||||
| recordVo.setAnswers(answers); | |||||
| return response; | |||||
| } | |||||
| return response; | |||||
| } | |||||
| @Override | |||||
| public DmIntentResponse knowledgeAnswer(DmIntentVo intent){ | |||||
| MarkRecordVo recordVo = new MarkRecordVo(); | |||||
| recordVo.setDevId(intent.getDevId()); | |||||
| recordVo.setId(IdUtil.getSnowflakeNextId()); | |||||
| recordVo.setCreateTime(LocalDateTime.now()); | |||||
| recordVo.setQuestion(intent.getContent()); | |||||
| recordVo.setHit(1); | |||||
| R<DmManDeviceDto> manDeviceDtoR = manDeviceService.manDeviceInfoInner(intent.getDevId()); | |||||
| Long enterpriseId = manDeviceDtoR.getData().getTId(); | |||||
| String enterpriseName = ""; | |||||
| R<SysEnterpriseDto> enterpriseDtoR = remoteEnterpriseService.getInfo(Long.valueOf(enterpriseId)); | |||||
| if (enterpriseDtoR.isOk()) { | |||||
| enterpriseName = enterpriseDtoR.getData().getName(); | |||||
| } | |||||
| // 调用知识库问答 | |||||
| DmIntentResponse response = new DmIntentResponse(); | |||||
| response.setSkillCode("30"); | |||||
| DmKnowledgeResponse qaAjax = null; | |||||
| KnowledgeVo knowledgeVo = new KnowledgeVo(); | |||||
| knowledgeVo.setManCode(manDeviceDtoR.getData().getManCode()); | |||||
| knowledgeVo.setTenantId(manDeviceDtoR.getData().getTId()); | |||||
| knowledgeVo.setQuestion(intent.getContent()); | |||||
| R<DmKnowledgeResponse> dmKnowledgeResponseR = remoteQAService.query(knowledgeVo); | |||||
| if (dmKnowledgeResponseR!= null && dmKnowledgeResponseR.getData() != null) { | |||||
| qaAjax = dmKnowledgeResponseR.getData(); | |||||
| } | |||||
| String content = ""; | |||||
| if (qaAjax != null) { | |||||
| log.info("知识库问答返回结果:{}",qaAjax.toString()); | |||||
| if (qaAjax.getTarget() == 1) { | |||||
| content = qaAjax.getResult().get(0).getKnowledgeLib(); | |||||
| pushIntoDashboardRedis(enterpriseName,content,"knowledge"); | |||||
| response.setH5(JSONObject.from(qaAjax)); | |||||
| // 记录log | |||||
| recordVo.setType(2); | |||||
| List answers = new ArrayList(); | |||||
| answers.add(response.getMsg()); | |||||
| recordVo.setAnswers(answers); // 航班查询 | |||||
| return response; | |||||
| } | |||||
| else { | |||||
| // 知识库没有答案,返回空 | |||||
| response.clear(); | |||||
| response.setMsg(""); | |||||
| } | |||||
| } | |||||
| return response; | |||||
| } | |||||
| private DmIntentResponse sessionHandle(DmIntentVo intent) { | |||||
| R<DmManDeviceDto> manDeviceDtoR = manDeviceService.manDeviceInfoInner(intent.getDevId()); | |||||
| Long enterpriseId = manDeviceDtoR.getData().getTId(); | |||||
| DmIntentResponse response = new DmIntentResponse(); | |||||
| // 判断会议室缓存 | |||||
| // 获取设备号对应缓存 | |||||
| //之前已有session信息 | |||||
| CoversationSessionVo sessionObject = (CoversationSessionVo) redisTemplate2.opsForValue().get("group:device" + ":" + intent.getDevId() + ":" +"session"); //category | |||||
| if (sessionObject != null) { | |||||
| switch (sessionObject.getCategory()) { | |||||
| case "meeting": | |||||
| response.setMsg(""); | |||||
| response.setSkillCode("1"); | |||||
| response.setH5(meetingOrderTemplate.handle(intent.getDevId(),intent.getContent(), enterpriseId)); | |||||
| response.setAction("Meeting"); | |||||
| break; | |||||
| case "delivery": | |||||
| response.setMsg(""); | |||||
| response.setSkillCode("33"); | |||||
| response.setH5(deliveryOrderTemplate.handle(intent.getDevId(),intent.getContent(), enterpriseId)); | |||||
| break; | |||||
| case "flight": | |||||
| response.setMsg(""); | |||||
| response.setSkillCode("34"); | |||||
| response.setH5(flightMessageTemplate.handle(intent.getDevId(),intent.getContent(), enterpriseId)); | |||||
| break; | |||||
| } | |||||
| } | |||||
| return response; | |||||
| } | |||||
| private int pushIntoDashboardRedis(String name,String content,String type) { | |||||
| try { | |||||
| JSONObject object = new JSONObject(); | |||||
| object.put("tenantName", name); | |||||
| object.put("content", content); | |||||
| if (type.equals("skill")) { | |||||
| redisTemplate2.opsForList().leftPush("dashboard:skill-consume", object.toJSONString()); | |||||
| } else if (type.equals("knowledge")) { | |||||
| redisTemplate2.opsForList().leftPush("dashboard:knowledge-consume", object.toJSONString()); | |||||
| } | |||||
| return 1; | |||||
| } catch (Exception e) { | |||||
| log.error("推送到仪表盘失败:{}",e.getMessage()); | |||||
| return 0; | |||||
| } | |||||
| } | |||||
| private void sendToDashboard(DmIntentVo intent, String enterpriseName) { | |||||
| if (SkillConstants.SkillType.BOOK_MEETING_ROOM.getCode().equals(intent.getSkillCode()) ) { | |||||
| if (!redisTemplate2.hasKey("group:device" + ":" + intent.getDevId() + ":" +"session")) { | |||||
| // 获取名称为"meeting-order"的BaseTemplate的实例 | |||||
| redisTemplate.opsForValue().increment("dashboard:meeting", 1); | |||||
| pushIntoDashboardRedis(enterpriseName, "会议室预定", "skill"); | |||||
| } | |||||
| } else if (SkillConstants.SkillType.CREATE_VISITOR_INFO.getCode().equals(intent.getSkillCode())) { | |||||
| // 访客预定 | |||||
| redisTemplate.opsForValue().increment("dashboard:create_visitor_info", 1); | |||||
| pushIntoDashboardRedis(enterpriseName,"访客邀约","skill"); | |||||
| } else if (SkillConstants.SkillType.REGISTER_VISITOR.getCode().equals(intent.getSkillCode())) { | |||||
| // 访客登记 | |||||
| redisTemplate.opsForValue().increment("dashboard:register_visitor", 1); | |||||
| pushIntoDashboardRedis(enterpriseName,"陌生人登记","skill"); | |||||
| } else if (SkillConstants.SkillType.BROADCAST_DISPLAY.getCode().equals(intent.getSkillCode())) { | |||||
| // 播报展示 | |||||
| redisTemplate.opsForValue().increment("dashboard:broadcast_display", 1); | |||||
| pushIntoDashboardRedis(enterpriseName,"播报展示","skill"); | |||||
| } else if (SkillConstants.SkillType.OPEN_DOOR.getCode().equals(intent.getSkillCode())) { | |||||
| // 开门记录 | |||||
| redisTemplate.opsForValue().increment("dashboard:open_door", 1); | |||||
| pushIntoDashboardRedis(enterpriseName,"开门","skill"); | |||||
| }else if (SkillConstants.SkillType.DELIVERY.getCode().equals(intent.getSkillCode()) ) { | |||||
| if (!redisTemplate2.hasKey("group:device" + ":" + intent.getDevId() + ":" + "session")) { | |||||
| // 获取名称为"meeting-order"的BaseTemplate的实例 | |||||
| redisTemplate.opsForValue().increment("dashboard:delivery", 1); | |||||
| pushIntoDashboardRedis(enterpriseName, "寄快递", "skill"); | |||||
| } | |||||
| }else if (SkillConstants.SkillType.FLIGHT.getCode().equals(intent.getSkillCode()) ) { | |||||
| redisTemplate.opsForValue().increment("dashboard:flight", 1); | |||||
| pushIntoDashboardRedis(enterpriseName, "查询航班信息", "skill"); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| @@ -0,0 +1,17 @@ | |||||
| package com.xueyi.nlt.nlt.service.impl; | |||||
| import com.xueyi.common.web.entity.service.impl.BaseServiceImpl; | |||||
| import com.xueyi.nlt.api.nlt.domain.vo.DmIntentVo; | |||||
| import com.xueyi.nlt.api.nlt.domain.vo.response.DmIntentResponse; | |||||
| import com.xueyi.nlt.nlt.domain.query.DmRegularQuery; | |||||
| import com.xueyi.nlt.nlt.domain.dto.DmRegularDto; | |||||
| import com.xueyi.nlt.nlt.manager.IDmRegularManager; | |||||
| import com.xueyi.nlt.nlt.service.IDmRegularService; | |||||
| public class DmRegularServiceImpl extends BaseServiceImpl<DmRegularQuery, DmRegularDto, IDmRegularManager> implements IDmRegularService { | |||||
| @Override | |||||
| public DmIntentResponse doRegularMatch(String content) { | |||||
| return baseManager.doRegularMatch(content); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,51 @@ | |||||
| package com.xueyi.nlt.nlt.service.impl; | |||||
| import com.alibaba.fastjson2.JSONObject; | |||||
| import com.xueyi.nlt.api.nlt.feign.RemoteBaiduNLPService; | |||||
| import com.xueyi.nlt.nlt.config.BaiduConfig; | |||||
| import com.xueyi.nlt.nlt.domain.vo.WordProcessVo; | |||||
| import com.xueyi.nlt.nlt.service.IDmWordProcessService; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.stereotype.Service; | |||||
| @Service | |||||
| public class DmWordProcessServiceImpl implements IDmWordProcessService { | |||||
| @Autowired | |||||
| RemoteBaiduNLPService remoteBaiduNLPService; | |||||
| @Autowired | |||||
| private BaiduConfig baiduConfig; | |||||
| private static final Logger log = LoggerFactory.getLogger(DmWordProcessServiceImpl.class); | |||||
| public WordProcessVo semanticIntegrityDetection (WordProcessVo wordProcessVo){ | |||||
| log.info("元数据:" + wordProcessVo.getMetadata()); | |||||
| String body = "grant_type=client_credentials&client_id=" + baiduConfig.getApiKey() + "&client_secret=" + baiduConfig.getSecretKey(); | |||||
| JSONObject accessJson = remoteBaiduNLPService.getAccessToken(body); | |||||
| JSONObject result = remoteBaiduNLPService.detected("UTF-8", accessJson.getString("access_token"), "{\"text\":\"" + wordProcessVo.getMetadata() + "\"}"); | |||||
| if(result.containsKey("ppl")){ | |||||
| if(result != null){ | |||||
| if(result.containsKey("error_code")){ | |||||
| String errorMsg = String.format("语义完整性检测失败,错误码:%d,错误信息:%s", result.getInteger("error_code"),result.getString("error_msg")); | |||||
| log.error(errorMsg); | |||||
| }else{ | |||||
| Double ppl = result.getDouble("ppl"); | |||||
| if(ppl < 300) { | |||||
| wordProcessVo.setProcessedResult("2"); | |||||
| }else if(ppl > 1000){ | |||||
| wordProcessVo.setProcessedResult("0"); | |||||
| }else{ | |||||
| wordProcessVo.setProcessedResult("1"); | |||||
| } | |||||
| log.info("语义完整性检测结果:" + wordProcessVo.getProcessedResult()); | |||||
| } | |||||
| }else{ | |||||
| log.error("语义完整性检测失败,未获取到返回结果"); | |||||
| } | |||||
| } | |||||
| return wordProcessVo; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,19 @@ | |||||
| package com.xueyi.nlt.nlt.service.impl; | |||||
| import com.alibaba.fastjson2.JSONObject; | |||||
| import com.yomahub.tlog.core.annotation.TLogAspect; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import org.springframework.stereotype.Component; | |||||
| @Component | |||||
| public class LogServiceImpl { | |||||
| private static final Logger intentLog = LoggerFactory.getLogger("intentLog"); | |||||
| @TLogAspect({"enterpriseName","type"}) | |||||
| public void record(JSONObject jo,String text,String enterpriseName, String type) { | |||||
| jo.put("text",text); | |||||
| intentLog.info("{}", jo.toJSONString()); | |||||
| } | |||||
| } | |||||
| @@ -57,11 +57,38 @@ | |||||
| </filter> | </filter> | ||||
| </appender> | </appender> | ||||
| <!-- 系统日志输出 --> | |||||
| <appender name="file_intent" class="ch.qos.logback.core.rolling.RollingFileAppender"> | |||||
| <file>${log.path}/intent/intentInfo.log</file> | |||||
| <!-- 循环政策:基于时间创建日志文件 --> | |||||
| <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> | |||||
| <!-- 日志文件名格式 --> | |||||
| <fileNamePattern>${log.path}/intent/intentInfo.%d{yyyy-MM-dd}.log</fileNamePattern> | |||||
| <!-- 日志最大的历史 60天 --> | |||||
| <maxHistory>60</maxHistory> | |||||
| </rollingPolicy> | |||||
| <encoder class="com.yomahub.tlog.core.enhance.logback.AspectLogbackEncoder"> | |||||
| <pattern>${log.pattern}</pattern> | |||||
| </encoder> | |||||
| <filter class="ch.qos.logback.classic.filter.LevelFilter"> | |||||
| <!-- 过滤的级别 --> | |||||
| <level>INFO</level> | |||||
| <!-- 匹配时的操作:接收(记录) --> | |||||
| <onMatch>ACCEPT</onMatch> | |||||
| <!-- 不匹配时的操作:拒绝(不记录) --> | |||||
| <onMismatch>DENY</onMismatch> | |||||
| </filter> | |||||
| </appender> | |||||
| <!-- 系统模块日志级别控制 --> | <!-- 系统模块日志级别控制 --> | ||||
| <logger name="com.xueyi" level="info" /> | <logger name="com.xueyi" level="info" /> | ||||
| <!-- Spring日志级别控制 --> | <!-- Spring日志级别控制 --> | ||||
| <logger name="org.springframework" level="warn" /> | <logger name="org.springframework" level="warn" /> | ||||
| <!-- 意图业务日志级别控制 --> | |||||
| <logger name="intentLog" additivity="false" level="info" > | |||||
| <appender-ref ref="file_intent" /> | |||||
| </logger> | |||||
| <root level="info"> | <root level="info"> | ||||
| <appender-ref ref="console" /> | <appender-ref ref="console" /> | ||||
| </root> | </root> | ||||
| @@ -70,5 +97,6 @@ | |||||
| <root level="info"> | <root level="info"> | ||||
| <appender-ref ref="file_info" /> | <appender-ref ref="file_info" /> | ||||
| <appender-ref ref="file_error" /> | <appender-ref ref="file_error" /> | ||||
| </root> | </root> | ||||
| </configuration> | </configuration> | ||||