Merge remote-tracking branch 'origin/master'

master
xiong_cl 7 months ago
commit e6a5ed9b4f
  1. 54
      shandan-basedata/src/main/java/com/keyware/shandan/dynacmicform/core/db/parser/TableInfoParser.java
  2. 30
      shandan-bianmu/src/main/java/com/keyware/shandan/bianmu/controller/MetadataController.java
  3. 65
      shandan-bianmu/src/main/java/com/keyware/shandan/bianmu/controller/SearchController.java
  4. 30
      shandan-bianmu/src/main/resources/static/js/business/directory/directory.js
  5. 7
      shandan-bianmu/src/main/resources/view/business/directory/directory.html
  6. 35
      shandan-browser/src/main/java/com/keyware/shandan/browser/controller/SearchController.java
  7. 39
      shandan-browser/src/main/java/com/keyware/shandan/browser/service/FileSearchService.java
  8. 10
      shandan-browser/src/main/java/com/keyware/shandan/browser/service/MetadataSearchService.java
  9. 26
      shandan-browser/src/main/resources/static/js/browser.js
  10. 10
      shandan-common/src/main/java/com/keyware/shandan/common/service/ExportServiceImpl.java
  11. 9
      shandan-common/src/main/java/com/keyware/shandan/common/service/IExportService.java
  12. 50
      shandan-common/src/main/resources/static/js/common/common.js
  13. 62
      shandan-system/src/main/java/com/keyware/shandan/bianmu/controller/MetadataCommonController.java
  14. 191
      shandan-system/src/main/java/com/keyware/shandan/bianmu/export/FileSearchExport.java
  15. 14
      shandan-system/src/main/java/com/keyware/shandan/bianmu/mapper/DirectoryResourceMapper.java
  16. 1
      shandan-system/src/main/java/com/keyware/shandan/bianmu/service/DataLabelsService.java
  17. 2
      shandan-system/src/main/java/com/keyware/shandan/browser/entity/PageVo.java

