AI修复建议接口对接

This commit is contained in:
付民康
2025-03-26 20:09:09 +08:00
parent eb55e0171f
commit 94ec137e24
4 changed files with 189 additions and 15 deletions

Binary file not shown.

View File

@@ -1,6 +1,7 @@
import { reqCatchV2, type catchRt } from '@/utils/catch'; import { reqCatchV2, type catchRt } from '@/utils/catch';
import request from '@/utils/request'; import request from '@/utils/request';
import * as types from '@/api/repo/types'; import * as types from '@/api/repo/types';
import axios from 'axios';
// 详情-获取版本信息 // 详情-获取版本信息
export function releaseInfo(params: { softwareId: string }): Promise<any> { export function releaseInfo(params: { softwareId: string }): Promise<any> {
@@ -263,3 +264,16 @@ export const getReleaseExport = (data): Promise<any> => {
}) })
); );
} }
// 获取AI修复建议
export function getVulSuggestion(data) {
return axios.request({
url: '/gateway/detect' + `/ai/vul_suggestion`,
method: 'POST',
data,
headers: {
Authorization: 'Bearer ' + localStorage.getItem('access_token'),
},
});
}

View File

@@ -2,6 +2,7 @@
<d-table :data="dataSource" @sort-change="handleSortChange" :header-bg="true"> <d-table :data="dataSource" @sort-change="handleSortChange" :header-bg="true">
<d-column <d-column
v-for="(column, index) in props.tableConfig" v-for="(column, index) in props.tableConfig"
:key="index"
:field="column.field" :field="column.field"
:header="column.header" :header="column.header"
:sortable="column.sortable" :sortable="column.sortable"
@@ -11,11 +12,66 @@
{{ column.format ? column.format(scope.row[column.field]) : scope.row[column.field] }} {{ column.format ? column.format(scope.row[column.field]) : scope.row[column.field] }}
</template> </template>
</d-column> </d-column>
<d-column header="操作">
<template #default="{ row }">
<d-button
size="sm"
variant="text"
color="primary"
class="p-0 justify-start"
@click="openModal(row)"
>
AI修复建议
</d-button>
</template>
</d-column>
</d-table> </d-table>
<d-modal v-model="showModal" title="AI修复建议" class="w-[600px]">
<Card v-loading="loading" simple class="jyh-card mb-4">
<d-row class="mb-3">
<d-col :span="24">
<span class="card-key width-90">漏洞特性</span>
<span class="card-val">{{ vulSuggestion.vulFeature || '--' }}</span>
</d-col>
</d-row>
<d-row class="mb-3">
<d-col :span="24">
<span class="card-key width-90">漏洞描述</span>
<span class="card-val">{{ vulSuggestion.vulDescription || '--' }}</span>
</d-col>
</d-row>
<d-row class="mb-3">
<d-col :span="24">
<span class="card-key width-90">升级版本</span>
<span class="card-val">{{ vulSuggestion.upgradeVersion || '--' }}</span>
</d-col>
</d-row>
<d-row class="mb-3">
<d-col :span="24">
<div class="flex items-center justify-start w-full gap-1">
<img src="@/assets/imgs/jyh/aiIcon.png" />
<span class="text-base font-medium">AI修复建议:</span>
</div>
<div>
<ul v-if="vulSuggestion.fixSuggestionList && vulSuggestion.fixSuggestionList.length">
<li v-for="(info, index) in vulSuggestion.fixSuggestionList" :key="index">{{ info }}</li>
</ul>
<span v-else>--</span>
</div>
</d-col>
</d-row>
</Card>
<div class="flex justify-center">
<d-button variant="solid" class="mr-[8px]" @click="confirmAction">确定</d-button>
<d-button @click="closeModal">取消</d-button>
</div>
</d-modal>
</template> </template>
<script> <script>
import { defineComponent, ref, onMounted } from 'vue'; import { defineComponent, ref } from 'vue';
import { getVulSuggestion } from '@/api/jyh';
export default defineComponent({ export default defineComponent({
props: { props: {
@@ -26,27 +82,131 @@ export default defineComponent({
dataSource: { dataSource: {
type: Array, type: Array,
default: () => [] default: () => []
},
currentVersion: {
type: String,
default: ''
},
libraryName: {
type: String,
default: ''
} }
}, },
setup(props) { setup(props, { emit }) {
onMounted(() => { const showModal = ref(false);
console.log('props', props); const selectRow = ref({});
const vulSuggestion = ref({
vulFeature: '',
vulDescription: '',
upgradeVersion: '',
fixSuggestionList: []
}); });
const loading = ref(false);
const fetchVulSuggestion = async (row) => {
try {
loading.value = true;
const { data } = await getVulSuggestion({
name: props.libraryName,
version: props.currentVersion,
cve: row.vulnId
});
if (data.code === 200) {
vulSuggestion.value = {
vulFeature: data.data?.vulFeature || '',
vulDescription: data.data?.vulDescription || '',
upgradeVersion: data.data?.upgradeVersion || '',
fixSuggestionList: data.data?.fixSuggestionList || []
};
} else {
vulSuggestion.value = {
vulFeature: '',
vulDescription: '',
upgradeVersion: '',
fixSuggestionList: []
};
}
} catch (error) {
console.error('获取漏洞建议失败:', error);
vulSuggestion.value = {
vulFeature: '',
vulDescription: '',
upgradeVersion: '',
fixSuggestionList: []
};
} finally {
loading.value = false;
}
};
const handleSortChange = ({ field, direction }) => { const handleSortChange = ({ field, direction }) => {
console.log('field', field); emit('sort-change', { field, direction });
console.log('direction', direction);
};
const sortDateMethod = (a, b) => {
return a.date > b.date;
};
const sortNameMethod = (a, b) => {
return a.lastName > b.lastName;
}; };
return { props, handleSortChange, sortDateMethod, sortNameMethod }; const openModal = async (row) => {
selectRow.value = row;
showModal.value = true;
await fetchVulSuggestion(row);
};
const closeModal = () => {
showModal.value = false;
vulSuggestion.value = {
vulFeature: '',
vulDescription: '',
upgradeVersion: '',
fixSuggestionList: []
};
};
const confirmAction = () => {
emit('confirm-action', selectRow.value);
closeModal();
};
return {
props,
showModal,
selectRow,
vulSuggestion,
loading,
handleSortChange,
openModal,
closeModal,
confirmAction
};
} }
}); });
</script> </script>
<style scoped lang="scss"></style> <style scoped lang="scss">
.card-key {
display: inline-block;
color: #666;
margin-right: 8px;
}
.card-val {
color: #333;
}
.width-90 {
width: 90px;
display: inline-block;
}
.jyh-card {
margin-bottom: 16px;
}
ul {
list-style-type: disc;
padding-left: 20px;
margin-top: 8px;
li {
margin-bottom: 4px;
}
}
</style>

View File

@@ -61,7 +61,7 @@
<template #body> <template #body>
<div style="padding: 8px 20px 20px 20px; width: 100%"> <div style="padding: 8px 20px 20px 20px; width: 100%">
<Table class="jyh-table" :tableConfig="tableConfig" :dataSource="rowItem.vulnerabilities" /> <Table class="jyh-table" :tableConfig="tableConfig" :dataSource="rowItem.vulnerabilities" :libraryName="rowItem.libraryName" :currentVersion="rowItem.currentVersion"/>
<div class="w-full flex items-center justify-end"> <div class="w-full flex items-center justify-end">
<span>{{ rowItem.vulnerabilities.length }}</span> <span>{{ rowItem.vulnerabilities.length }}</span>
</div> </div>