mirror of
https://github.com/labring/FastGPT.git
synced 2026-05-16 01:09:01 +08:00
4.14.4 features (#6090)
* perf: zod with app log (#6083) * perf: safe decode * perf: zod with app log * fix: text * remove log * rename field * refactor: improve like/dislike interaction (#6080) * refactor: improve like/dislike interaction * button style & merge status * perf * fix * i18n * feedback ui * format * api optimize * openapi * read status --------- Co-authored-by: archer <545436317@qq.com> * perf: remove empty chat * perf: delete resource tip * fix: confirm * feedback filter * fix: ts * perf: linker scroll * perf: feedback ui * fix: plugin file input store * fix: max tokens * update comment * fix: condition value type * fix feedback (#6095) * fix feedback * text * list * fix: versionid --------- Co-authored-by: archer <545436317@qq.com> * fix: chat setting render;export logs filter * add test * perf: log list api * perf: redirect check * perf: log list * create ui * create ui --------- Co-authored-by: heheer <heheer@sealos.io>
This commit is contained in:
@@ -0,0 +1,248 @@
|
||||
import { z } from 'zod';
|
||||
import { PaginationSchema } from '../../../api';
|
||||
import { AppLogKeysEnum, AppLogTimespanEnum } from '../../../../core/app/logs/constants';
|
||||
import { ChatSourceEnum } from '../../../../core/chat/constants';
|
||||
import { AppLogKeysSchema } from '../../../../core/app/logs/type';
|
||||
import { SourceMemberSchema } from '../../../../support/user/type';
|
||||
|
||||
/* Log key mange */
|
||||
export const GetLogKeysQuerySchema = z.object({
|
||||
appId: z.string().meta({ example: '68ad85a7463006c963799a05', description: '应用 ID' })
|
||||
});
|
||||
export type getLogKeysQuery = z.infer<typeof GetLogKeysQuerySchema>;
|
||||
|
||||
export const GetLogKeysResponseSchema = z.object({
|
||||
logKeys: z
|
||||
.array(AppLogKeysSchema)
|
||||
.default([])
|
||||
.meta({ example: [AppLogKeysEnum.SOURCE, AppLogKeysEnum.CREATED_TIME], description: '日志键' })
|
||||
});
|
||||
export type getLogKeysResponseType = z.infer<typeof GetLogKeysResponseSchema>;
|
||||
|
||||
export const UpdateLogKeysBodySchema = z.object({
|
||||
appId: z.string().meta({ example: '68ad85a7463006c963799a05', description: '应用 ID' }),
|
||||
logKeys: z
|
||||
.array(AppLogKeysSchema)
|
||||
.meta({ example: [AppLogKeysEnum.SOURCE, AppLogKeysEnum.CREATED_TIME], description: '日志键' })
|
||||
});
|
||||
export type updateLogKeysBody = z.infer<typeof UpdateLogKeysBodySchema>;
|
||||
|
||||
// Chat Log Item Schema (based on AppChatLogSchema)
|
||||
export const ChatLogItemSchema = z.object({
|
||||
_id: z.string().meta({ example: '68ad85a7463006c963799a05', description: '对话日志 ID' }),
|
||||
chatId: z.string().meta({ example: 'chat123', description: '对话 ID' }),
|
||||
title: z.string().optional().meta({ example: '用户对话', description: '对话标题' }),
|
||||
customTitle: z.string().nullish().meta({ example: '自定义标题', description: '自定义对话标题' }),
|
||||
source: z.enum(ChatSourceEnum).meta({ example: ChatSourceEnum.api, description: '对话来源' }),
|
||||
sourceName: z.string().optional().meta({ example: 'API调用', description: '来源名称' }),
|
||||
updateTime: z.date().meta({ example: '2024-01-01T00:30:00.000Z', description: '更新时间' }),
|
||||
createTime: z.date().meta({ example: '2024-01-01T00:00:00.000Z', description: '创建时间' }),
|
||||
messageCount: z.int().nullish().meta({ example: 10, description: '消息数量' }),
|
||||
userGoodFeedbackCount: z.int().nullish().meta({ example: 3, description: '好评反馈数量' }),
|
||||
userBadFeedbackCount: z.int().nullish().meta({ example: 1, description: '差评反馈数量' }),
|
||||
customFeedbacksCount: z.int().nullish().meta({ example: 2, description: '自定义反馈数量' }),
|
||||
markCount: z.int().nullish().meta({ example: 0, description: '标记数量' }),
|
||||
averageResponseTime: z
|
||||
.number()
|
||||
.nullish()
|
||||
.meta({ example: 1500, description: '平均响应时间(毫秒)' }),
|
||||
errorCount: z.int().nullish().meta({ example: 0, description: '错误次数' }),
|
||||
totalPoints: z.number().nullish().meta({ example: 150.5, description: '总积分消耗' }),
|
||||
outLinkUid: z.string().nullish().meta({ example: 'outLink123', description: '外链用户 ID' }),
|
||||
tmbId: z.string().nullish().meta({ example: 'tmb123', description: '团队成员 ID' }),
|
||||
sourceMember: SourceMemberSchema.optional().meta({ description: '来源成员信息' }),
|
||||
versionName: z.string().nullish().meta({ example: 'v1.0.0', description: '版本名称' }),
|
||||
region: z.string().nullish().meta({ example: '中国', description: '区域' })
|
||||
});
|
||||
export type AppLogsListItemType = z.infer<typeof ChatLogItemSchema>;
|
||||
|
||||
/* Get chat logs */
|
||||
const FeedbackLogParamSchema = z.object({
|
||||
feedbackType: z.enum(['all', 'has_feedback', 'good', 'bad']).optional().meta({
|
||||
example: 'good',
|
||||
description: '反馈类型:all-全部记录,has_feedback-包含反馈,good-包含赞,bad-包含踩'
|
||||
}),
|
||||
unreadOnly: z.boolean().optional().meta({
|
||||
example: false,
|
||||
description: '是否仅显示未读反馈(当 feedbackType 为 all 时忽略)'
|
||||
})
|
||||
});
|
||||
// Get App Chat Logs Query Parameters (based on GetAppChatLogsProps)
|
||||
export const GetAppChatLogsBodySchema = PaginationSchema.extend(
|
||||
FeedbackLogParamSchema.shape
|
||||
).extend({
|
||||
appId: z.string().meta({
|
||||
example: '68ad85a7463006c963799a05',
|
||||
description: '应用 ID'
|
||||
}),
|
||||
dateStart: z.union([z.string(), z.date()]).meta({
|
||||
example: '2024-01-01T00:00:00.000Z',
|
||||
description: '开始时间'
|
||||
}),
|
||||
dateEnd: z.union([z.string(), z.date()]).meta({
|
||||
example: '2024-12-31T23:59:59.999Z',
|
||||
description: '结束时间'
|
||||
}),
|
||||
sources: z
|
||||
.array(z.nativeEnum(ChatSourceEnum))
|
||||
.optional()
|
||||
.meta({
|
||||
example: [ChatSourceEnum.api, ChatSourceEnum.online],
|
||||
description: '对话来源筛选'
|
||||
}),
|
||||
tmbIds: z
|
||||
.array(z.string())
|
||||
.optional()
|
||||
.meta({
|
||||
example: ['tmb123', 'tmb456'],
|
||||
description: '团队成员 ID 列表'
|
||||
}),
|
||||
chatSearch: z.string().optional().meta({
|
||||
example: 'hello',
|
||||
description: '对话内容搜索关键词'
|
||||
})
|
||||
});
|
||||
export type getAppChatLogsBody = z.infer<typeof GetAppChatLogsBodySchema>;
|
||||
// Get App Chat Logs Response
|
||||
export const GetAppChatLogsResponseSchema = z
|
||||
.object({
|
||||
total: z.number().meta({ example: 100, description: '总记录数' }),
|
||||
list: z.array(ChatLogItemSchema)
|
||||
})
|
||||
.meta({ example: { total: 100, list: [] }, description: '应用对话日志列表' });
|
||||
export type getAppChatLogsResponseType = z.infer<typeof GetAppChatLogsResponseSchema>;
|
||||
|
||||
/* Export chat log */
|
||||
export const ExportChatLogsBodySchema = GetAppChatLogsBodySchema.omit({
|
||||
pageSize: true,
|
||||
offset: true,
|
||||
pageNum: true
|
||||
}).safeExtend({
|
||||
title: z.string().meta({
|
||||
example: 'chat logs',
|
||||
description: '标题'
|
||||
}),
|
||||
sourcesMap: z.record(z.string(), z.object({ label: z.string() })).meta({
|
||||
example: { api: { label: 'API' }, online: { label: '在线' } },
|
||||
description: '来源映射'
|
||||
}),
|
||||
logKeys: z.array(z.enum(AppLogKeysEnum)).meta({
|
||||
example: [AppLogKeysEnum.SOURCE, AppLogKeysEnum.CREATED_TIME],
|
||||
description: '日志键'
|
||||
})
|
||||
});
|
||||
|
||||
/* Get chart data */
|
||||
// Get Chart Data Request Body (based on getChartDataBody)
|
||||
export const GetChartDataBodySchema = z.object({
|
||||
appId: z.string().meta({
|
||||
example: '68ad85a7463006c963799a05',
|
||||
description: '应用 ID'
|
||||
}),
|
||||
dateStart: z.date().meta({
|
||||
example: '2024-01-01T00:00:00.000Z',
|
||||
description: '开始日期'
|
||||
}),
|
||||
dateEnd: z.date().meta({
|
||||
example: '2024-12-31T23:59:59.999Z',
|
||||
description: '结束日期'
|
||||
}),
|
||||
source: z
|
||||
.array(z.nativeEnum(ChatSourceEnum))
|
||||
.optional()
|
||||
.meta({
|
||||
example: [ChatSourceEnum.api, ChatSourceEnum.online],
|
||||
description: '对话来源筛选'
|
||||
}),
|
||||
offset: z.number().meta({
|
||||
example: 1,
|
||||
description: '时区偏移量'
|
||||
}),
|
||||
userTimespan: z.nativeEnum(AppLogTimespanEnum).meta({
|
||||
example: AppLogTimespanEnum.day,
|
||||
description: '用户数据时间跨度'
|
||||
}),
|
||||
chatTimespan: z.nativeEnum(AppLogTimespanEnum).meta({
|
||||
example: AppLogTimespanEnum.day,
|
||||
description: '对话数据时间跨度'
|
||||
}),
|
||||
appTimespan: z.nativeEnum(AppLogTimespanEnum).meta({
|
||||
example: AppLogTimespanEnum.day,
|
||||
description: '应用数据时间跨度'
|
||||
})
|
||||
});
|
||||
export type getChartDataBody = z.infer<typeof GetChartDataBodySchema>;
|
||||
|
||||
// User Statistics Data Point (based on AppChatLogUserData)
|
||||
export const UserStatsDataPointSchema = z.object({
|
||||
timestamp: z.number().meta({ example: 1704067200, description: '时间戳' }),
|
||||
summary: z.object({
|
||||
userCount: z.number().meta({ example: 100, description: '用户总数' }),
|
||||
newUserCount: z.number().meta({ example: 30, description: '新用户数' }),
|
||||
retentionUserCount: z.number().meta({ example: 70, description: '留存用户数' }),
|
||||
points: z.number().meta({ example: 1500, description: '积分消耗' }),
|
||||
sourceCountMap: z.record(z.string(), z.number()).meta({
|
||||
example: { api: 50, web: 30, mobile: 20 },
|
||||
description: '各来源用户数量'
|
||||
})
|
||||
})
|
||||
});
|
||||
export type userStatsDataPoint = z.infer<typeof UserStatsDataPointSchema>;
|
||||
|
||||
// Chat Statistics Data Point (based on AppChatLogChatData)
|
||||
export const ChatStatsDataPointSchema = z.object({
|
||||
timestamp: z.number().meta({ example: 1704067200, description: '时间戳' }),
|
||||
summary: z.object({
|
||||
chatItemCount: z.number().meta({ example: 500, description: '对话项目总数' }),
|
||||
chatCount: z.number().meta({ example: 100, description: '对话会话总数' }),
|
||||
errorCount: z.number().meta({ example: 5, description: '错误次数' }),
|
||||
points: z.number().meta({ example: 800, description: '积分消耗' })
|
||||
})
|
||||
});
|
||||
export type chatStatsDataPoint = z.infer<typeof ChatStatsDataPointSchema>;
|
||||
|
||||
// App Statistics Data Point (based on AppChatLogAppData)
|
||||
export const AppStatsDataPointSchema = z.object({
|
||||
timestamp: z.number().meta({ example: 1704067200, description: '时间戳' }),
|
||||
summary: z.object({
|
||||
goodFeedBackCount: z.number().meta({ example: 25, description: '好评反馈数量' }),
|
||||
badFeedBackCount: z.number().meta({ example: 3, description: '差评反馈数量' }),
|
||||
chatCount: z.number().meta({ example: 100, description: '对话数量' }),
|
||||
totalResponseTime: z.number().meta({ example: 120000, description: '总响应时间(毫秒)' })
|
||||
})
|
||||
});
|
||||
export type appStatsDataPoint = z.infer<typeof AppStatsDataPointSchema>;
|
||||
|
||||
// Get Chart Data Response (based on getChartDataResponse)
|
||||
export const GetChartDataResponseSchema = z.object({
|
||||
userData: z.array(UserStatsDataPointSchema).meta({ description: '用户统计数据' }),
|
||||
chatData: z.array(ChatStatsDataPointSchema).meta({ description: '对话统计数据' }),
|
||||
appData: z.array(AppStatsDataPointSchema).meta({ description: '应用统计数据' })
|
||||
});
|
||||
export type getChartDataResponse = z.infer<typeof GetChartDataResponseSchema>;
|
||||
|
||||
// Get Total Data Query Parameters (based on getTotalDataQuery)
|
||||
export const GetTotalDataQuerySchema = z.object({
|
||||
appId: z.string().meta({
|
||||
example: '68ad85a7463006c963799a05',
|
||||
description: '应用 ID'
|
||||
})
|
||||
});
|
||||
export type getTotalDataQuery = z.infer<typeof GetTotalDataQuerySchema>;
|
||||
|
||||
// Get Total Data Response (based on getTotalDataResponse)
|
||||
export const GetTotalDataResponseSchema = z.object({
|
||||
totalUsers: z.number().meta({
|
||||
example: 1000,
|
||||
description: '总用户数'
|
||||
}),
|
||||
totalChats: z.number().meta({
|
||||
example: 5000,
|
||||
description: '总对话数'
|
||||
}),
|
||||
totalPoints: z.number().meta({
|
||||
example: 15000,
|
||||
description: '总积分消耗'
|
||||
})
|
||||
});
|
||||
export type getTotalDataResponse = z.infer<typeof GetTotalDataResponseSchema>;
|
||||
@@ -0,0 +1,154 @@
|
||||
import type { OpenAPIPath } from '../../../type';
|
||||
import { TagsMap } from '../../../tag';
|
||||
import { z } from 'zod';
|
||||
import {
|
||||
GetAppChatLogsBodySchema,
|
||||
GetAppChatLogsResponseSchema,
|
||||
ExportChatLogsBodySchema,
|
||||
GetChartDataBodySchema,
|
||||
GetChartDataResponseSchema,
|
||||
GetTotalDataQuerySchema,
|
||||
GetTotalDataResponseSchema,
|
||||
GetLogKeysQuerySchema,
|
||||
GetLogKeysResponseSchema,
|
||||
UpdateLogKeysBodySchema
|
||||
} from './api';
|
||||
|
||||
export const AppLogPath: OpenAPIPath = {
|
||||
'/core/app/logs/getLogKeys': {
|
||||
get: {
|
||||
summary: '获取应用日志键',
|
||||
description: '获取应用的日志键列表',
|
||||
tags: [TagsMap.appLog],
|
||||
requestParams: {
|
||||
query: GetLogKeysQuerySchema
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
description: '成功获取应用日志键',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: GetLogKeysResponseSchema
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'/core/app/logs/updateLogKeys': {
|
||||
post: {
|
||||
summary: '更新应用日志键',
|
||||
description: '更新应用的日志键列表',
|
||||
tags: [TagsMap.appLog],
|
||||
requestBody: {
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: UpdateLogKeysBodySchema
|
||||
}
|
||||
}
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
description: '成功更新应用日志键',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: z.object({})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'/core/app/logs/list': {
|
||||
post: {
|
||||
summary: '获取应用日志列表',
|
||||
description: '分页获取应用的对话日志列表,支持按时间范围、来源、用户等条件筛选',
|
||||
tags: [TagsMap.appLog],
|
||||
requestBody: {
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: GetAppChatLogsBodySchema
|
||||
}
|
||||
}
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
description: '成功获取应用日志列表',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: GetAppChatLogsResponseSchema
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'/core/app/logs/exportLogs': {
|
||||
post: {
|
||||
summary: '导出应用日志',
|
||||
description: '导出应用的对话日志为 CSV 文件,支持自定义导出字段和筛选条件',
|
||||
tags: [TagsMap.appLog],
|
||||
requestBody: {
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: ExportChatLogsBodySchema
|
||||
}
|
||||
}
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
description: '成功导出应用日志,返回 CSV 文件',
|
||||
content: {
|
||||
'text/csv': {
|
||||
schema: z.string()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'/proApi/core/app/logs/getTotalData': {
|
||||
get: {
|
||||
summary: '获取应用总体数据统计',
|
||||
description: '获取应用的总体数据统计,包括总用户数、总对话数、总积分消耗',
|
||||
tags: [TagsMap.appLog],
|
||||
requestParams: {
|
||||
query: GetTotalDataQuerySchema
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
description: '成功获取应用总体数据',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: GetTotalDataResponseSchema
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'/proApi/core/app/logs/getChartData': {
|
||||
post: {
|
||||
summary: '获取应用图表数据',
|
||||
description: '获取应用的图表统计数据,包括用户数据、对话数据、应用数据的时序统计',
|
||||
tags: [TagsMap.appLog],
|
||||
requestBody: {
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: GetChartDataBodySchema
|
||||
}
|
||||
}
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
description: '成功获取应用图表数据',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: GetChartDataResponseSchema
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user