@ -26,14 +26,12 @@ public class TableInfoParser {
.name(form.getFormId())
.comment(form.getFormName())
.build();
String dbType = ContextHelper.getDatabaseType();
DbColumns dbColumns = DbColumns.valueOf(dbType);
Assert.notNull(dbColumns, "该数据库不支持:" + dbType);
JSONArray fieldConfigJson = JSONArray.parseArray(form.getFieldConfig());
analyzeGrid(fieldConfigJson);
Assert.isTrue(fieldConfigJson != null && fieldConfigJson.size() > 0, "表单解析失败,没有发现字段配置");
List<Column> columns = new ArrayList<>();
fieldConfigJson.forEach(obj -> {
JSONObject json = (JSONObject) obj;
@ -53,10 +51,58 @@ public class TableInfoParser {
columns.add(col);
}
table.setColumns(columns);
return table;
}
/**
* 处理 grid 表单中的一行多列组件
* @param fieldConfigJson
* @author YaoJz
* @date 2024/04/11 16:12
*/
private static void analyzeGrid(JSONArray fieldConfigJson){
//grid中列的信息
JSONArray gridColJsonArr = new JSONArray();
// fieldConfigJson中为grid的列
JSONArray gridJsonArr = new JSONArray();
fieldConfigJson.forEach(obj ->{
JSONObject json = (JSONObject) obj;
if ("grid".equals(json.get("tag").toString())){
gridJsonArr.add(obj);
getGridCol(json, gridColJsonArr);
}
});
gridJsonArr.forEach(obj->{
fieldConfigJson.remove(obj);
});
gridColJsonArr.forEach(obj->{
fieldConfigJson.add(obj);
});
}
/**
* 获取到 grid下的所有列---遍历树
* @param json
* @param gridColJsonArr
* @author YaoJz
* @date 2024/04/11 17:00
*/
private static void getGridCol( JSONObject json,JSONArray gridColJsonArr ){
JSONArray columns = json.getJSONArray("columns");
columns.forEach(col ->{
JSONObject colJson = (JSONObject) col;
JSONArray list = colJson.getJSONArray("list");
list.forEach(obj ->{
JSONObject colNextJson = (JSONObject) obj;
if (colNextJson!=null){
if ("grid".equals(colNextJson.get("tag").toString())){
getGridCol(colNextJson, gridColJsonArr );
}else {
gridColJsonArr.add(colNextJson);
}
}
});
});
}
public static Column parseColumn(@NotNull JSONObject json, Column dbColumn) {
Integer length = json.getInteger("fieldLength");
if (length == null) {

@ -3,11 +3,16 @@ package com.keyware.shandan.bianmu.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.keyware.shandan.bianmu.entity.DirectoryMetadataVo;
import com.keyware.shandan.bianmu.entity.DirectoryResource;
import com.keyware.shandan.bianmu.entity.MetadataBasicVo;
import com.keyware.shandan.bianmu.enums.ReviewStatus;
import com.keyware.shandan.bianmu.export.DirectoryExport;
import com.keyware.shandan.bianmu.export.ExportCache;
import com.keyware.shandan.bianmu.export.ExportProgress;
import com.keyware.shandan.bianmu.service.DataLabelsService;
import com.keyware.shandan.bianmu.service.DirectoryMetadataService;
import com.keyware.shandan.bianmu.service.MetadataService;
import com.keyware.shandan.browser.entity.SearchConditionVo;
import com.keyware.shandan.common.controller.BaseController;
import com.keyware.shandan.common.entity.Result;
import com.keyware.shandan.common.enums.SecretLevel;
@ -19,6 +24,8 @@ import com.keyware.shandan.system.entity.SysFormConfig;
import com.keyware.shandan.system.service.SysFileService;
import com.keyware.shandan.system.service.SysFormConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
@ -182,4 +189,27 @@ public class MetadataController extends BaseController<MetadataService, Metadata
ids.forEach(id -> sb.append("[").append(id).append("],"));
return sb.toString();
}
/**
* 目录数据导出-创建任务
*
* @param directoryId 目录ID
* @param condition 复杂查询条件
* @return 导出ID
*/
@PostMapping("/export/directory/{directoryId}")
public Result<Object> exportQuery(@PathVariable String directoryId, SearchConditionVo condition, Authentication auth) {
// Assert.notNull(auth, "用户信息认证失败");
// String userId = auth.getPrincipal().toString();
// // 这个逻辑只允许同一个用户同时只能有一个导出任务
// ExportProgress export = ExportCache.getCache(userId, null);
// if (export == null) {
// // 查询数据
// List<DirectoryResource> list = metadataSearchService.searchAllListByCondition(directoryId, condition);
// export = new DirectoryExport(userId, list);
// ExportCache.startExport(export);
// }
// return Result.of(export.getProgress(), !export.isError());
return null;
}
}

@ -0,0 +1,65 @@
package com.keyware.shandan.bianmu.controller;
import cn.hutool.core.io.FileUtil;
import com.keyware.shandan.bianmu.export.ExportCache;
import com.keyware.shandan.bianmu.export.ExportProgress;
import com.keyware.shandan.bianmu.service.MetadataService;
import com.keyware.shandan.common.entity.Result;
import com.keyware.shandan.common.util.FileDownload;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
/**
* 组合搜索前端控制器
*
* @author Administrator
* @since 2021/7/1
*/
@RestController
@RequestMapping("/search")
public class SearchController {
@Autowired
private MetadataService metadataService;
/**
* 目录数据导出-查询任务状态
*
* @param exportId 导出ID
* @return 导出ID
*/
@GetMapping("/export/status/{exportId}")
public Result<Object> exportStatus(@PathVariable String exportId, Authentication auth) {
String userId = auth.getPrincipal().toString();
ExportProgress export = ExportCache.getCache(userId, exportId);
if (export == null) {
return Result.of(null, false, "导出任务不存在");
}
if (export.isError()) {
ExportCache.popCache(userId, exportId);
}
return Result.of(export.getProgress(), !export.isError());
}
/**
* 目录数据导出-下载
*
* @param response HttpServletResponse
* @param exportId 导出ID
* @return -
*/
@GetMapping("/export/download/{exportId}")
public String exportDownload(HttpServletResponse response, @PathVariable String exportId, Authentication auth) {
String userId = auth.getPrincipal().toString();
ExportProgress export = ExportCache.popCache(userId, exportId);
if (export == null) {
return "错误:导出任务不存在";
}
String path = export.getDownloadPath();
String downloadName = FileUtil.getName(path);
File file = new File(path);
return FileDownload.download(response, file, downloadName, true);
}
}

@ -369,7 +369,35 @@ function startRender() {
openEditLayer(`${ctx}/business/metadata/edit?id=${obj.id}`)
}
})
//导出文件
metaListTable.addTableRowEvent('export', function (obj) {
console.log("文件导出")
if (basicData.id === DIR_ROOT_ID) {
showErrorMsg('不允许导出根目录数据');
return false;
}
Util.exportFileSearchResource('/business/metadata/list/exportDirectory',metaListTable.table.where)
// if (obj.resourceType === 'file') {
// layer.open({
// title: '文件下载',
// type: 0,
// content: `<p>是否需要同时下载置标标签数据?</p>`,
// btn: ['需要', '不需要'],
// yes: function (index) {
// layer.close(index)
// window.open(`${ctx}/sys/file/download/${obj.id}`)
// window.open(`${ctx}/sys/file/download/label/${obj.id}`)
// },
// btn2: function (index) {
// layer.close(index);
// window.open(`${ctx}/sys/file/download/${obj.id}`)
// },
//
// });
// } else {
// showErrorMsg('数据表不支持下载');
// }
})
// 下载
metaListTable.addTableRowEvent('downFile', function (obj) {
if (obj.resourceType === 'file') {

@ -94,7 +94,12 @@
title="{{disabled ? title : ''}}"
disabled="{{disabled ? 'disabled' : ''}}"
lay-event="addLink">关联数据表
</button>-->
</button> addFile{{disabled ? 'disabled' : ''}}
-->
<button class="layui-btn layui-btn-sm {{disabled ? 'layui-disabled' : ''}} "
title="{{disabled ? title : ''}}"
lay-event="export">
<i class="layui-icon">&#xe67c;</i>导出</button>
<button class="layui-btn layui-btn-sm {{disabled ? 'layui-disabled' : ''}} "
title="{{disabled ? title : ''}}"
lay-event="addFile{{disabled ? 'disabled' : ''}}">

@ -21,7 +21,10 @@ import com.keyware.shandan.browser.service.MetadataSearchService;
import com.keyware.shandan.browser.service.SearchService;
import com.keyware.shandan.common.entity.Result;
import com.keyware.shandan.common.util.FileDownload;
import com.keyware.shandan.dynacmicform.core.db.executor.SqlExecutor;
import com.keyware.shandan.frame.annotation.AppLog;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
@ -32,6 +35,7 @@ import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 组合搜索前端控制器
@ -42,7 +46,7 @@ import java.util.List;
@RestController
@RequestMapping("/search")
public class SearchController {
private static final Logger log = LoggerFactory.getLogger(SqlExecutor.class);
@Autowired
private SearchService searchService;
@ -146,7 +150,6 @@ public class SearchController {
@AppLog(operate = "文件全文检索查询")
@GetMapping("/full/file")
public Result<Object> searchFile(FullSearchParam param) {
try {
return Result.of(fileSearchService.searchFile(param));
} catch (IOException e) {
@ -154,7 +157,35 @@ public class SearchController {
return Result.of(null, false, "Elasticsearch 请求异常");
}
}
/**
* 检索文件 导出
* @param param
* @return com.keyware.shandan.common.entity.Result<java.lang.Object>
* @author YaoJz
* @date 2024/04/15 16:06
*/
@AppLog(operate = "文件全文检索导出")
@GetMapping("/full/fileExport")
public Result<Object> searchFileExport(FullSearchParam param,Authentication auth) {
Assert.notNull(auth, "用户信息认证失败");
String userId = auth.getPrincipal().toString();
// 这个逻辑只允许同一个用户同时只能有一个导出任务
ExportProgress export = ExportCache.getCache(userId, null);
if (export == null) {
// 查询数据
List<DirectoryResource> list = null;
try {
list = fileSearchService.searchFileAll(param);
} catch (IOException e) {
log.error("检索文件失败",e);
}
;
export = new DirectoryExport(userId, list);
ExportCache.startExport(export);
}
return Result.of(export.getProgress(), !export.isError());
}
/**
* 查询指定数据资源下的数据
*

@ -1,8 +1,10 @@
package com.keyware.shandan.browser.service;
import cn.hutool.core.util.ReflectUtil;
import com.keyware.shandan.bianmu.entity.DirectoryResource;
import com.keyware.shandan.bianmu.entity.DirectoryVo;
import com.keyware.shandan.bianmu.enums.ReviewStatus;
import com.keyware.shandan.bianmu.mapper.DirectoryResourceMapper;
import com.keyware.shandan.bianmu.service.DirectoryService;
import com.keyware.shandan.browser.SearchLogProcessor;
import com.keyware.shandan.browser.entity.FullSearchParam;
@ -24,6 +26,7 @@ import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchPhraseQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
@ -57,7 +60,41 @@ public class FileSearchService {
@Autowired
private SysSettingService settingService;
@Autowired
private DirectoryResourceMapper directoryResourceMapper;
public PageVo searchFile(FullSearchParam param) throws IOException {
SearchResponse response = getSearchResponse(param);
return PageVo.ofSearchHits(response.getHits(), param);
}
/**
* 获取es检索出的全部结果
* @author YaoJz
* @date 2024/04/15 16:10
*/
public List<DirectoryResource> searchFileAll(FullSearchParam param) throws IOException{
SearchResponse searchResponse = getSearchResponse(param);
SearchHits hits = searchResponse.getHits();
SearchHit[] hits1 = hits.getHits();
List<Map<String, Object>> list = Arrays.stream(hits1).map(PageVo::fillMetadata).collect(Collectors.toList());
ArrayList<String> arrayList = new ArrayList<>();
for (Map map : list){
arrayList.add((String) map.get("id"));
}
//根据查询出的id 获取 DirectoryResource
List<DirectoryResource> directoryResources = directoryResourceMapper.selectListById(arrayList);
return directoryResources;
}
/**
* 抽取了原来 searchFile 方法 将es检索出的全部结果返回
* @param param
* @return org.elasticsearch.action.search.SearchResponse
* @author YaoJz
* @date 2024/04/15 16:09
*/
private SearchResponse getSearchResponse(FullSearchParam param) throws IOException {
SearchRequest request = Requests.searchRequest("shandan");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.from(param.getPage() * param.getSize() - param.getSize()).size(param.getSize());
@ -136,7 +173,7 @@ public class FileSearchService {
request.source(searchSourceBuilder);
SearchResponse response = esClient.search(request, RequestOptions.DEFAULT);
return PageVo.ofSearchHits(response.getHits(), param);
return response;
}
/**

@ -67,6 +67,9 @@ public class MetadataSearchService {
return directoryResourceMapper.customSelectList(parent.getDirectoryPath() + "/", labels, queryWrapper);
}
public List<DirectoryResource> selectListById(){
return directoryResourceMapper.selectListById(new ArrayList<String>());
}
public QueryWrapper<DirectoryResource> buildQueryWrapper(DirectoryVo dir, SearchConditionVo condition) {
QueryWrapper<DirectoryResource> queryWrapper = new QueryWrapper<>();
@ -106,6 +109,13 @@ public class MetadataSearchService {
}
}
break;
case "secretLevel":
System.out.println("secretLevel");
System.out.println("secretLevel");
System.out.println("secretLevel");
System.out.println("secretLevel");
break;
default:
}
});

@ -84,6 +84,7 @@ layui.use(['layer', 'listPage', 'globalTree', 'gtable', 'form', 'element', 'drop
* @param node
*/
function nodeClickEventListener(node) {
let {basicData} = node.param;
activeDirectory = basicData || {id: DIR_ROOT_ID};
//设置当前位置
@ -215,6 +216,7 @@ layui.use(['layer', 'listPage', 'globalTree', 'gtable', 'form', 'element', 'drop
function initConditionBlock() {
// 监听更多条件按钮点击事件
$('#condition-btn').click(function () {
const display = $('#condition-div').css('display');
if (display == 'none') {
$('div[lay-id="dirMetadataTable"] .mengban').show().on('click', function () {
@ -331,7 +333,7 @@ layui.use(['layer', 'listPage', 'globalTree', 'gtable', 'form', 'element', 'drop
let htm = '';
conditions = [];
for (let key in formVal) {
if (key === 'searchKeyInput') continue;
// if (key === 'searchKeyInput') continue;
let fieldName = key, fieldValue = formVal[key], logicJudgement;
// 如果fieldName是条件下拉框,则跳过
if (fieldName.startsWith('logic-') || fieldName === 'directoryId' || fieldName === 'metadataId' || fieldName === 'preciseQuery') continue;
@ -357,7 +359,11 @@ layui.use(['layer', 'listPage', 'globalTree', 'gtable', 'form', 'element', 'drop
case 'like':
condition = '包含';
}
if (key === 'searchKeyInput'){
fieldName = "metadataName"
conditions.push({fieldName, fieldValue, logicJudgement});
continue;
}
if (fieldName === 'secretLevel') {
hasText = $(`select[name="secretLevel"] option[value="${fieldValue}"]`).text();
} else if (fieldName === 'dataFrom') {
@ -435,7 +441,9 @@ layui.use(['layer', 'listPage', 'globalTree', 'gtable', 'form', 'element', 'drop
function exportDirectory(id, params) {
Util.exportResource(`/search/export/directory/${id}`, params)
}
function exportFileSearchDirectory(id, params) {
Util.exportFileSearchResource(`/search/full/fileExport?metaId=${id}`, params)
}
/**
* 初始化文件全文检索查询列表
* @param id 目录ID
@ -453,7 +461,9 @@ layui.use(['layer', 'listPage', 'globalTree', 'gtable', 'form', 'element', 'drop
height: 'full-110',
where: {searchFields: THeadSetLayer.filterConfigData(fileTableId).filter(conf => conf.isShow).map(conf => conf.colName).join(',')},
request: {pageName: 'page', limitName: 'size'},
defaultToolbar: [{title: '列表配置', layEvent: 'theadSet2', icon: 'layui-icon-cols'}],
defaultToolbar: [
{title: '导出', layEvent: 'export', icon: 'layui-icon-export'},
{title: '列表配置', layEvent: 'theadSet2', icon: 'layui-icon-cols'}],
autoSort: false,
limit: 30,
method: 'get',
@ -485,7 +495,13 @@ layui.use(['layer', 'listPage', 'globalTree', 'gtable', 'form', 'element', 'drop
dirFileTable.table.where.sort = sort;
dirFileTable.reloadTable({table: dirFileTable.table})
})
dirFileTable.addTableRowEvent('export', function (obj) {
if (id === DIR_ROOT_ID) {
showErrorMsg('不允许导出根目录数据');
return false;
}
layer.confirm('该导出操作比较耗时,导出时请勿刷新页面', {title: '提示信息'}, () => exportFileSearchDirectory(id, dirFileTable.table.where));
})
// 查看按钮监听
dirFileTable.addTableRowEvent('details-file', function (obj) {
openMaxLayerWithURL(`${ctx}/sys/file/view?fileId=${obj.id}`)

@ -0,0 +1,10 @@
package com.keyware.shandan.common.service;
/**
* @author YaoJz
* @description
* @date 2024/4/16 15:28
*/
public class ExportServiceImpl implements IExportService{
}

@ -0,0 +1,9 @@
package com.keyware.shandan.common.service;
/**
* @author YaoJz
* @description
* @date 2024/4/16 15:27
*/
public interface IExportService {
}

@ -547,6 +547,56 @@ commonUtil = {
return Math.round(size * 100) / 100 + initUnit;
}
},
exportFileSearchResource: (url, params)=>{
console.log("download")
let layerIndex;
showExportMsgLayer('开始准备数据');
Util.get(url, params, false).then(({flag, data, msg}) => {
if (flag) {
showExportMsgLayer(data.msg);
queryProgress(data.exportId);
} else {
showExportMsgLayer('数据查询失败,' + msg);
}
})
function queryProgress(exportId) {
console.log("download0")
setTimeout(function () {
Util.get(`/search/export/status/${exportId}`, {}, false).then(({flag, data, msg}) => {
console.log("download1")
if (flag) {
showExportMsgLayer(data.msg);
if (data.isDone) {
console.log("download2")
layer.close(layerIndex);
console.log("download3",`${ctx}`,'/search/export/download/${exportId}',`${exportId}`)
window.open(`${ctx}/search/export/download/${exportId}`, '_blank')
console.log("download4wwwwww")
} else {
queryProgress(exportId);
}
} else {
showExportMsgLayer('数据查询失败,' + msg);
}
});
}, 1000);
}
function showExportMsgLayer(msg) {
layerIndex && layer.close(layerIndex);
layerIndex = layer.msg(msg, {
icon: 16,
shade: 0.01,
anim: -1,
isOutAnim: false,
time: 1000 * 60 * 30,
closeBtn: 1
});
}
},
exportResource: (url, params)=>{
let layerIndex;
showExportMsgLayer('开始准备数据');

@ -6,6 +6,10 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.keyware.shandan.bianmu.entity.DirectoryResource;
import com.keyware.shandan.bianmu.entity.DirectoryVo;
import com.keyware.shandan.bianmu.entity.MetadataBasicVo;
import com.keyware.shandan.bianmu.export.DirectoryExport;
import com.keyware.shandan.bianmu.export.ExportCache;
import com.keyware.shandan.bianmu.export.ExportProgress;
import com.keyware.shandan.bianmu.mapper.DirectoryResourceMapper;
import com.keyware.shandan.bianmu.service.*;
import com.keyware.shandan.common.entity.Result;
import com.keyware.shandan.common.util.StreamUtil;
@ -17,13 +21,16 @@ import com.keyware.shandan.system.constants.FormTypeEnum;
import com.keyware.shandan.system.entity.SysFormConfig;
import com.keyware.shandan.system.service.SysFormConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import java.io.IOException;
import java.text.Collator;
import java.util.*;
@ -56,7 +63,8 @@ public class MetadataCommonController {
@Autowired
private DataLabelsService labelsService;
@Autowired
private DirectoryResourceMapper directoryResourceMapper;
@Autowired
private SysFormConfigService formConfigService;
@ -147,4 +155,56 @@ public class MetadataCommonController {
}
return Result.of(result);
}
@GetMapping("/list/exportDirectory")
@Transactional(readOnly = true)
public Result<Object> exportDirectory(Authentication auth,Page<DirectoryResource> page, String directoryId, String resourceName, boolean allChild, String reviewStatus, String type) {
if (StringUtils.isBlank(directoryId)) {
return Result.of(null, false, "参数不能为空");
}
Page<DirectoryResource> result = treeService.resourcePage(page, directoryId, resourceName, allChild,reviewStatus);
StreamUtil.as(result.getRecords()).peek(item -> {
if (StringUtils.hasText(item.getDirectoryPath())) {
int i = item.getDirectoryPath().lastIndexOf("/");
if (i > 0) {
item.setDirectoryPath(item.getDirectoryPath().substring(0, i));
}
}
item.setHasMark(labelEntityService.getById(item.getId()) != null);
}).toList();
//定义中文排序器
Comparator<Object> chinese = Collator.getInstance(Locale.CHINA);
if(!"control".equals(type)) {
result.getRecords().sort((a, b) -> chinese.compare(a.getDirectoryPath(), b.getDirectoryPath()));
}else{
result.getRecords().sort((a, b) -> compare(b.getTaskTime(), a.getTaskTime()));
}
List<DirectoryResource> records = result.getRecords();
ArrayList<String> list = new ArrayList<>();
for (DirectoryResource directoryResource:records){
list.add(directoryResource.getDirectoryId());
}
List<DirectoryResource> directoryResources = directoryResourceMapper.selectListById(list);
Assert.notNull(auth, "用户信息认证失败");
String userId = auth.getPrincipal().toString();
// 这个逻辑只允许同一个用户同时只能有一个导出任务
ExportProgress export = ExportCache.getCache(userId, null);
if (export == null) {
// 查询数据
export = new DirectoryExport(userId, directoryResources);
ExportCache.startExport(export);
}
return Result.of(export.getProgress(), !export.isError());
}
@GetMapping("/export/status/{exportId}")
public Result<Object> exportStatus(@PathVariable String exportId, Authentication auth) {
String userId = auth.getPrincipal().toString();
ExportProgress export = ExportCache.getCache(userId, exportId);
if (export == null) {
return Result.of(null, false, "导出任务不存在");
}
if (export.isError()) {
ExportCache.popCache(userId, exportId);
}
return Result.of(export.getProgress(), !export.isError());
}
}

@ -0,0 +1,191 @@
package com.keyware.shandan.bianmu.export;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.poi.excel.BigExcelWriter;
import cn.hutool.poi.excel.ExcelUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.keyware.shandan.bianmu.entity.DirectoryResource;
import com.keyware.shandan.bianmu.entity.MetadataBasicVo;
import com.keyware.shandan.bianmu.service.MetadataService;
import com.keyware.shandan.common.util.StreamUtil;
import com.keyware.shandan.common.util.StringUtils;
import com.keyware.shandan.frame.config.component.AppContext;
import com.keyware.shandan.system.entity.SysFile;
import com.keyware.shandan.system.service.SysFileService;
import com.keyware.shandan.system.utils.SysSettingUtil;
import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author YaoJz
* @description
* @date 2024/4/15 16:40
*/
public class FileSearchExport extends ExportProgress {
private final List<DirectoryResource> dataList;
private final SysFileService fileService;
private final MetadataService metadataService;
private final String outputDir = "目录导出_" + DateUtil.format(new Date(), "yyyyMMddHHmmss");
private final String outputPath = tempDir + File.separator + outputDir;
private final String zipName = outputPath + ".zip";
public FileSearchExport(String userId, List<DirectoryResource> dataList) {
super(userId);
this.dataList = dataList;
this.fileService = AppContext.getContext().getBean(SysFileService.class);
this.metadataService = AppContext.getContext().getBean(MetadataService.class);
}
@Override
public void run() {
try {
this.setTitle("正在生成临时数据文件");
dataList.forEach(resource -> {
if ("file".equals(resource.getResourceType())) {
exportFile(resource);
} else {
exportMetadata(resource);
}
this.autoAddStep();
});
this.setTitle("正在打包数据文件");
ZipUtil.zip(outputPath, zipName, true);
this.autoAddStep();
this.setDone();
this.setTitle("数据包准备完毕,开始下载");
} catch (Exception e) {
throw new RuntimeException("导出数据在打包时出错", e);
} finally {
delete(outputPath);
}
}
/**
* 导出文件
*
* @param resource 数据资源
*/
private void exportFile(DirectoryResource resource) {
File serverFile = new File(createResourceDir(resource.getDirectoryPath()));
SysFile file = fileService.getById(resource.getId());
String fileDownloadUrl = SysSettingUtil.getSysSetting("BIANMU").getSysInternalAddress() + file.getWebUri();
try {
HttpUtil.downloadFile(fileDownloadUrl, serverFile);
} catch (Exception e) {
List<String> lines = new ArrayList<>();
String dirPath = serverFile.getParentFile().getPath();
String name = "(文件读取错误)" + serverFile.getName();
FileUtil.writeLines(lines, new File(dirPath, name), "utf-8");
}
}
/**
* 导出数据表
*
* @param resource 数据资源
*/
private void exportMetadata(DirectoryResource resource) {
File serverFile = new File(createResourceDir(resource.getDirectoryPath()));
String path = serverFile.getPath() + ".xlsx";
MetadataBasicVo metadata = null;
JSONArray colsArray = null;
List<Map<String, Object>> datas = null;
try {
metadata = metadataService.getById(resource.getId());
colsArray = metadataService.getColumns(metadata.getId());
datas = metadataService.getDynamicData(metadata);
} catch (Exception e) {
colsArray = new JSONArray();
JSONObject defaultJson = new JSONObject();
defaultJson.put("comment", "数据查询失败,无法连接到指定的数据库");
colsArray.add(defaultJson);
datas = new ArrayList<>();
File temp = new File(path);
String dirPath = temp.getParentFile().getPath();
path = dirPath + File.separator + "(数据读取错误)" + temp.getName();
}
// 所有行的集合
List<List<String>> rowList = new ArrayList<>();
// 字段列名集合
List<String> colNameList = new ArrayList<>();
// 字段列名注释集合,当做表格的第一行
List<String> columns = StreamUtil.as(colsArray).map(json -> {
JSONObject col = (JSONObject) json;
String comment = col.getString("comment");
String colName = col.getString("columnName");
colNameList.add(colName);
return colName + (StringUtils.hasText(comment) ? "[" + comment + "]" : "");
}).toList();
// 添加第一行
rowList.add(columns);
// 遍历数据
datas.forEach(data -> {
// 遍历字段列名,获取对应数据并拼接成列的集合
List<String> cells = colNameList.stream().map(col -> {
Object value = data.get(col);
if (value == null) {
return "";
}
return String.valueOf(value);
}).collect(Collectors.toList());
rowList.add(cells);
});
List<List<String>> rows = CollUtil.newArrayList(rowList);
//通过工具类创建writer
BigExcelWriter writer = ExcelUtil.getBigWriter(path);
//一次性写出内容
writer.write(rows);
//关闭writer,释放内存
writer.close();
}
/**
* 根据资源路径创建目录
*
* @param resourcePath 资源路径
* @return 资源的的绝对路径
*/
private String createResourceDir(String resourcePath) {
String path = outputPath + File.separator + resourcePath;
File file = new File(path);
File parent = file.getParentFile();
if (!parent.exists()) {
parent.mkdirs();
}
return path;
}
private void delete(String path) {
File file = new File(path);
if (file.exists()) {
FileUtil.del(file);
}
}
@Override
public long getStepTotal() {
return dataList.size() + 1;
}
@Override
public String getDownloadPath() {
return zipName;
}
}

@ -34,6 +34,20 @@ public interface DirectoryResourceMapper extends BaseMapper<DirectoryResource> {
@ResultType(DirectoryResource.class)
List<DirectoryResource> customSelectList(@Param("dirPath") String dirPath, @Param("labels") List<DataLabelsVo> labels, @Param(Constants.WRAPPER) QueryWrapper<DirectoryResource> queryWrapper);
// DIRECTORY_PATH LIKE (#{dirPath} || '%') AND
@Select("<script>" +
" SELECT DISTINCT TT.* FROM (" +
" SELECT * FROM V_DIRECTORY_RESOURCE VDR WHERE EXISTS(SELECT 1 FROM B_DIRECTORY D1 WHERE D1.ID = VDR.PARENT_ID AND D1.REVIEW_STATUS IN ('PASS','SUBMITTED'))" +
" ) TT " +
" LEFT JOIN (SELECT B2.*, B1.ENTITY_ID FROM B_DATA_LABEL_ENTITY B1 INNER JOIN B_DATA_LABELS B2 ON B1.LABEL_ID = B2.ID) DLN ON DLN.ENTITY_ID = TT.ID " +
" WHERE TT.ID IN " +
" <foreach collection='ids' item='id' open='(' separator=',' close=')'>" +
" #{id}" +
" </foreach>" +
"</script>")
@ResultType(DirectoryResource.class)
List<DirectoryResource> selectListById( @Param("ids") List<String> ids);
@Select("<script>" +
" SELECT DISTINCT TT.* FROM (" +
" SELECT * FROM V_DIRECTORY_RESOURCE VDR WHERE DIRECTORY_PATH LIKE (#{dirPath} || '%') AND EXISTS(SELECT 1 FROM B_DIRECTORY D1 WHERE D1.ID = VDR.PARENT_ID AND D1.REVIEW_STATUS IN ('PASS','SUBMITTED'))" +

@ -65,7 +65,6 @@ public interface DataLabelsService extends IBaseService<DataLabelsVo, Integer> {
/**
* 统计标签下关联的实体数据数量
*
* @param label
* @return
*/

@ -82,7 +82,7 @@ public class PageVo<T> implements Serializable {
* @param hit -
* @return -
*/
private static Map<String, Object> fillMetadata(SearchHit hit) {
public static Map<String, Object> fillMetadata(SearchHit hit) {
SearchResultRow resultRow = new SearchResultRow();
//Map<String, Object> source = hit.getSourceAsMap();
resultRow.putAll(hit.getSourceAsMap());