mirror of
https://github.com/labring/FastGPT.git
synced 2025-10-18 17:51:24 +00:00
@@ -153,7 +153,7 @@ const LogTable = ({
|
||||
tmbIds: isSelectAllTmb ? undefined : selectTmbIds,
|
||||
chatSearch,
|
||||
|
||||
title: headerTitle,
|
||||
title: headerTitle + ',' + t('app:logs_keys_chatDetails'),
|
||||
logKeys: enabledKeys,
|
||||
sourcesMap: Object.fromEntries(
|
||||
Object.entries(ChatSourceMap).map(([key, config]) => [
|
||||
|
@@ -14,11 +14,16 @@ import { Types } from 'mongoose';
|
||||
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||
import { ChatItemCollectionName } from '@fastgpt/service/core/chat/chatItemSchema';
|
||||
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
|
||||
import type { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { type ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { AppLogKeysEnum } from '@fastgpt/global/core/app/logs/constants';
|
||||
import { sanitizeCsvField } from '@fastgpt/service/common/file/csv';
|
||||
import { AppReadChatLogPerVal } from '@fastgpt/global/support/permission/app/constant';
|
||||
|
||||
const formatJsonString = (data: any) => {
|
||||
if (data == null) return '';
|
||||
return JSON.stringify(data).replace(/\n/g, '\\n');
|
||||
};
|
||||
|
||||
export type ExportChatLogsBody = GetAppChatLogsProps & {
|
||||
title: string;
|
||||
sourcesMap: Record<string, { label: string }>;
|
||||
@@ -178,6 +183,35 @@ async function handler(req: ApiRequestProps<ExportChatLogsBody, {}>, res: NextAp
|
||||
as: 'chatItemsData'
|
||||
}
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: ChatItemCollectionName,
|
||||
let: { chatId: '$chatId' },
|
||||
pipeline: [
|
||||
{
|
||||
$match: {
|
||||
$expr: {
|
||||
$and: [
|
||||
{ $eq: ['$appId', new Types.ObjectId(appId)] },
|
||||
{ $eq: ['$chatId', '$$chatId'] }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{ $sort: { _id: 1 } },
|
||||
{
|
||||
$project: {
|
||||
value: 1,
|
||||
userGoodFeedback: 1,
|
||||
userBadFeedback: 1,
|
||||
customFeedbacks: 1,
|
||||
adminFeedback: 1
|
||||
}
|
||||
}
|
||||
],
|
||||
as: 'chatitems'
|
||||
}
|
||||
},
|
||||
{
|
||||
$addFields: {
|
||||
messageCount: { $ifNull: [{ $arrayElemAt: ['$chatItemsData.messageCount', 0] }, 0] },
|
||||
@@ -206,7 +240,45 @@ async function handler(req: ApiRequestProps<ExportChatLogsBody, {}>, res: NextAp
|
||||
]
|
||||
},
|
||||
errorCount: { $ifNull: [{ $arrayElemAt: ['$chatItemsData.errorCount', 0] }, 0] },
|
||||
totalPoints: { $ifNull: [{ $arrayElemAt: ['$chatItemsData.totalPoints', 0] }, 0] }
|
||||
totalPoints: { $ifNull: [{ $arrayElemAt: ['$chatItemsData.totalPoints', 0] }, 0] },
|
||||
userGoodFeedbackItems: {
|
||||
$filter: {
|
||||
input: '$chatitems',
|
||||
as: 'item',
|
||||
cond: { $ifNull: ['$$item.userGoodFeedback', false] }
|
||||
}
|
||||
},
|
||||
userBadFeedbackItems: {
|
||||
$filter: {
|
||||
input: '$chatitems',
|
||||
as: 'item',
|
||||
cond: { $ifNull: ['$$item.userBadFeedback', false] }
|
||||
}
|
||||
},
|
||||
customFeedbackItems: {
|
||||
$filter: {
|
||||
input: '$chatitems',
|
||||
as: 'item',
|
||||
cond: { $gt: [{ $size: { $ifNull: ['$$item.customFeedbacks', []] } }, 0] }
|
||||
}
|
||||
},
|
||||
markItems: {
|
||||
$filter: {
|
||||
input: '$chatitems',
|
||||
as: 'item',
|
||||
cond: { $ifNull: ['$$item.adminFeedback', false] }
|
||||
}
|
||||
},
|
||||
chatDetails: {
|
||||
$map: {
|
||||
input: { $slice: ['$chatitems', -1000] },
|
||||
as: 'item',
|
||||
in: {
|
||||
id: '$$item._id',
|
||||
value: '$$item.value'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -227,11 +299,18 @@ async function handler(req: ApiRequestProps<ExportChatLogsBody, {}>, res: NextAp
|
||||
errorCount: 1,
|
||||
totalPoints: 1,
|
||||
outLinkUid: 1,
|
||||
tmbId: 1
|
||||
tmbId: 1,
|
||||
userGoodFeedbackItems: 1,
|
||||
userBadFeedbackItems: 1,
|
||||
customFeedbackItems: 1,
|
||||
markItems: 1,
|
||||
chatDetails: 1
|
||||
}
|
||||
}
|
||||
],
|
||||
{ ...readFromSecondary }
|
||||
{
|
||||
...readFromSecondary
|
||||
}
|
||||
).cursor({ batchSize: 1000 });
|
||||
|
||||
const write = responseWriteController({
|
||||
@@ -254,7 +333,7 @@ async function handler(req: ApiRequestProps<ExportChatLogsBody, {}>, res: NextAp
|
||||
? doc.outLinkUid
|
||||
: teamMemberWithContact.find((member) => String(member.memberId) === String(doc.tmbId))?.name;
|
||||
|
||||
const valueMap: Partial<Record<AppLogKeysEnum, () => any>> = {
|
||||
const valueMap: Record<string, () => any> = {
|
||||
[AppLogKeysEnum.SOURCE]: () => source,
|
||||
[AppLogKeysEnum.CREATED_TIME]: () => createdTime,
|
||||
[AppLogKeysEnum.LAST_CONVERSATION_TIME]: () => lastConversationTime,
|
||||
@@ -263,21 +342,40 @@ async function handler(req: ApiRequestProps<ExportChatLogsBody, {}>, res: NextAp
|
||||
[AppLogKeysEnum.SESSION_ID]: () => doc.id || '-',
|
||||
[AppLogKeysEnum.MESSAGE_COUNT]: () => doc.messageCount,
|
||||
[AppLogKeysEnum.FEEDBACK]: () => {
|
||||
const good = doc.userGoodFeedbackCount || 0;
|
||||
const bad = doc.userBadFeedbackCount || 0;
|
||||
return `good: ${good}, bad: ${bad}`;
|
||||
const goodItems = (doc.userGoodFeedbackItems || []).map((item: any) => ({
|
||||
chatItemId: item._id,
|
||||
feedback: item.userGoodFeedback
|
||||
}));
|
||||
const badItems = (doc.userBadFeedbackItems || []).map((item: any) => ({
|
||||
chatItemId: item._id,
|
||||
feedback: item.userBadFeedback
|
||||
}));
|
||||
return formatJsonString({ good: goodItems, bad: badItems });
|
||||
},
|
||||
[AppLogKeysEnum.CUSTOM_FEEDBACK]: () => {
|
||||
const customItems = (doc.customFeedbackItems || []).map((item: any) => ({
|
||||
chatItemId: item._id,
|
||||
feedbacks: item.customFeedbacks || []
|
||||
}));
|
||||
return formatJsonString(customItems);
|
||||
},
|
||||
[AppLogKeysEnum.ANNOTATED_COUNT]: () => {
|
||||
const markItems = (doc.markItems || []).map((item: any) => ({
|
||||
chatItemId: item._id,
|
||||
feedback: item.adminFeedback
|
||||
}));
|
||||
return formatJsonString(markItems);
|
||||
},
|
||||
[AppLogKeysEnum.CUSTOM_FEEDBACK]: () => doc.customFeedbacksCount || 0,
|
||||
[AppLogKeysEnum.ANNOTATED_COUNT]: () => doc.markCount || 0,
|
||||
[AppLogKeysEnum.RESPONSE_TIME]: () =>
|
||||
doc.averageResponseTime ? Number(doc.averageResponseTime).toFixed(2) : 0,
|
||||
[AppLogKeysEnum.ERROR_COUNT]: () => doc.errorCount || 0,
|
||||
[AppLogKeysEnum.POINTS]: () => (doc.totalPoints ? Number(doc.totalPoints).toFixed(2) : 0)
|
||||
[AppLogKeysEnum.POINTS]: () => (doc.totalPoints ? Number(doc.totalPoints).toFixed(2) : 0),
|
||||
chatDetails: () => formatJsonString(doc.chatDetails || [])
|
||||
};
|
||||
|
||||
const row = logKeys
|
||||
const row = [...logKeys, 'chatDetails']
|
||||
.map((key) => {
|
||||
const getter = valueMap[key as AppLogKeysEnum];
|
||||
const getter = valueMap[key];
|
||||
const val = getter ? getter() : '';
|
||||
return sanitizeCsvField(val ?? '');
|
||||
})
|
||||
|
Reference in New Issue
Block a user