数据包下载功能

This commit is contained in:
yuxin-pc 2025-06-09 10:38:58 +08:00
parent 8263f74a2c
commit 9910c1479a
13 changed files with 426 additions and 4 deletions

View File

@ -0,0 +1,37 @@
import axios from 'axios';
export default {
getPackageList(page, size) {
return axios.get('/api/data-package/list', {
params: {page, size}
});
},
getLogList(page, size) {
return axios.get('/api/data-package/log/list', {
params: {page, size}
});
},
downloadPackage(id) {
// 1. 先调用后端记录日志
axios.get(`/api/data-package/download/${id}`)
.then(response => {
// 2. 获取文件URL后直接触发下载
const fileUrl = response.data;
console.log("开始下载:", fileUrl);
// 方法1直接跳转最简单
window.location.href = fileUrl;
// 方法2创建隐藏的iframe避免页面跳转
// const iframe = document.createElement('iframe');
// iframe.style.display = 'none';
// iframe.src = fileUrl;
// document.body.appendChild(iframe);
})
.catch(error => {
this.$message.error("下载失败: " + error.message);
});
}
};

View File

@ -12,7 +12,8 @@ import settings from './modules/settings'
import wordBank from './modules/wordBank'
import sensitiveTarget from "./modules/targetSensitive"
import metaDict from "./modules/metaDict"
import apiRules from "@/js/modules/apiRules";
import apiRules from "@/js/modules/apiRules"
import DataPackage from "../modules/dataPackage/DataPackage";
Vue.use(Vuex)
@ -32,7 +33,8 @@ export default new Vuex.Store({
wordBank,
sensitiveTarget,
metaDict,
apiRules
apiRules,
DataPackage
},
strict: debug
})

View File

@ -6,6 +6,7 @@ import OverView from './modules/overview/Homepage'
import LogManage from './modules/log/LogList'
import ServerManage from './modules/server/ServerList'
import ProjectManage from './modules/project/ProjectList'
import DataPackage from "./modules/dataPackage/DataPackage";
// import TaskManage from './modules/task/MainFrame'
import CommonTaskTab from "@/modules/task/CommonTaskList";
import SingleWebsiteTaskTab from "@/modules/task/SingleWebsiteTaskList";
@ -66,7 +67,8 @@ const routes = [
// {path: 'settings', component: SystemSettings, meta: {keepAlive: true}},
{path: 'settings/metaData', component: MetaDataTab, meta: {keepAlive: true}},
{path: 'settings/metaDict', component: MetaDictTab, meta: {keepAlive: true}},
{path: 'settings/wordBank', component: WordBankTab, meta: {keepAlive: true}}
{path: 'settings/wordBank', component: WordBankTab, meta: {keepAlive: true}},
{path: 'data-package', component: DataPackage, meta: {keepAlive: true}}
]
},
{path: '/oscm/login', component: login}

View File

@ -54,6 +54,7 @@
<span slot="title">系统管理</span>
</template>
<el-menu-item index="/oscm/log">运行日志</el-menu-item>
<el-menu-item index="/oscm/data-package">数据包操作</el-menu-item>
<el-menu-item index="/oscm/settings/wordBank">词库管理</el-menu-item>
<el-menu-item index="/oscm/settings/metaData">元数据表格</el-menu-item>
<el-menu-item index="/oscm/settings/metaDict">元数据字典</el-menu-item>

View File

