mirror of
https://github.com/zongzibinbin/MallChat.git
synced 2026-03-14 22:23:42 +08:00
Merge branch 'main' into main
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
package com.abin.mallchat.custom.chatai.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class GPTRequestDTO {
|
||||
/**
|
||||
* 聊天内容
|
||||
*/
|
||||
private String content;
|
||||
/**
|
||||
* 用户Id
|
||||
*/
|
||||
private Long uid;
|
||||
}
|
||||
@@ -4,12 +4,17 @@ import cn.hutool.http.HttpResponse;
|
||||
import com.abin.mallchat.common.chat.domain.entity.Message;
|
||||
import com.abin.mallchat.common.chat.domain.entity.msg.MessageExtra;
|
||||
import com.abin.mallchat.common.common.constant.RedisKey;
|
||||
import com.abin.mallchat.common.common.domain.dto.FrequencyControlDTO;
|
||||
import com.abin.mallchat.common.common.exception.FrequencyControlException;
|
||||
import com.abin.mallchat.common.common.service.frequencycontrol.FrequencyControlUtil;
|
||||
import com.abin.mallchat.common.common.utils.RedisUtils;
|
||||
import com.abin.mallchat.custom.chatai.dto.GPTRequestDTO;
|
||||
import com.abin.mallchat.custom.chatai.properties.ChatGLM2Properties;
|
||||
import com.abin.mallchat.custom.chatai.utils.ChatGLM2Utils;
|
||||
import com.abin.mallchat.custom.user.domain.vo.response.user.UserInfoResp;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
@@ -21,10 +26,15 @@ import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.abin.mallchat.common.common.constant.RedisKey.USER_GLM2_TIME_LAST;
|
||||
import static com.abin.mallchat.common.common.service.frequencycontrol.FrequencyControlStrategyFactory.TOTAL_COUNT_WITH_IN_FIX_TIME_FREQUENCY_CONTROLLER;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ChatGLM2Handler extends AbstractChatAIHandler {
|
||||
/**
|
||||
* ChatGLM2Handler限流前缀
|
||||
*/
|
||||
private static final String CHAT_GLM2_FREQUENCY_PREFIX = "ChatGLM2Handler";
|
||||
|
||||
private static final List<String> ERROR_MSG = Arrays.asList(
|
||||
"还摸鱼呢?你不下班我还要下班呢。。。。",
|
||||
@@ -74,27 +84,42 @@ public class ChatGLM2Handler extends AbstractChatAIHandler {
|
||||
protected String doChat(Message message) {
|
||||
String content = message.getContent().replace("@" + AI_NAME, "").trim();
|
||||
Long uid = message.getFromUid();
|
||||
Long minute;
|
||||
try {
|
||||
FrequencyControlDTO frequencyControlDTO = new FrequencyControlDTO();
|
||||
frequencyControlDTO.setKey(CHAT_GLM2_FREQUENCY_PREFIX + ":" + uid);
|
||||
frequencyControlDTO.setUnit(TimeUnit.MINUTES);
|
||||
frequencyControlDTO.setCount(1);
|
||||
frequencyControlDTO.setTime(glm2Properties.getMinute().intValue());
|
||||
return FrequencyControlUtil.executeWithFrequencyControl(TOTAL_COUNT_WITH_IN_FIX_TIME_FREQUENCY_CONTROLLER, frequencyControlDTO, () -> sendRequestToGPT(new GPTRequestDTO(content, uid)));
|
||||
} catch (FrequencyControlException e) {
|
||||
return "你太快了亲爱的~过一会再来找人家~";
|
||||
} catch (Throwable e) {
|
||||
return "系统开小差啦~~";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @param gptRequestDTO
|
||||
* @return
|
||||
*/
|
||||
@Nullable
|
||||
private String sendRequestToGPT(GPTRequestDTO gptRequestDTO) {
|
||||
String content = gptRequestDTO.getContent();
|
||||
String text;
|
||||
if ((minute = userMinutesLater(uid)) > 0) {
|
||||
text = "你太快了 " + minute + "分钟后重试";
|
||||
} else {
|
||||
HttpResponse response = null;
|
||||
try {
|
||||
response = ChatGLM2Utils
|
||||
.create()
|
||||
.url(glm2Properties.getUrl())
|
||||
.prompt(content)
|
||||
.timeout(glm2Properties.getTimeout())
|
||||
.send();
|
||||
text = ChatGLM2Utils.parseText(response);
|
||||
} catch (Exception e) {
|
||||
log.warn("glm2 doChat warn:", e);
|
||||
return getErrorText();
|
||||
}
|
||||
if (StringUtils.isNotBlank(text)) {
|
||||
RedisUtils.set(RedisKey.getKey(USER_GLM2_TIME_LAST, uid), new Date(), glm2Properties.getMinute(), TimeUnit.MINUTES);
|
||||
}
|
||||
HttpResponse response = null;
|
||||
try {
|
||||
response = ChatGLM2Utils
|
||||
.create()
|
||||
.url(glm2Properties.getUrl())
|
||||
.prompt(content)
|
||||
.timeout(glm2Properties.getTimeout())
|
||||
.send();
|
||||
text = ChatGLM2Utils.parseText(response);
|
||||
} catch (Exception e) {
|
||||
log.warn("glm2 doChat warn:", e);
|
||||
return getErrorText();
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
@@ -3,9 +3,10 @@ package com.abin.mallchat.custom.chatai.handler;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.abin.mallchat.common.chat.domain.entity.Message;
|
||||
import com.abin.mallchat.common.chat.domain.entity.msg.MessageExtra;
|
||||
import com.abin.mallchat.common.common.constant.RedisKey;
|
||||
import com.abin.mallchat.common.common.utils.DateUtils;
|
||||
import com.abin.mallchat.common.common.utils.RedisUtils;
|
||||
import com.abin.mallchat.common.common.domain.dto.FrequencyControlDTO;
|
||||
import com.abin.mallchat.common.common.exception.FrequencyControlException;
|
||||
import com.abin.mallchat.common.common.service.frequencycontrol.FrequencyControlUtil;
|
||||
import com.abin.mallchat.custom.chatai.dto.GPTRequestDTO;
|
||||
import com.abin.mallchat.custom.chatai.properties.ChatGPTProperties;
|
||||
import com.abin.mallchat.custom.chatai.utils.ChatGPTUtils;
|
||||
import com.abin.mallchat.custom.user.domain.vo.response.user.UserInfoResp;
|
||||
@@ -17,9 +18,15 @@ import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.abin.mallchat.common.common.service.frequencycontrol.FrequencyControlStrategyFactory.TOTAL_COUNT_WITH_IN_FIX_TIME_FREQUENCY_CONTROLLER;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class GPTChatAIHandler extends AbstractChatAIHandler {
|
||||
/**
|
||||
* GPTChatAIHandler限流前缀
|
||||
*/
|
||||
private static final String CHAT_FREQUENCY_PREFIX = "GPTChatAIHandler";
|
||||
|
||||
@Autowired
|
||||
private ChatGPTProperties chatGPTProperties;
|
||||
@@ -51,43 +58,44 @@ public class GPTChatAIHandler extends AbstractChatAIHandler {
|
||||
return chatGPTProperties.getAIUserId();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected String doChat(Message message) {
|
||||
String content = message.getContent().replace("@" + AI_NAME, "").trim();
|
||||
Long uid = message.getFromUid();
|
||||
Long chatNum;
|
||||
try {
|
||||
FrequencyControlDTO frequencyControlDTO = new FrequencyControlDTO();
|
||||
frequencyControlDTO.setKey(CHAT_FREQUENCY_PREFIX + ":" + uid);
|
||||
frequencyControlDTO.setUnit(TimeUnit.HOURS);
|
||||
frequencyControlDTO.setCount(chatGPTProperties.getLimit());
|
||||
frequencyControlDTO.setTime(24);
|
||||
return FrequencyControlUtil.executeWithFrequencyControl(TOTAL_COUNT_WITH_IN_FIX_TIME_FREQUENCY_CONTROLLER, frequencyControlDTO, () -> sendRequestToGPT(new GPTRequestDTO(content, uid)));
|
||||
} catch (FrequencyControlException e) {
|
||||
return "亲爱的,你今天找我聊了" + chatGPTProperties.getLimit() + "次了~人家累了~明天见";
|
||||
} catch (Throwable e) {
|
||||
return "系统开小差啦~~";
|
||||
}
|
||||
}
|
||||
|
||||
private String sendRequestToGPT(GPTRequestDTO gptRequestDTO) {
|
||||
String content = gptRequestDTO.getContent();
|
||||
String text;
|
||||
if ((chatNum = getUserChatNum(uid)) > chatGPTProperties.getLimit()) {
|
||||
text = "你今天已经和我聊了" + chatNum + "次了,我累了,明天再聊吧";
|
||||
} else {
|
||||
HttpResponse response = null;
|
||||
try {
|
||||
response = ChatGPTUtils.create(chatGPTProperties.getKey())
|
||||
.proxyUrl(chatGPTProperties.getProxyUrl())
|
||||
.model(chatGPTProperties.getModelName())
|
||||
.timeout(chatGPTProperties.getTimeout())
|
||||
.prompt(content)
|
||||
.send();
|
||||
text = ChatGPTUtils.parseText(response);
|
||||
userChatNumInrc(uid);
|
||||
} catch (Exception e) {
|
||||
log.warn("gpt doChat warn:", e);
|
||||
text = "我累了,明天再聊吧";
|
||||
}
|
||||
HttpResponse response = null;
|
||||
try {
|
||||
response = ChatGPTUtils.create(chatGPTProperties.getKey())
|
||||
.proxyUrl(chatGPTProperties.getProxyUrl())
|
||||
.model(chatGPTProperties.getModelName())
|
||||
.timeout(chatGPTProperties.getTimeout())
|
||||
.prompt(content)
|
||||
.send();
|
||||
text = ChatGPTUtils.parseText(response);
|
||||
} catch (Exception e) {
|
||||
log.warn("gpt doChat warn:", e);
|
||||
text= "我累了,明天再聊吧";
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
private Long userChatNumInrc(Long uid) {
|
||||
return RedisUtils.inc(RedisKey.getKey(RedisKey.USER_CHAT_NUM, uid), DateUtils.getEndTimeByToday().intValue(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
private Long getUserChatNum(Long uid) {
|
||||
Long num = RedisUtils.get(RedisKey.getKey(RedisKey.USER_CHAT_NUM, uid), Long.class);
|
||||
return num == null ? 0 : num;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.abin.mallchat.custom.user.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.abin.mallchat.common.common.annotation.FrequencyControl;
|
||||
import com.abin.mallchat.common.common.config.ThreadPoolConfig;
|
||||
@@ -20,6 +19,8 @@ import com.abin.mallchat.custom.user.service.LoginService;
|
||||
import com.abin.mallchat.custom.user.service.WebSocketService;
|
||||
import com.abin.mallchat.custom.user.service.adapter.WSAdapter;
|
||||
import com.abin.mallchat.custom.user.websocket.NettyUtil;
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
||||
import lombok.SneakyThrows;
|
||||
@@ -32,13 +33,13 @@ import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
@@ -51,12 +52,18 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
@Slf4j
|
||||
public class WebSocketServiceImpl implements WebSocketService {
|
||||
|
||||
private static final Duration EXPIRE_TIME = Duration.ofHours(1);
|
||||
private static final Long MAX_MUM_SIZE = 10000L;
|
||||
|
||||
private static final AtomicInteger CODE = new AtomicInteger();
|
||||
|
||||
/**
|
||||
* 所有请求登录的code与channel关系
|
||||
* todo 有可能有人请求了二维码,就是不登录,留个坑,之后处理
|
||||
*/
|
||||
private static final ConcurrentHashMap<Integer, Channel> WAIT_LOGIN_MAP = new ConcurrentHashMap<>();
|
||||
private static final Cache<Integer, Channel> WAIT_LOGIN_MAP = Caffeine.newBuilder()
|
||||
.expireAfterWrite(EXPIRE_TIME)
|
||||
.maximumSize(MAX_MUM_SIZE)
|
||||
.build();
|
||||
/**
|
||||
* 所有已连接的websocket连接列表和一些额外参数
|
||||
*/
|
||||
@@ -70,7 +77,6 @@ public class WebSocketServiceImpl implements WebSocketService {
|
||||
return ONLINE_WS_MAP;
|
||||
}
|
||||
|
||||
public static final int EXPIRE_SECONDS = 60 * 60;
|
||||
@Autowired
|
||||
private WxMpService wxMpService;
|
||||
@Autowired
|
||||
@@ -99,7 +105,7 @@ public class WebSocketServiceImpl implements WebSocketService {
|
||||
//生成随机不重复的登录码
|
||||
Integer code = generateLoginCode(channel);
|
||||
//请求微信接口,获取登录码地址
|
||||
WxMpQrCodeTicket wxMpQrCodeTicket = wxMpService.getQrcodeService().qrCodeCreateTmpTicket(code, EXPIRE_SECONDS);
|
||||
WxMpQrCodeTicket wxMpQrCodeTicket = wxMpService.getQrcodeService().qrCodeCreateTmpTicket(code, (int) EXPIRE_TIME.getSeconds());
|
||||
//返回给前端
|
||||
sendMsg(channel, WSAdapter.buildLoginResp(wxMpQrCodeTicket));
|
||||
}
|
||||
@@ -111,12 +117,11 @@ public class WebSocketServiceImpl implements WebSocketService {
|
||||
* @return
|
||||
*/
|
||||
private Integer generateLoginCode(Channel channel) {
|
||||
int code;
|
||||
do {
|
||||
code = RandomUtil.randomInt(Integer.MAX_VALUE);
|
||||
} while (WAIT_LOGIN_MAP.contains(code)
|
||||
|| Objects.nonNull(WAIT_LOGIN_MAP.putIfAbsent(code, channel)));
|
||||
return code;
|
||||
CODE.getAndIncrement();
|
||||
} while (WAIT_LOGIN_MAP.asMap().containsKey(CODE.get())
|
||||
|| Objects.isNull(WAIT_LOGIN_MAP.get(CODE.get(), c -> channel)));
|
||||
return CODE.get();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -203,12 +208,12 @@ public class WebSocketServiceImpl implements WebSocketService {
|
||||
@Override
|
||||
public Boolean scanLoginSuccess(Integer loginCode, User user, String token) {
|
||||
//发送消息
|
||||
Channel channel = WAIT_LOGIN_MAP.get(loginCode);
|
||||
Channel channel = WAIT_LOGIN_MAP.getIfPresent(loginCode);
|
||||
if (Objects.isNull(channel)) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
//移除code
|
||||
WAIT_LOGIN_MAP.remove(loginCode);
|
||||
WAIT_LOGIN_MAP.invalidate(loginCode);
|
||||
//用户登录
|
||||
loginSuccess(channel, user, token);
|
||||
return true;
|
||||
@@ -216,7 +221,7 @@ public class WebSocketServiceImpl implements WebSocketService {
|
||||
|
||||
@Override
|
||||
public Boolean scanSuccess(Integer loginCode) {
|
||||
Channel channel = WAIT_LOGIN_MAP.get(loginCode);
|
||||
Channel channel = WAIT_LOGIN_MAP.getIfPresent(loginCode);
|
||||
if (Objects.isNull(channel)) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
@@ -291,4 +296,6 @@ public class WebSocketServiceImpl implements WebSocketService {
|
||||
reentrantLock.unlock();
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import io.netty.handler.codec.http.HttpHeaders;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
public class HttpHeadersHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
@@ -19,11 +19,8 @@ public class HttpHeadersHandler extends ChannelInboundHandlerAdapter {
|
||||
UrlBuilder urlBuilder = UrlBuilder.ofHttp(request.uri());
|
||||
|
||||
// 获取token参数
|
||||
CharSequence sequence = urlBuilder.getQuery().get("token");
|
||||
if (Objects.nonNull(sequence)) {
|
||||
String token = sequence.toString();
|
||||
NettyUtil.setAttr(ctx.channel(), NettyUtil.TOKEN, token);
|
||||
}
|
||||
String token = Optional.ofNullable(urlBuilder.getQuery()).map(k->k.get("token")).map(CharSequence::toString).orElse("");
|
||||
NettyUtil.setAttr(ctx.channel(), NettyUtil.TOKEN, token);
|
||||
|
||||
// 获取请求路径
|
||||
request.setUri(urlBuilder.getPath().toString());
|
||||
|
||||
Reference in New Issue
Block a user