mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-26 01:33:05 +00:00
feat: 对话日志支持自定义时间
This commit is contained in:
parent
451be0c81c
commit
3b995d34eb
|
|
@ -97,7 +97,8 @@ class ChatSerializers(serializers.Serializer):
|
|||
|
||||
class Query(serializers.Serializer):
|
||||
abstract = serializers.CharField(required=False, error_messages=ErrMessage.char("摘要"))
|
||||
history_day = serializers.IntegerField(required=True, error_messages=ErrMessage.integer("历史天数"))
|
||||
start_time = serializers.DateField(format='%Y-%m-%d', error_messages=ErrMessage.date("开始时间"))
|
||||
end_time = serializers.DateField(format='%Y-%m-%d', error_messages=ErrMessage.date("结束时间"))
|
||||
user_id = serializers.UUIDField(required=True, error_messages=ErrMessage.uuid("用户id"))
|
||||
application_id = serializers.UUIDField(required=True, error_messages=ErrMessage.uuid("应用id"))
|
||||
min_star = serializers.IntegerField(required=False, min_value=0,
|
||||
|
|
@ -110,23 +111,34 @@ class ChatSerializers(serializers.Serializer):
|
|||
])
|
||||
|
||||
def get_end_time(self):
|
||||
history_day = self.data.get('history_day')
|
||||
return datetime.datetime.now() - datetime.timedelta(days=history_day)
|
||||
return datetime.datetime.combine(
|
||||
datetime.datetime.strptime(self.data.get('end_time'), '%Y-%m-%d'),
|
||||
datetime.datetime.max.time())
|
||||
|
||||
def get_query_set(self):
|
||||
def get_start_time(self):
|
||||
return self.data.get('start_time')
|
||||
|
||||
def get_query_set(self, select_ids=None):
|
||||
end_time = self.get_end_time()
|
||||
start_time = self.get_start_time()
|
||||
query_set = QuerySet(model=get_dynamics_model(
|
||||
{'application_chat.application_id': models.CharField(),
|
||||
'application_chat.abstract': models.CharField(),
|
||||
"star_num": models.IntegerField(),
|
||||
'trample_num': models.IntegerField(),
|
||||
'comparer': models.CharField(),
|
||||
'application_chat.create_time': models.DateTimeField()}))
|
||||
'application_chat.create_time': models.DateTimeField(),
|
||||
'application_chat.id': models.UUIDField(), }))
|
||||
|
||||
base_query_dict = {'application_chat.application_id': self.data.get("application_id"),
|
||||
'application_chat.create_time__gte': end_time}
|
||||
'application_chat.create_time__gte': start_time,
|
||||
'application_chat.create_time__lte': end_time,
|
||||
}
|
||||
if 'abstract' in self.data and self.data.get('abstract') is not None:
|
||||
base_query_dict['application_chat.abstract__icontains'] = self.data.get('abstract')
|
||||
|
||||
if select_ids is not None and len(select_ids) > 0:
|
||||
base_query_dict['application_chat.id__in'] = select_ids
|
||||
base_condition = Q(**base_query_dict)
|
||||
min_star_query = None
|
||||
min_trample_query = None
|
||||
|
|
@ -176,11 +188,11 @@ class ChatSerializers(serializers.Serializer):
|
|||
row.get('message_tokens') + row.get('answer_tokens'), row.get('run_time'),
|
||||
str(row.get('create_time'))]
|
||||
|
||||
def export(self, with_valid=True):
|
||||
def export(self, data, with_valid=True):
|
||||
if with_valid:
|
||||
self.is_valid(raise_exception=True)
|
||||
|
||||
data_list = native_search(self.get_query_set(),
|
||||
data_list = native_search(self.get_query_set(data.get('select_ids')),
|
||||
select_string=get_file_content(
|
||||
os.path.join(PROJECT_DIR, "apps", "application", 'sql',
|
||||
'export_application_chat.sql')),
|
||||
|
|
|
|||
|
|
@ -54,10 +54,10 @@ class ChatView(APIView):
|
|||
[lambda r, keywords: Permission(group=Group.APPLICATION, operate=Operate.USE,
|
||||
dynamic_tag=keywords.get('application_id'))])
|
||||
)
|
||||
def get(self, request: Request, application_id: str):
|
||||
def post(self, request: Request, application_id: str):
|
||||
return ChatSerializers.Query(
|
||||
data={**query_params_to_single_dict(request.query_params), 'application_id': application_id,
|
||||
'user_id': request.user.id}).export()
|
||||
'user_id': request.user.id}).export(request.data)
|
||||
|
||||
class Open(APIView):
|
||||
authentication_classes = [TokenAuth]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Result } from '@/request/Result'
|
||||
import { get, del, put, exportExcel } from '@/request/index'
|
||||
import { get, del, put, exportExcel, exportExcelPost } from '@/request/index'
|
||||
import type { pageRequest } from '@/api/type/common'
|
||||
import { type Ref } from 'vue'
|
||||
|
||||
|
|
@ -34,9 +34,10 @@ const exportChatLog: (
|
|||
application_id: string,
|
||||
application_name: string,
|
||||
param: any,
|
||||
data: any,
|
||||
loading?: Ref<boolean>
|
||||
) => void = (application_id, application_name, param, loading) => {
|
||||
exportExcel(application_name, `${prefix}/${application_id}/chat/export`, param, loading)
|
||||
) => void = (application_id, application_name, param, data, loading) => {
|
||||
exportExcelPost(application_name, `${prefix}/${application_id}/chat/export`, param, data, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -240,6 +240,45 @@ export const exportExcel: (
|
|||
.catch((e) => {})
|
||||
}
|
||||
|
||||
export const exportExcelPost: (
|
||||
fileName: string,
|
||||
url: string,
|
||||
params: any,
|
||||
data: any,
|
||||
loading?: NProgress | Ref<boolean>
|
||||
) => Promise<any> = (
|
||||
fileName: string,
|
||||
url: string,
|
||||
params: any,
|
||||
data: any,
|
||||
loading?: NProgress | Ref<boolean>
|
||||
) => {
|
||||
return promise(
|
||||
request({
|
||||
url: url,
|
||||
method: 'post',
|
||||
params, // 查询字符串参数
|
||||
data, // 请求体数据
|
||||
responseType: 'blob'
|
||||
}),
|
||||
loading
|
||||
)
|
||||
.then((res: any) => {
|
||||
if (res) {
|
||||
const blob = new Blob([res], {
|
||||
type: 'application/vnd.ms-excel'
|
||||
})
|
||||
const link = document.createElement('a')
|
||||
link.href = window.URL.createObjectURL(blob)
|
||||
link.download = fileName
|
||||
link.click()
|
||||
// 释放内存
|
||||
window.URL.revokeObjectURL(link.href)
|
||||
}
|
||||
return true
|
||||
})
|
||||
.catch((e) => {})
|
||||
}
|
||||
|
||||
export const download: (
|
||||
url: string,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<LayoutContainer header="对话日志">
|
||||
<div class="p-24">
|
||||
<div class="mb-16">
|
||||
<el-select v-model="history_day" class="mr-12 w-240" @change="changeHandle">
|
||||
<el-select v-model="history_day" class="mr-12 w-120" @change="changeDayHandle">
|
||||
<el-option
|
||||
v-for="item in dayOptions"
|
||||
:key="item.value"
|
||||
|
|
@ -10,12 +10,23 @@
|
|||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-date-picker
|
||||
v-if="history_day === 'other'"
|
||||
v-model="daterangeValue"
|
||||
type="daterange"
|
||||
:start-placeholder="$t('views.applicationOverview.monitor.startDatePlaceholder')"
|
||||
:end-placeholder="$t('views.applicationOverview.monitor.endDatePlaceholder')"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
@change="changeDayRangeHandle"
|
||||
/>
|
||||
<el-input
|
||||
v-model="search"
|
||||
@change="getList"
|
||||
placeholder="搜索"
|
||||
prefix-icon="Search"
|
||||
class="w-240"
|
||||
style="margin-left: 10px"
|
||||
clearable
|
||||
/>
|
||||
<el-button class="float-right" @click="exportLog">导出</el-button>
|
||||
|
|
@ -29,8 +40,10 @@
|
|||
@row-click="rowClickHandle"
|
||||
v-loading="loading"
|
||||
:row-class-name="setRowClass"
|
||||
@selection-change="handleSelectionChange"
|
||||
class="log-table"
|
||||
>
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column prop="abstract" label="摘要" show-overflow-tooltip />
|
||||
<el-table-column prop="chat_record_count" label="对话提问数" align="right" />
|
||||
<el-table-column prop="star_num" align="right">
|
||||
|
|
@ -45,7 +58,9 @@
|
|||
link
|
||||
@click="popoverVisible = !popoverVisible"
|
||||
>
|
||||
<el-icon><Filter /></el-icon>
|
||||
<el-icon>
|
||||
<Filter />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</template>
|
||||
<div class="filter">
|
||||
|
|
@ -139,9 +154,11 @@ import { cloneDeep } from 'lodash'
|
|||
import ChatRecordDrawer from './component/ChatRecordDrawer.vue'
|
||||
import { MsgSuccess, MsgConfirm } from '@/utils/message'
|
||||
import logApi from '@/api/log'
|
||||
import { datetimeFormat } from '@/utils/time'
|
||||
import { beforeDay, datetimeFormat, nowDate } from '@/utils/time'
|
||||
import useStore from '@/stores'
|
||||
import type { Dict } from '@/api/type/common'
|
||||
import { t } from '@/locales'
|
||||
|
||||
const { application, log } = useStore()
|
||||
const route = useRoute()
|
||||
const {
|
||||
|
|
@ -151,21 +168,33 @@ const {
|
|||
const dayOptions = [
|
||||
{
|
||||
value: 7,
|
||||
label: '过去7天'
|
||||
// @ts-ignore
|
||||
label: t('views.applicationOverview.monitor.pastDayOptions.past7Days') // 使用 t 方法来国际化显示文本
|
||||
},
|
||||
{
|
||||
value: 30,
|
||||
label: '过去30天'
|
||||
label: t('views.applicationOverview.monitor.pastDayOptions.past30Days')
|
||||
},
|
||||
{
|
||||
value: 90,
|
||||
label: '过去90天'
|
||||
label: t('views.applicationOverview.monitor.pastDayOptions.past90Days')
|
||||
},
|
||||
{
|
||||
value: 183,
|
||||
label: '过去半年'
|
||||
label: t('views.applicationOverview.monitor.pastDayOptions.past183Days')
|
||||
},
|
||||
{
|
||||
value: 'other',
|
||||
label: t('views.applicationOverview.monitor.pastDayOptions.other')
|
||||
}
|
||||
]
|
||||
const daterangeValue = ref('')
|
||||
// 提交日期时间
|
||||
const daterange = ref({
|
||||
start_time: '',
|
||||
end_time: ''
|
||||
})
|
||||
const multipleSelection = ref<any[]>([])
|
||||
|
||||
const ChatRecordRef = ref()
|
||||
const loading = ref(false)
|
||||
|
|
@ -182,7 +211,8 @@ const tableIndexMap = computed<Dict<number>>(() => {
|
|||
}))
|
||||
.reduce((pre, next) => ({ ...pre, ...next }), {})
|
||||
})
|
||||
const history_day = ref(7)
|
||||
const history_day = ref<number | string>(7)
|
||||
|
||||
const search = ref('')
|
||||
const detail = ref<any>(null)
|
||||
|
||||
|
|
@ -279,6 +309,10 @@ const setRowClass = ({ row }: any) => {
|
|||
return currentChatId.value === row?.id ? 'highlight' : ''
|
||||
}
|
||||
|
||||
const handleSelectionChange = (val: any[]) => {
|
||||
multipleSelection.value = val
|
||||
}
|
||||
|
||||
function deleteLog(row: any) {
|
||||
MsgConfirm(`是否删除对话:${row.abstract} ?`, `删除后无法恢复,请谨慎操作。`, {
|
||||
confirmButtonText: '删除',
|
||||
|
|
@ -299,15 +333,11 @@ function handleSizeChange() {
|
|||
getList()
|
||||
}
|
||||
|
||||
function changeHandle(val: number) {
|
||||
history_day.value = val
|
||||
paginationConfig.current_page = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
function getList() {
|
||||
paginationConfig.current_page = 1
|
||||
let obj: any = {
|
||||
history_day: history_day.value,
|
||||
start_time: daterange.value.start_time,
|
||||
end_time: daterange.value.end_time,
|
||||
...filter.value
|
||||
}
|
||||
if (search.value) {
|
||||
|
|
@ -329,23 +359,46 @@ function getDetail() {
|
|||
}
|
||||
|
||||
const exportLog = () => {
|
||||
const arr: string[] = []
|
||||
multipleSelection.value.map((v) => {
|
||||
if (v) {
|
||||
arr.push(v.id)
|
||||
}
|
||||
})
|
||||
if (detail.value) {
|
||||
let obj: any = {
|
||||
history_day: history_day.value,
|
||||
start_time: daterange.value.start_time,
|
||||
end_time: daterange.value.end_time,
|
||||
...filter.value
|
||||
}
|
||||
if (search.value) {
|
||||
obj = { ...obj, abstract: search.value }
|
||||
}
|
||||
logApi.exportChatLog(detail.value.id, detail.value.name, obj, loading)
|
||||
|
||||
logApi.exportChatLog(detail.value.id, detail.value.name, obj, { select_ids: arr }, loading)
|
||||
}
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
getList()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
function changeDayRangeHandle(val: string) {
|
||||
daterange.value.start_time = val[0]
|
||||
daterange.value.end_time = val[1]
|
||||
getList()
|
||||
}
|
||||
|
||||
function changeDayHandle(val: number | string) {
|
||||
if (val !== 'other') {
|
||||
daterange.value.start_time = beforeDay(val)
|
||||
daterange.value.end_time = nowDate
|
||||
getList()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
changeDayHandle(history_day.value)
|
||||
getDetail()
|
||||
})
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Reference in New Issue