From 9eccfde3619b90f4b8ed4e1b562979196a50beb9 Mon Sep 17 00:00:00 2001 From: jmal Date: Tue, 23 May 2023 11:23:05 +0800 Subject: [PATCH 01/20] update actions --- .github/workflows/build_docker.yml | 2 +- .github/workflows/ci.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_docker.yml b/.github/workflows/build_docker.yml index d4b1bb52..b7533067 100644 --- a/.github/workflows/build_docker.yml +++ b/.github/workflows/build_docker.yml @@ -2,7 +2,7 @@ name: build_docker on: push: - branches: [main] + branches: [master] release: types: [created] # 表示在创建新的 Release 时触发 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 24f58aec..71d05025 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,11 +3,11 @@ name: CI on: push: branches: - - main + - master pull_request: branches: - - main + - master jobs: lint: From 48306044a4816a80cb211cf917b545cf1086bdda Mon Sep 17 00:00:00 2001 From: jmal Date: Fri, 11 Aug 2023 16:33:16 +0800 Subject: [PATCH 02/20] chore(gitignore): update ignored files --- .gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 0fedcce5..4cf36a71 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ yarn-debug.log* yarn-error.log* pnpm-debug.log* lerna-debug.log* - +package-lock.json node_modules .DS_Store dist @@ -30,4 +30,4 @@ coverage # Environment variables files /service/.env -/docker-compose/nginx/html \ No newline at end of file +/docker-compose/nginx/html From d8276a754fff04ca8df2ac78d34b30e2117df142 Mon Sep 17 00:00:00 2001 From: jmal Date: Tue, 29 Aug 2023 15:22:08 +0800 Subject: [PATCH 03/20] feat(*): add chat record --- service/src/index.ts | 41 +++- service/src/storage/mongo.ts | 74 ++++++- src/api/index.ts | 11 +- src/components/common/Setting/Audit.vue | 3 +- src/components/common/Setting/ChatRecord.vue | 158 +++++++++++++++ .../common/Setting/Message/Avatar.vue | 20 ++ .../common/Setting/Message/Text.vue | 117 +++++++++++ .../common/Setting/Message/index.vue | 187 ++++++++++++++++++ .../common/Setting/Message/style.less | 75 +++++++ src/components/common/Setting/index.vue | 36 ++-- src/locales/en-US.ts | 1 + src/locales/ko-KR.ts | 1 + src/locales/zh-CN.ts | 1 + src/locales/zh-TW.ts | 1 + 14 files changed, 704 insertions(+), 22 deletions(-) create mode 100644 src/components/common/Setting/ChatRecord.vue create mode 100644 src/components/common/Setting/Message/Avatar.vue create mode 100644 src/components/common/Setting/Message/Text.vue create mode 100644 src/components/common/Setting/Message/index.vue create mode 100644 src/components/common/Setting/Message/style.less diff --git a/service/src/index.ts b/service/src/index.ts index 3c316ebd..1b591a31 100644 --- a/service/src/index.ts +++ b/service/src/index.ts @@ -21,6 +21,7 @@ import { getChat, getChatRoom, getChatRooms, + getChatRoomsCount, getChats, getUser, getUserById, @@ -87,6 +88,42 @@ router.get('/chatrooms', auth, async (req, res) => { } }) +function formatTimestamp(timestamp: number) { + const date = new Date(timestamp) + const year = date.getFullYear() + const month = String(date.getMonth() + 1).padStart(2, '0') + const day = String(date.getDate()).padStart(2, '0') + const hours = String(date.getHours()).padStart(2, '0') + const minutes = String(date.getMinutes()).padStart(2, '0') + const seconds = String(date.getSeconds()).padStart(2, '0') + + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}` +} + +router.get('/chatrooms-count', auth, async (req, res) => { + try { + const userId = req.query.userId as string + const page = +req.query.page + const size = +req.query.size + const rooms = await getChatRoomsCount(userId, page, size) + const result = [] + rooms.data.forEach((r) => { + result.push({ + uuid: r.roomId, + title: r.title, + userId: r.userId, + lastTime: formatTimestamp(r.dateTime), + chatCount: r.chatCount, + }) + }) + res.send({ status: 'Success', message: null, data: { data: result, total: rooms.total } }) + } + catch (error) { + console.error(error) + res.send({ status: 'Fail', message: 'Load error', data: [] }) + } +}) + router.post('/room-create', auth, async (req, res) => { try { const userId = req.headers.userId as string @@ -183,13 +220,13 @@ router.get('/chat-history', auth, async (req, res) => { const userId = req.headers.userId as string const roomId = +req.query.roomId const lastId = req.query.lastId as string + const all = req.query.all as string if (!roomId || !await existsChatRoom(userId, roomId)) { res.send({ status: 'Success', message: null, data: [] }) // res.send({ status: 'Fail', message: 'Unknow room', data: null }) return } - const chats = await getChats(roomId, !isNotEmptyString(lastId) ? null : parseInt(lastId)) - + const chats = await getChats(roomId, !isNotEmptyString(lastId) ? null : parseInt(lastId), all) const result = [] chats.forEach((c) => { if (c.status !== Status.InversionDeleted) { diff --git a/service/src/storage/mongo.ts b/service/src/storage/mongo.ts index 12ec6f72..f842f9a7 100644 --- a/service/src/storage/mongo.ts +++ b/service/src/storage/mongo.ts @@ -138,6 +138,71 @@ export async function getChatRooms(userId: string) { return rooms } +export async function getChatRoomsCount(userId: string, page: number, size: number) { + let total = 0 + const skip = (page - 1) * size + const limit = size + const agg = [] + if (userId !== null && userId !== undefined && userId.trim().length !== 0) { + agg.push({ + $match: { + userId, + }, + }) + total = await roomCol.countDocuments({ userId }) + } + else { + total = await roomCol.countDocuments() + } + const agg2 = [ + { + $lookup: { + from: 'chat', + localField: 'roomId', + foreignField: 'roomId', + as: 'chat', + }, + }, { + $unwind: { + path: '$chat', + preserveNullAndEmptyArrays: true, + }, + }, { + $group: { + _id: '$_id', + userId: { + $first: '$userId', + }, + title: { + $first: '$title', + }, + roomId: { + $first: '$roomId', + }, + dateTime: { + $last: '$chat.dateTime', + }, + chatCount: { + $sum: 1, + }, + }, + }, { + $sort: { + dateTime: -1, + }, + }, { + $skip: skip, + }, { + $limit: limit, + }, + ] + Array.prototype.push.apply(agg, agg2) + + const cursor = roomCol.aggregate(agg) + const data = await cursor.toArray() + return { total, data } +} + export async function getChatRoom(userId: string, roomId: number) { return await roomCol.findOne({ userId, roomId, status: { $ne: Status.Deleted } }) as ChatRoom } @@ -152,10 +217,15 @@ export async function deleteAllChatRooms(userId: string) { await chatCol.updateMany({ userId, status: Status.Normal }, { $set: { status: Status.Deleted } }) } -export async function getChats(roomId: number, lastId?: number) { +export async function getChats(roomId: number, lastId?: number, all?: string) { if (!lastId) lastId = new Date().getTime() - const query = { roomId, uuid: { $lt: lastId }, status: { $ne: Status.Deleted } } + let query = {} + if (all === null || all === undefined || all.trim().length === 0) + query = { roomId, uuid: { $lt: lastId }, status: { $ne: Status.Deleted } } + else + query = { roomId, uuid: { $lt: lastId } } + const limit = 20 const cursor = await chatCol.find(query).sort({ dateTime: -1 }).limit(limit) const chats = [] diff --git a/src/api/index.ts b/src/api/index.ts index 3c76a0f1..37df9c97 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -150,6 +150,13 @@ export function fetchGetChatRooms() { }) } +export function fetchGetChatRoomsCount(page: number, size: number, userId: string) { + return get({ + url: '/chatrooms-count', + data: { page, size, userId }, + }) +} + export function fetchCreateChatRoom(title: string, roomId: number) { return post({ url: '/room-create', @@ -185,9 +192,9 @@ export function fetchDeleteChatRoom(roomId: number) { }) } -export function fetchGetChatHistory(roomId: number, lastId?: number) { +export function fetchGetChatHistory(roomId: number, lastId?: number, all?: string) { return get({ - url: `/chat-history?roomId=${roomId}&lastId=${lastId}`, + url: `/chat-history?roomId=${roomId}&lastId=${lastId}&all=${all}`, }) } diff --git a/src/components/common/Setting/Audit.vue b/src/components/common/Setting/Audit.vue index b093919b..09c48371 100644 --- a/src/components/common/Setting/Audit.vue +++ b/src/components/common/Setting/Audit.vue @@ -1,8 +1,7 @@ + + + + diff --git a/src/components/common/Setting/Message/Avatar.vue b/src/components/common/Setting/Message/Avatar.vue new file mode 100644 index 00000000..fb2454df --- /dev/null +++ b/src/components/common/Setting/Message/Avatar.vue @@ -0,0 +1,20 @@ + + + diff --git a/src/components/common/Setting/Message/Text.vue b/src/components/common/Setting/Message/Text.vue new file mode 100644 index 00000000..25c3b7ad --- /dev/null +++ b/src/components/common/Setting/Message/Text.vue @@ -0,0 +1,117 @@ + + + + + diff --git a/src/components/common/Setting/Message/index.vue b/src/components/common/Setting/Message/index.vue new file mode 100644 index 00000000..2048ff1c --- /dev/null +++ b/src/components/common/Setting/Message/index.vue @@ -0,0 +1,187 @@ + + + diff --git a/src/components/common/Setting/Message/style.less b/src/components/common/Setting/Message/style.less new file mode 100644 index 00000000..0b2bbf8c --- /dev/null +++ b/src/components/common/Setting/Message/style.less @@ -0,0 +1,75 @@ +.markdown-body { + background-color: transparent; + font-size: 14px; + + p { + white-space: pre-wrap; + } + + ol { + list-style-type: decimal; + } + + ul { + list-style-type: disc; + } + + pre code, + pre tt { + line-height: 1.65; + } + + .highlight pre, + pre { + background-color: #fff; + } + + code.hljs { + padding: 0; + } + + .code-block { + &-wrapper { + position: relative; + padding-top: 24px; + } + + &-header { + position: absolute; + top: 5px; + right: 0; + width: 100%; + padding: 0 1rem; + display: flex; + justify-content: flex-end; + align-items: center; + color: #b3b3b3; + + &__copy { + cursor: pointer; + margin-left: 0.5rem; + user-select: none; + + &:hover { + color: #65a665; + } + } + } + } + +} + +html.dark { + + .message-reply { + .whitespace-pre-wrap { + white-space: pre-wrap; + color: var(--n-text-color); + } + } + + .highlight pre, + pre { + background-color: #282c34; + } +} diff --git a/src/components/common/Setting/index.vue b/src/components/common/Setting/index.vue index c97ec9ce..2d802f80 100644 --- a/src/components/common/Setting/index.vue +++ b/src/components/common/Setting/index.vue @@ -13,6 +13,7 @@ import Key from './Keys.vue' import { SvgIcon } from '@/components/common' import { useAuthStore, useUserStore } from '@/store' import { useBasicLayout } from '@/hooks/useBasicLayout' +import ChatRecord from '@/components/common/Setting/ChatRecord.vue' const props = defineProps() @@ -82,6 +83,27 @@ const show = computed({ + + + + + + + + + + + + - - - - - - - - diff --git a/src/locales/en-US.ts b/src/locales/en-US.ts index d7e7d7d6..2f8bf1ec 100644 --- a/src/locales/en-US.ts +++ b/src/locales/en-US.ts @@ -77,6 +77,7 @@ export default { advanced: 'Advanced', statistics: 'Statistics', config: 'Base Config', + chatRecord: 'Chat History', siteConfig: 'Site Config', mailConfig: 'Mail Config', auditConfig: 'Audit Config', diff --git a/src/locales/ko-KR.ts b/src/locales/ko-KR.ts index 9bae20e0..fcf5001f 100644 --- a/src/locales/ko-KR.ts +++ b/src/locales/ko-KR.ts @@ -77,6 +77,7 @@ export default { advanced: '고급', config: '기본 구성', statistics: '통계학', + chatRecord: '채팅 기록', siteConfig: '사이트 구성', mailConfig: '메일 구성', auditConfig: '감사 구성', diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index 0eb570dc..328390a3 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -77,6 +77,7 @@ export default { advanced: '高级', statistics: '统计', config: '基本配置', + chatRecord: '聊天记录', siteConfig: '网站配置', mailConfig: '邮箱配置', auditConfig: '敏感词审核', diff --git a/src/locales/zh-TW.ts b/src/locales/zh-TW.ts index 2a4051ca..ae388263 100644 --- a/src/locales/zh-TW.ts +++ b/src/locales/zh-TW.ts @@ -77,6 +77,7 @@ export default { advanced: '高級', statistics: '統計', config: '基本設定', + chatRecord: '聊天紀錄', siteConfig: '网站配置', mailConfig: '邮箱配置', auditConfig: '敏感词审核', From 7414dc578876664882eeb6a32027634a9db07f49 Mon Sep 17 00:00:00 2001 From: jmal Date: Tue, 29 Aug 2023 15:49:48 +0800 Subject: [PATCH 04/20] refactor: add chat record --- src/components/common/Setting/ChatRecord.vue | 4 +- .../common/Setting/Message/index.vue | 66 +------------------ 2 files changed, 4 insertions(+), 66 deletions(-) diff --git a/src/components/common/Setting/ChatRecord.vue b/src/components/common/Setting/ChatRecord.vue index 76691fc3..f46cc041 100644 --- a/src/components/common/Setting/ChatRecord.vue +++ b/src/components/common/Setting/ChatRecord.vue @@ -45,7 +45,7 @@ const columns = [{ show.value = true dataSources.value.length = 0 fetchGetChatHistory(row.uuid, undefined, 'all').then((res: any) => { - dataSources.value = res.data + dataSources.value = res.data as Chat.Chat[] }) }, }, @@ -75,7 +75,7 @@ const pagination = reactive ({ }, }) -async function handleSelectUser(value: string) { +async function handleSelectUser() { await handleGetChatRoomsCount(pagination.page) } diff --git a/src/components/common/Setting/Message/index.vue b/src/components/common/Setting/Message/index.vue index 2048ff1c..97533878 100644 --- a/src/components/common/Setting/Message/index.vue +++ b/src/components/common/Setting/Message/index.vue @@ -1,12 +1,9 @@