优化用户聊天记录显示

This commit is contained in:
RockYang 2023-03-28 17:19:50 +08:00
parent ebc2041e8a
commit 4e575d01db
5 changed files with 92 additions and 16 deletions

View File

@ -4,7 +4,7 @@
## TODOLIST ## TODOLIST
* [ ] 使用 level DB 保存用户聊天记录 * [x] 使用 level DB 保存用户聊天记录
* [x] 用户聊天鉴权,设置口令模式 * [x] 用户聊天鉴权,设置口令模式
* [ ] 定期清理不在线的会话 sessionID 和聊天上下文记录 * [ ] 定期清理不在线的会话 sessionID 和聊天上下文记录
* [x] 给 Token 设置调用次数 * [x] 给 Token 设置调用次数

View File

@ -341,3 +341,25 @@ func (s *Server) GetChatHistoryHandle(c *gin.Context) {
c.JSON(http.StatusOK, types.BizVo{Code: types.Success, Data: messages}) c.JSON(http.StatusOK, types.BizVo{Code: types.Success, Data: messages})
} }
// ClearHistoryHandle 清空聊天记录
func (s *Server) ClearHistoryHandle(c *gin.Context) {
sessionId := c.GetHeader(types.TokenName)
var data struct {
Role string `json:"role"`
}
err := json.NewDecoder(c.Request.Body).Decode(&data)
if err != nil {
c.JSON(http.StatusOK, types.BizVo{Code: types.Failed, Message: "Invalid args"})
return
}
session := s.ChatSession[sessionId]
err = ClearChatHistory(session.Username, data.Role)
if err != nil {
c.JSON(http.StatusOK, types.BizVo{Code: types.Failed, Message: "Failed to remove data from DB"})
return
}
c.JSON(http.StatusOK, types.BizVo{Code: types.Success})
}

View File

@ -291,6 +291,7 @@ func (s *Server) GetChatRoleListHandle(c *gin.Context) {
}) })
} }
} }
c.JSON(http.StatusOK, types.BizVo{Code: types.Success, Message: types.OkMsg, Data: res}) c.JSON(http.StatusOK, types.BizVo{Code: types.Success, Message: types.OkMsg, Data: res})
} }

View File

@ -83,6 +83,7 @@ func (s *Server) Run(webRoot embed.FS, path string, debug bool) {
engine.POST("/api/login", s.LoginHandle) engine.POST("/api/login", s.LoginHandle)
engine.Any("/api/chat", s.ChatHandle) engine.Any("/api/chat", s.ChatHandle)
engine.POST("api/chat/history", s.GetChatHistoryHandle) engine.POST("api/chat/history", s.GetChatHistoryHandle)
engine.POST("api/chat/history/clear", s.ClearHistoryHandle)
engine.POST("/api/config/set", s.ConfigSetHandle) engine.POST("/api/config/set", s.ConfigSetHandle)
engine.GET("/api/config/chat-roles/get", s.GetChatRoleListHandle) engine.GET("/api/config/chat-roles/get", s.GetChatRoleListHandle)
@ -157,7 +158,7 @@ func corsMiddleware() gin.HandlerFunc {
c.Header("Access-Control-Allow-Origin", origin) c.Header("Access-Control-Allow-Origin", origin)
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE") c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
//允许跨域设置可以返回其他子段,可以自定义字段 //允许跨域设置可以返回其他子段,可以自定义字段
c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, Content-Type, ChatGPT-Username") c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, Content-Type, ChatGPT-TOKEN")
// 允许浏览器(客户端)可以解析的头部 (重要) // 允许浏览器(客户端)可以解析的头部 (重要)
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers") c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers")
//设置缓存时间 //设置缓存时间

View File