@ -0,0 +1,180 @@
<!-- src/views/DataPackage.vue -->
<template>
<div>
<el-tabs v-model="activeTab">
<el-tab-pane label="下载列表" name="package">
<el-table :data="packageList" style="width: 100%">
<el-table-column prop="packageName" label="数据包名称"></el-table-column>
<el-table-column prop="createTime" label="上传时间" :formatter="formatDate"></el-table-column>
<el-table-column prop="packageSize" label="大小" :formatter="formatSize"></el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button @click="downloadPackage(scope.row.id)" type="primary" size="small">下载</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
@current-change="handlePackagePageChange"
:current-page="packagePage.current"
:page-size="packagePage.size"
:total="packagePage.total"
layout="total, prev, pager, next">
</el-pagination>
</el-tab-pane>
<el-tab-pane label="日志列表" name="log">
<el-table :data="logList" style="width: 100%">
<el-table-column prop="packageName" label="数据包名称"></el-table-column>
<el-table-column prop="operationType" label="操作类型"></el-table-column>
<el-table-column prop="operationTime" label="操作时间" :formatter="formatDate"></el-table-column>
<el-table-column prop="userName" label="用户名"></el-table-column>
<el-table-column prop="userIp" label="IP"></el-table-column>
</el-table>
<el-pagination
@current-change="handleLogPageChange"
:current-page="logPage.current"
:page-size="logPage.size"
:total="logPage.total"
layout="total, prev, pager, next">
</el-pagination>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import Vue from 'vue'
import {
Button,
ButtonGroup,
Cascader,
Col,
Dialog,
Divider,
Dropdown,
DropdownItem,
DropdownMenu,
Form,
FormItem,
Input,
Option,
RadioGroup,
Row,
Select,
TabPane,
Tabs,
Tag,
Tooltip
} from 'element-ui'
import dataPackageApi from '@/js/api/dataPackageApi';
Vue.use(Select)
Vue.use(Option)
Vue.use(Input)
Vue.use(Row)
Vue.use(Col)
Vue.use(Button)
Vue.use(Dialog)
Vue.use(Form)
Vue.use(FormItem)
Vue.use(Divider)
Vue.use(Cascader)
Vue.use(Dropdown)
Vue.use(DropdownMenu)
Vue.use(DropdownItem)
Vue.use(Tabs)
Vue.use(TabPane)
Vue.use(RadioGroup)
Vue.use(Tag)
Vue.use(ButtonGroup)
Vue.use(Tooltip)
export default {
data() {
return {
activeTab: 'package',
packageList: [],
logList: [],
packagePage: {
current: 1,
size: 10,
total: 0
},
logPage: {
current: 1,
size: 10,
total: 0
}
};
},
created() {
this.fetchPackageList();
this.fetchLogList();
},
methods: {
fetchPackageList() {
dataPackageApi.getPackageList(this.packagePage.current - 1, this.packagePage.size)
.then(response => {
this.packageList = response.data.content;
this.packagePage.total = response.data.totalElements;
});
},
fetchLogList() {
dataPackageApi.getLogList(this.logPage.current - 1, this.logPage.size)
.then(response => {
this.logList = response.data.content;
this.logPage.total = response.data.totalElements;
});
},
handlePackagePageChange(current) {
this.packagePage.current = current;
this.fetchPackageList();
},
handleLogPageChange(current) {
this.logPage.current = current;
this.fetchLogList();
},
downloadPackage(id) {
dataPackageApi.downloadPackage(id)
.then(response => {
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
//
const contentDisposition = response.headers['content-disposition'];
let fileName = 'package.dat';
if (contentDisposition) {
const fileNameMatch = contentDisposition.match(/filename="(.+?)"/);
if (fileNameMatch && fileNameMatch.length === 2) {
fileName = fileNameMatch[1];
}
}
link.setAttribute('download', fileName);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});
},
formatDate(row, column, cellValue) {
if (!cellValue) return '';
const date = new Date(cellValue);
return date.toLocaleString();
},
formatSize(row, column, cellValue) {
if (!cellValue) return '0 B';
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
let size = Number(cellValue);
let unitIndex = 0;
while (size >= 1024 && unitIndex < units.length - 1) {
size /= 1024;
unitIndex++;
}
return `${size.toFixed(2)} ${units[unitIndex]}`;
}
}
};
</script>

View File