@ -3,7 +3,7 @@
<div id="container"> <div id="container">
<div class="tool-box"> <div class="tool-box">
<el-image style="width: 24px; height: 24px" :src="logo"/> <el-image style="width: 24px; height: 24px" :src="logo"/>
<el-button round>欢迎来到人工智能时代</el-button> <!-- <el-button round>WeChatGPT</el-button>-->
<el-select v-model="role" class="chat-role" <el-select v-model="role" class="chat-role"
v-on:change="changeRole" v-on:change="changeRole"
placeholder="请选择对话角色"> placeholder="请选择对话角色">
@ -14,6 +14,12 @@
:value="item.key" :value="item.key"
/> />
</el-select> </el-select>
<el-button type="danger" class="clear-history" size="small" circle @click="clearChatHistory">
<el-icon>
<Delete/>
</el-icon>
</el-button>
</div> </div>
<div class="chat-box" id="chat-box" :style="{height: chatBoxHeight+'px'}"> <div class="chat-box" id="chat-box" :style="{height: chatBoxHeight+'px'}">
@ -89,7 +95,7 @@ import ChatPrompt from "@/components/ChatPrompt.vue";
import ChatReply from "@/components/ChatReply.vue"; import ChatReply from "@/components/ChatReply.vue";
import {randString} from "@/utils/libs"; import {randString} from "@/utils/libs";
import {ElMessage, ElMessageBox} from 'element-plus' import {ElMessage, ElMessageBox} from 'element-plus'
import {Tools, Lock} from '@element-plus/icons-vue' import {Tools, Lock, Delete} from '@element-plus/icons-vue'
import ConfigDialog from '@/components/ConfigDialog.vue' import ConfigDialog from '@/components/ConfigDialog.vue'
import {httpPost, httpGet} from "@/utils/http"; import {httpPost, httpGet} from "@/utils/http";
import {getSessionId, setSessionId} from "@/utils/storage"; import {getSessionId, setSessionId} from "@/utils/storage";
@ -98,7 +104,7 @@ import 'highlight.js/styles/a11y-dark.css'
export default defineComponent({ export default defineComponent({
name: "XChat", name: "XChat",
components: {ChatPrompt, ChatReply, Tools, Lock, ConfigDialog}, components: {ChatPrompt, ChatReply, Tools, Lock, Delete, ConfigDialog},
data() { data() {
return { return {
title: 'ChatGPT 控制台', title: 'ChatGPT 控制台',
@ -118,7 +124,7 @@ export default defineComponent({
socket: null, socket: null,
toolBoxHeight: 61 + 42, // toolBoxHeight: 61 + 42, //
inputBoxWidth: window.innerWidth - 20, inputBoxWidth: window.innerWidth - 20,
sending: true, sending: false,
loading: false loading: false
} }
}, },
@ -189,9 +195,6 @@ export default defineComponent({
}); });
this.connect(); this.connect();
this.fetchChatHistory();
}, },
methods: { methods: {
@ -203,13 +206,16 @@ export default defineComponent({
socket.addEventListener('open', () => { socket.addEventListener('open', () => {
// //
httpGet("/api/config/chat-roles/get").then((res) => { httpGet("/api/config/chat-roles/get").then((res) => {
ElMessage.success('创建会话成功!'); // ElMessage.success('');
this.chatRoles = res.data; this.chatRoles = res.data;
this.loading = false this.loading = false
}).catch(() => { }).catch(() => {
ElMessage.error("获取聊天角色失败"); ElMessage.error("获取聊天角色失败");
}) })
//
this.fetchChatHistory();
if (this.connectingMessageBox && typeof this.connectingMessageBox.close === 'function') { if (this.connectingMessageBox && typeof this.connectingMessageBox.close === 'function') {
this.connectingMessageBox.close(); this.connectingMessageBox.close();
this.connectingMessageBox = null; this.connectingMessageBox = null;
@ -303,16 +309,33 @@ export default defineComponent({
break; break;
} }
} }
this.fetchChatHistory();
}, },
// //
fetchChatHistory: function () { fetchChatHistory: function () {
httpPost("/api/chat/history", {role: this.role}).then((res) => { httpPost("/api/chat/history", {role: this.role}).then((res) => {
this.chatData = res.data const data = res.data
}).catch((e) => { const md = require('markdown-it')();
console.error(e.message) for (let i = 0; i < data.length; i++) {
if (data[i].type === "prompt") {
this.chatData.push(data[i]);
continue;
}
data[i].content = md.render(data[i].content);
this.chatData.push(data[i]);
}
nextTick(() => {
hl.configure({ignoreUnescapedHTML: true})
const lines = document.querySelectorAll('.chat-line');
const blocks = lines[lines.length - 1].querySelectorAll('pre code');
blocks.forEach((block) => {
hl.highlightElement(block)
})
})
}).catch(() => {
// console.error(e.message)
}) })
}, },
@ -390,6 +413,31 @@ export default defineComponent({
if (e.keyCode === 13) { if (e.keyCode === 13) {
this.submitToken(); this.submitToken();
} }
},
//
clearChatHistory: function () {
ElMessageBox.confirm(
'确认要清空当前角色聊天历史记录吗?<br/>此操作不可以撤销!',
'操作提示:',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
dangerouslyUseHTMLString: true,
showClose: true,
closeOnClickModal: false,
center: true,
}
).then(() => {
httpPost("/api/chat/history/clear", {role: this.role}).then(() => {
ElMessage.success("当前角色会话已清空");
this.chatData = [];
}).catch(() => {
ElMessage.error("删除失败")
})
}).catch(() => {
})
} }
}, },
@ -418,7 +466,7 @@ export default defineComponent({
align-items center; align-items center;
.el-select { .el-select {
max-width 150px; max-width 120px;
} }
.chat-role { .chat-role {
@ -428,6 +476,10 @@ export default defineComponent({
.el-image { .el-image {
margin-right 5px; margin-right 5px;
} }
.clear-history {
margin-left 5px;
}
} }
.chat-box { .chat-box {