@ -69,7 +69,7 @@
</template>
<script>
import Vue from 'vue'
import {Row, Col, Table, TableColumn, Button, Select, Option, DatePicker, Message, Dialog} from 'element-ui'
import {Row, Col, Table, TableColumn, Button, Select, Option, DatePicker, Message, Dialog, Pagination} from 'element-ui'
import logApi from '../../js/api/logApi'
import projectApi from '../../js/api/projectApi'
import mytable from '../components/CustomTable'
@ -84,6 +84,7 @@ Vue.use(Select)
Vue.use(Option)
Vue.use(DatePicker)
Vue.use(Dialog)
Vue.use(Pagination)
export default {
name: 'LogManage',
props: ['taskid'],

View File

@ -72,6 +72,10 @@
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
</dependencies>
<!-- <repositories>-->
<!-- <repository>-->

View File

@ -0,0 +1,66 @@
package com.jsc.oscm.controller;
import com.jsc.oscm.entity.DataPackage;
import com.jsc.oscm.entity.DataPackageLog;
import com.jsc.oscm.service.DataPackageService;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
@RestController
@RequestMapping("/data-package")
public class DataPackageController {
@javax.annotation.Resource
private DataPackageService dataPackageService;
@GetMapping("/list")
public ResponseEntity<Page<DataPackage>> getPackageList(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
Pageable pageable = PageRequest.of(page, size);
return ResponseEntity.ok(dataPackageService.getPackageList(pageable));
}
@GetMapping("/log/list")
public ResponseEntity<Page<DataPackageLog>> getLogList(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
Pageable pageable = PageRequest.of(page, size);
return ResponseEntity.ok(dataPackageService.getLogList(pageable));
}
@PostMapping
public ResponseEntity<Void> addPackage(@RequestBody DataPackage dataPackage) {
dataPackageService.addPackage(dataPackage);
return ResponseEntity.ok().build();
}
@GetMapping("/download/{id}")
public ResponseEntity<String> getDownloadUrl(@PathVariable Integer id, HttpServletRequest request) {
DataPackage dataPackage = dataPackageService.getPackageById(id);
if (dataPackage == null) {
return ResponseEntity.notFound().build();
}
// 记录下载日志关键操作
dataPackageService.addLog(dataPackage.getPackageName(), "下载", request);
// 直接返回Nginx文件URL
return ResponseEntity.ok(dataPackage.getDownloadUrl());
}
}

View File

@ -0,0 +1,7 @@
package com.jsc.oscm.dao;
import com.jsc.oscm.entity.DataPackageLog;
import org.springframework.data.jpa.repository.JpaRepository;
public interface DataPackageLogRepository extends JpaRepository<DataPackageLog, Integer> {
}

View File

@ -0,0 +1,7 @@
package com.jsc.oscm.dao;
import com.jsc.oscm.entity.DataPackage;
import org.springframework.data.jpa.repository.JpaRepository;
public interface DataPackageRepository extends JpaRepository<DataPackage, Integer> {
}

View File

@ -0,0 +1,28 @@
package com.jsc.oscm.entity;
import lombok.Data;
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
@Data
@Table(name = "data_package")
public class DataPackage {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "package_name")
private String packageName;
@Column(name = "package_size")
private Long packageSize;
@Column(name = "download_url")
private String downloadUrl;
@Column(name = "create_time")
private LocalDateTime createTime;
}

View File

@ -0,0 +1,34 @@
package com.jsc.oscm.entity;
import lombok.Data;
import javax.persistence.*;
import java.time.LocalDateTime;
@Data
@Entity
@Table(name = "data_package_log")
public class DataPackageLog {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "package_name")
private String packageName;
@Column(name = "operation_time")
private LocalDateTime operationTime;
@Column(name = "operation_type")
private String operationType;
@Column(name = "user_name")
private String userName;
@Column(name = "user_ip")
private String userIp;
@Column(name = "user_agent")
private String userAgent;
}

View File

@ -0,0 +1,53 @@
package com.jsc.oscm.service;
import com.jsc.oscm.dao.DataPackageLogRepository;
import com.jsc.oscm.dao.DataPackageRepository;
import com.jsc.oscm.entity.DataPackage;
import com.jsc.oscm.entity.DataPackageLog;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
@Service
public class DataPackageService {
@Resource
private DataPackageRepository dataPackageRepository;
@Resource
private DataPackageLogRepository dataPackageLogRepository;
public Page<DataPackage> getPackageList(Pageable pageable) {
return dataPackageRepository.findAll(pageable);
}
public Page<DataPackageLog> getLogList(Pageable pageable) {
return dataPackageLogRepository.findAll(pageable);
}
public void addPackage(DataPackage dataPackage) {
dataPackage.setCreateTime(LocalDateTime.now());
dataPackageRepository.save(dataPackage);
}
public void addLog(String packageName, String operationType, HttpServletRequest request) {
DataPackageLog log = new DataPackageLog();
log.setPackageName(packageName);
log.setOperationType(operationType);
log.setOperationTime(LocalDateTime.now());
log.setUserIp(request.getRemoteAddr());
log.setUserAgent(request.getHeader("User-Agent"));
// 可以从SecurityContext中获取当前用户
// Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
// log.setUserName(authentication.getName());
dataPackageLogRepository.save(log);
}
public DataPackage getPackageById(Integer id) {
return dataPackageRepository.findById(id).orElse(null);
}
}