# Conflicts:
#	data/src/main/java/com/sdm/data/controller/DataFileController.java
#	data/src/main/java/com/sdm/data/service/IDataFileService.java
#	data/src/main/java/com/sdm/data/service/impl/MinioFileIDataFileServiceImpl.java
#	project/src/main/java/com/sdm/project/controller/SimulationRunController.java
#	project/src/main/java/com/sdm/project/service/ISimulationRunService.java
#	project/src/main/java/com/sdm/project/service/impl/SimulationRunServiceImpl.java
This commit is contained in:
2025-11-25 16:42:49 +08:00
87 changed files with 1802 additions and 338 deletions

View File

@@ -116,6 +116,15 @@ public class SdmResponse<T> implements Serializable {
return new SdmResponse<T>(ResultCode.FAILED.getCode(), message, null);
}
/**
* 失败返回结果同时返回message
*
* @param message 提示信息
*/
public static <T> SdmResponse<T> failed(String message,T data) {
return new SdmResponse<T>(ResultCode.FAILED.getCode(), message, data);
}
/**
* 失败返回结果
*

View File

@@ -20,4 +20,13 @@ public enum NodeTypeEnum {
public String getValue() {
return value;
}
/**
* 是否节点类型
*/
public static boolean isNodeType(String value) {
return PROJECT.getValue().equals(value) || PHASE.getValue().equals(value)
|| DISCIPLINE.getValue().equals(value) || MACHINE.getValue().equals(value)
|| WORKSPACE.getValue().equals(value);
}
}

View File

@@ -0,0 +1,22 @@
package com.sdm.common.entity.req.data;
import lombok.Data;
import java.util.List;
@Data
public class KnowledgeCallBackReq {
// 接口5.1 前端生成的,用于发起统一一次审批流的凭证,5.2接口每次都会返回
private String uploadTaskId;
// 接口5.1 返回的这个文件成功文件的id集合
private List<Long> succBusinessIds;
// 接口5.1 返回的这个文件失败文件的id集合
private List<Long> failBusinessIds;
// 是否需要审批 0 否1 是
private Integer isApprove;
}

View File

@@ -0,0 +1,37 @@
package com.sdm.common.entity.req.pbs.hpc;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
@Data
public class HpcChunkUploadFileReq {
private String taskName;
// 原始文件的名称
private String sourceFileName;
// 当前为第几分片
private Integer chunk;
// DigestUtils.md5Hex(chunkData[])
private String chunkMd5;
// // 每个分块的大小--就是正常固定分片大小
// private Long size;
// 分片总数
private Integer chunkTotal;
// 分块文件传输对象
private MultipartFile file;
// // 是否为第一次请求,1 是,0 不是
// private Integer isFirstReq;
private Integer type;
// 第一片请求不传,后面的请求必传,第一次请求成功后后端会返回,本次文件的父目录
private String fileDirPath;
}

View File

@@ -0,0 +1,20 @@
package com.sdm.common.entity.req.pbs.hpc;
import lombok.Data;
import java.util.List;
@Data
public class HpcDownloadFilesReq {
// @NotBlank(message = "任务名不能为空")
private String taskName;
// @NotNull(message = "任务类型不能为空")
private Integer type;
private List<String> filesPaths;
// private String dirName;
}

View File

@@ -0,0 +1,19 @@
package com.sdm.common.entity.req.pbs.hpc;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "合并创建HPC JOB请求参数")
public class MergeSubmitHpcJobReq {
@Schema(description = "创建HPC JOB请求参数,对应的new指令")
private NewJobReq newJobReq;
@Schema(description = "HPC作业添加任务请求参数对应 job add 命令)")
private AddJobReq addJobReq;
@Schema(description = "HPC作业提交请求参数对应 job submit 命令)")
private SubmitHpcJobReq submitHpcJobReq;
}

View File

@@ -17,7 +17,4 @@ public class ChunkUploadMinioFileResp {
// 分片文件的临时目录,第一次请求后,每次都会返回
private String fileTempPath;
// 失败的原因
private String errMsg;
}

View File

@@ -1,5 +1,7 @@
package com.sdm.common.entity.resp.data;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -11,6 +13,7 @@ import java.time.LocalDateTime;
*/
@Data
@Schema(name = "FileMetadataDTO", description = "文件元数据传输对象")
@JsonIgnoreProperties(ignoreUnknown = true)
public class FileMetadataInfoResp implements Serializable {
private static final long serialVersionUID = 1L;
@@ -52,12 +55,14 @@ public class FileMetadataInfoResp implements Serializable {
private Long creatorId;
@Schema(description = "创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
@Schema(description = "更新者ID")
private Long updaterId;
@Schema(description = "更新时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
@Schema(description = "所属项目ID")
@@ -85,4 +90,28 @@ public class FileMetadataInfoResp implements Serializable {
@Schema(description = "完整访问URL拼接minio网关地址 + objectKey")
private String fileUrl;
private String relatedResourceUuid;
private String relatedResourceUuidOwnType;
private String dirType;
private String dirStatus;
private String fileStatus;
private String fileType;
private String approvalStatus;
private String approveType;
private String tempMetadata;
private String cidFlowId;
private String uploadTaskId;
private String uploadStatus;
private String cidFlowReviewer;
private String tag1;
private String tag2;
private String tag3;
private String tag4;
private String tag5;
private String tag6;
private String tag7;
private String tag8;
private String tag9;
private String tag10;
}

View File

@@ -3,6 +3,6 @@ package com.sdm.common.entity.resp.pbs.hpc;
import lombok.Data;
@Data
public class AddJobResp {
public class AddJobResp extends HpcBaseResp {
private String tsakId;
}

View File

@@ -3,6 +3,6 @@ package com.sdm.common.entity.resp.pbs.hpc;
import lombok.Data;
@Data
public class CloneJobResp {
public class CloneJobResp extends HpcBaseResp {
private String jobId;
}

View File

@@ -0,0 +1,17 @@
package com.sdm.common.entity.resp.pbs.hpc;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "创建HPC指令响应基类")
public class HpcBaseResp {
@Schema(description = "当前请求对应的完整指令", example = "job submit /id:3007")
private String hpcCommand;
@Schema(description = "当前请求指令异常信息", example = "'11' 不是内部或外部命令,也不是可运行的程序或批处理文件。")
private String errMsg;
}

View File

@@ -3,6 +3,6 @@ package com.sdm.common.entity.resp.pbs.hpc;
import lombok.Data;
@Data
public class JobCancelResp {
public class JobCancelResp extends HpcBaseResp {
private Boolean canceled;
}

View File

@@ -3,6 +3,6 @@ package com.sdm.common.entity.resp.pbs.hpc;
import lombok.Data;
@Data
public class JobFinishResp {
public class JobFinishResp extends HpcBaseResp {
private Boolean finished;
}

View File

@@ -3,6 +3,6 @@ package com.sdm.common.entity.resp.pbs.hpc;
import lombok.Data;
@Data
public class JobModifyResp {
public class JobModifyResp extends HpcBaseResp {
private Boolean modified;
}

View File

@@ -3,7 +3,7 @@ package com.sdm.common.entity.resp.pbs.hpc;
import lombok.Data;
@Data
public class JobRequeueResp {
public class JobRequeueResp extends HpcBaseResp {
private Boolean requeued;

View File

@@ -1,9 +1,10 @@
package com.sdm.common.entity.resp.pbs.hpc;
import com.sdm.common.common.SdmResponse;
import lombok.Data;
@Data
public class JobViewResp {
public class JobViewResp extends HpcBaseResp{
String id;
String state;
String name;

View File

@@ -1,8 +1,13 @@
package com.sdm.common.entity.resp.pbs.hpc;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class NewJobResp {
@Schema(description = "job new 返回")
public class NewJobResp extends HpcBaseResp {
@Schema(description = "job new 返回的任务id", example = "3001")
private String jobId;
}

View File

@@ -3,7 +3,7 @@ package com.sdm.common.entity.resp.pbs.hpc;
import lombok.Data;
@Data
public class NodeViewResp {
public class NodeViewResp extends HpcBaseResp {
private String systemId;
private String systemGuid;
private String jobTypes;

View File

@@ -3,7 +3,7 @@ package com.sdm.common.entity.resp.pbs.hpc;
import lombok.Data;
@Data
public class SubmitHpcJobResp {
public class SubmitHpcJobResp extends HpcBaseResp {
// true 成功 false 失败
private Boolean submit;

View File

@@ -0,0 +1,13 @@
package com.sdm.common.entity.resp.pbs.hpc.listjobs;
import com.sdm.common.entity.resp.pbs.hpc.HpcBaseResp;
import lombok.Data;
import java.util.List;
@Data
public class ListJobResp extends HpcBaseResp {
private List<ListJobs> listJobs;
}

View File

@@ -1,9 +1,9 @@
package com.sdm.common.entity.resp.pbs.hpc;
package com.sdm.common.entity.resp.pbs.hpc.listjobs;
import lombok.Data;
@Data
public class ListJobResp {
public class ListJobs {
// 任务ID对应表格Id列
private String id;
// 所有者对应表格Owner列格式\用户名

View File

@@ -1,9 +1,9 @@
package com.sdm.common.entity.resp.pbs.hpc;
package com.sdm.common.entity.resp.pbs.hpc.listtasks;
import lombok.Data;
@Data
public class ListTasksResp {
public class ListTasks {
private String taskId;
private String state;
private String taskName;

View File

@@ -0,0 +1,13 @@
package com.sdm.common.entity.resp.pbs.hpc.listtasks;
import com.sdm.common.entity.resp.pbs.hpc.HpcBaseResp;
import lombok.Data;
import java.util.List;
@Data
public class ListTasksResp extends HpcBaseResp {
private List<ListTasks> listTasks;
}

View File

@@ -1,9 +1,9 @@
package com.sdm.common.entity.resp.pbs.hpc;
package com.sdm.common.entity.resp.pbs.hpc.nodecore;
import lombok.Data;
@Data
public class NodeListCoreResp {
public class NodeListCore {
private String nodeProcessor; // CARSAFECLIENT - 0
private String state; // Idle / Running / Offline
private String jobId; // 可能为空

View File

@@ -0,0 +1,13 @@
package com.sdm.common.entity.resp.pbs.hpc.nodecore;
import com.sdm.common.entity.resp.pbs.hpc.HpcBaseResp;
import lombok.Data;
import java.util.List;
@Data
public class NodeListCoreResp extends HpcBaseResp {
private List<NodeListCore> nodeCores;
}

View File

@@ -1,9 +1,9 @@
package com.sdm.common.entity.resp.pbs.hpc;
package com.sdm.common.entity.resp.pbs.hpc.nodelist;
import lombok.Data;
@Data
public class NodeListResp {
public class NodeList {
private String nodeName;
private String state;
private String max;

View File

@@ -0,0 +1,11 @@
package com.sdm.common.entity.resp.pbs.hpc.nodelist;
import com.sdm.common.entity.resp.pbs.hpc.HpcBaseResp;
import lombok.Data;
import java.util.List;
@Data
public class NodeListResp extends HpcBaseResp {
private List<NodeList> nodes;
}

View File

@@ -3,6 +3,7 @@ package com.sdm.common.feign.impl.data;
import com.sdm.common.common.SdmResponse;
import com.sdm.common.entity.req.data.*;
import com.sdm.common.entity.req.system.LaunchApproveReq;
import com.sdm.common.entity.resp.PageDataResp;
import com.sdm.common.entity.resp.data.FileMetadataInfoResp;
import com.sdm.common.feign.inter.data.IDataFeignClient;
import lombok.extern.slf4j.Slf4j;
@@ -42,8 +43,8 @@ public class DataClientFeignClientImpl implements IDataFeignClient {
}
@Override
public SdmResponse queryDir(QueryDirReq req) {
SdmResponse response;
public SdmResponse<PageDataResp<List<FileMetadataInfoResp>>> queryDir(QueryDirReq req) {
SdmResponse<PageDataResp<List<FileMetadataInfoResp>>> response;
try {
response = dataClient.queryDir(req);
log.info("创建文响应件夹:"+ response);
@@ -109,10 +110,10 @@ public class DataClientFeignClientImpl implements IDataFeignClient {
}
@Override
public SdmResponse<FileMetadataInfoResp> queryFileMetadataInfo(String uuid, String uuidOwnType) {
public SdmResponse<FileMetadataInfoResp> queryFileMetadataInfo(String uuid, String uuidOwnType, Integer dirId) {
SdmResponse<FileMetadataInfoResp> response;
try {
response = dataClient.queryFileMetadataInfo(uuid, uuidOwnType);
response = dataClient.queryFileMetadataInfo(uuid, uuidOwnType, dirId);
log.info("查询文件夹响应:"+ response);
return response;
} catch (Exception e) {

View File

@@ -63,6 +63,40 @@ public class SimulationNodeFeignClientImpl implements ISimulationNodeFeignClient
}
@Override
public SdmResponse<List<AllNodeByProjectIdAndTypeResp>> getNodeTaskList(String uuid) {
SdmResponse<List<AllNodeByProjectIdAndTypeResp>> response;
try {
log.info("获取节点下的task列表请求参数uuid={}", uuid);
response = ISimulationNodeFeignClient.getNodeTaskList(uuid);
if (!response.isSuccess() || response.getData() == null || response.getData().isEmpty()) {
return SdmResponse.failed("获取节点下的task列表失败");
}
} catch (Exception e) {
log.error("获取节点下的task列表失败", e);
return SdmResponse.failed("获取节点下的task列表失败");
}
return response;
}
@Override
public SdmResponse<List<AllNodeByProjectIdAndTypeResp>> getTaskRunList(String uuid) {
SdmResponse<List<AllNodeByProjectIdAndTypeResp>> response;
try {
log.info("获取task下的run请求参数uuid={}", uuid);
response = ISimulationNodeFeignClient.getTaskRunList(uuid);
if (!response.isSuccess() || response.getData() == null || response.getData().isEmpty()) {
return SdmResponse.failed("获取task下的run失败");
}
} catch (Exception e) {
log.error("获取task下的run失败", e);
return SdmResponse.failed("获取task下的run失败");
}
return response;
}
@Override
public SdmResponse delteNode(DelNodeReq req) {
SdmResponse response;

View File

@@ -3,6 +3,7 @@ package com.sdm.common.feign.inter.data;
import com.sdm.common.common.SdmResponse;
import com.sdm.common.entity.req.data.*;
import com.sdm.common.entity.req.system.LaunchApproveReq;
import com.sdm.common.entity.resp.PageDataResp;
import com.sdm.common.entity.resp.data.FileMetadataInfoResp;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.servlet.http.HttpServletResponse;
@@ -26,7 +27,7 @@ public interface IDataFeignClient {
SdmResponse createDir(@RequestBody @Validated CreateDirReq req);
@PostMapping("/data/queryDir")
SdmResponse queryDir(@RequestBody @Validated QueryDirReq req);
SdmResponse<PageDataResp<List<FileMetadataInfoResp>>> queryDir(@RequestBody @Validated QueryDirReq req);
@PostMapping("/data/renameDirNew")
SdmResponse renameDirNew(@RequestBody @Validated RenameDirReq req);
@@ -42,7 +43,7 @@ public interface IDataFeignClient {
SdmResponse uploadFiles(UploadFilesReq req);
@GetMapping("/data/queryFileMetadataInfo")
SdmResponse<FileMetadataInfoResp> queryFileMetadataInfo(@RequestParam(value = "uuid") String uuid, @RequestParam(value = "uuidOwnType") String uuidOwnType);
SdmResponse<FileMetadataInfoResp> queryFileMetadataInfo(@RequestParam(value = "uuid") String uuid, @RequestParam(value = "uuidOwnType") String uuidOwnType, @RequestParam(value = "dirId") Integer dirId);
@PostMapping("/data/downloadFileToLocal")
void downloadFileToLocal(@RequestParam(value = "fileId") @Validated Long fileId, @RequestParam(value = "path") @Validated String path);

View File

@@ -5,6 +5,7 @@ import com.sdm.common.entity.req.project.DelNodeReq;
import com.sdm.common.entity.req.project.SpdmNodeListReq;
import com.sdm.common.entity.resp.AllNodeByProjectIdAndTypeResp;
import com.sdm.common.entity.resp.project.SimulationNodeResp;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@@ -33,6 +34,18 @@ public interface ISimulationNodeFeignClient {
@GetMapping("/node/getAllNodeByProjectIdAndType")
SdmResponse<List<AllNodeByProjectIdAndTypeResp>> getAllNodeByProjectIdAndType(@RequestParam(value = "chooseNodeId") String uuid, @RequestParam(value = "nextNodeType") String nextNodeType);
/**
* 获取节点下的task列表
*/
@GetMapping("/node/getNodeTaskList")
SdmResponse<List<AllNodeByProjectIdAndTypeResp>> getNodeTaskList(@RequestParam(value = "uuid") String uuid);
/**
* 获取task下的run
*/
@GetMapping("/node/getNodeTaskRunList")
SdmResponse<List<AllNodeByProjectIdAndTypeResp>> getTaskRunList(@RequestParam(value = "uuid") String uuid);
@PostMapping("node/delteNode")
SdmResponse delteNode(@RequestBody DelNodeReq req);

View File

@@ -1,6 +1,10 @@
package com.sdm.common.utils;
import com.sdm.common.entity.resp.pbs.hpc.*;
import com.sdm.common.entity.resp.pbs.hpc.listjobs.ListJobs;
import com.sdm.common.entity.resp.pbs.hpc.listtasks.ListTasks;
import com.sdm.common.entity.resp.pbs.hpc.nodecore.NodeListCore;
import com.sdm.common.entity.resp.pbs.hpc.nodelist.NodeList;
import com.sdm.common.log.CoreLogger;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@@ -79,20 +83,17 @@ public class HpcCommandResulParseUtil {
}
}
public static List<ListJobResp> parseJobLists(String cmdOutput) {
List<ListJobResp> result = new ArrayList<>();
public static List<ListJobs> parseJobLists(String cmdOutput) {
List<ListJobs> result = new ArrayList<>();
try {
if (StringUtils.isBlank(cmdOutput)) {
CoreLogger.error("parseJobLists cmdOutput null,cmdOutput:{}",cmdOutput);
return result;
}
String[] lines = cmdOutput.split("\r?\n");
ListJobResp current = null;
ListJobs current = null;
for (String line : lines) {
if (line == null) continue;
String trimmed = line.trim();
if (StringUtils.isBlank(trimmed)) {
continue; // 空行直接跳过,不影响逻辑
@@ -119,7 +120,7 @@ public class HpcCommandResulParseUtil {
if (current != null && current.getId() != null) {
result.add(current);
}
current = new ListJobResp();
current = new ListJobs();
current.setId(value);
continue;
}
@@ -159,13 +160,13 @@ public class HpcCommandResulParseUtil {
return result;
}
public static List<ListTasksResp> parseJobTasks(String cmdOutput) {
List<ListTasksResp> result = new ArrayList<>();
public static List<ListTasks> parseJobTasks(String cmdOutput) {
List<ListTasks> result = new ArrayList<>();
if (cmdOutput == null || cmdOutput.trim().isEmpty()) {
return result;
}
String[] lines = cmdOutput.split("\r?\n");
ListTasksResp current = null;
ListTasks current = null;
String lastKey = null;
for (String line : lines) {
String trimmed = line.trim();
@@ -180,7 +181,7 @@ public class HpcCommandResulParseUtil {
// 遇到 Task Id 开头新建对象
if (trimmed.startsWith("Task Id")) {
current = new ListTasksResp();
current = new ListTasks();
}
if (current == null) continue;
if (trimmed.contains(":")) {
@@ -233,11 +234,10 @@ public class HpcCommandResulParseUtil {
}
public static JobViewResp parseJobView(String cmdOutput) {
if (cmdOutput == null || cmdOutput.trim().isEmpty()) {
return null;
}
JobViewResp job = new JobViewResp();
if (cmdOutput == null || cmdOutput.trim().isEmpty()) {
return job;
}
JobViewTaskStatus taskStatus = new JobViewTaskStatus();
boolean inTaskBlock = false; // 标识是否在 Task Count 下的嵌套块
String lastKey = null; // 记录上一行 key用于多行值累加
@@ -343,13 +343,13 @@ public class HpcCommandResulParseUtil {
public static List<NodeListResp> parseNodList(String cmdOutput) {
List<NodeListResp> result = new ArrayList<>();
public static List<NodeList> parseNodList(String cmdOutput) {
List<NodeList> result = new ArrayList<>();
if (cmdOutput == null || cmdOutput.trim().isEmpty()) {
return result;
}
String[] lines = cmdOutput.split("\r?\n");
NodeListResp current = null;
NodeList current = null;
for (String line : lines) {
String trimmed = line.trim();
// 空行 = 一个节点结束
@@ -362,7 +362,7 @@ public class HpcCommandResulParseUtil {
}
// 新的 Node 记录开始
if (trimmed.startsWith("Node Name")) {
current = new NodeListResp();
current = new NodeList();
}
if (current != null && trimmed.contains(":")) {
String[] kv = trimmed.split(":", 2);
@@ -387,12 +387,12 @@ public class HpcCommandResulParseUtil {
return result;
}
public static List<NodeListCoreResp> parseNodeCoreList(String cmdOutput) {
List<NodeListCoreResp> list = new ArrayList<>();
public static List<NodeListCore> parseNodeCoreList(String cmdOutput) {
List<NodeListCore> list = new ArrayList<>();
// 按空行分割每个 Core 的块
String[] blocks = cmdOutput.split("\\n\\s*\\n");
for (String block : blocks) {
NodeListCoreResp resp = new NodeListCoreResp();
NodeListCore resp = new NodeListCore();
String[] lines = block.split("\\n");
String lastKey = null;
StringBuilder lastValue = new StringBuilder();
@@ -422,7 +422,7 @@ public class HpcCommandResulParseUtil {
}
// 将字段名映射到 Java 对象
private static void applyField(NodeListCoreResp resp, String key, String value) {
private static void applyField(NodeListCore resp, String key, String value) {
switch (key) {
case "Node Processor":
resp.setNodeProcessor(value);
@@ -444,12 +444,9 @@ public class HpcCommandResulParseUtil {
public static NodeViewResp parseNodeView(String cmdOutput) {
NodeViewResp resp = new NodeViewResp();
String[] lines = cmdOutput.split("\\n");
String lastKey = null;
StringBuilder lastValue = new StringBuilder();
for (String line : lines) {
if (line.contains(":")) {
// 保存上一对 key-value

View File

@@ -229,6 +229,23 @@ public class ExcelUtil {
}
}
/**
* 获取excel表头
* @param exportExcelFormats
* @return
*/
private static List<HeadVO> getExcelHeader(List<ExportExcelFormat> exportExcelFormats)
{
List<HeadVO> excelHeader = new ArrayList<>();
for(ExportExcelFormat exportExcelFormat : exportExcelFormats)
{
HeadVO headVO = HeadVO.builder().build();
headVO.setKey(exportExcelFormat.getTitle());
excelHeader.add(headVO);
}
return excelHeader;
}
/**
* 导出没有合并单元格excel
* @param dataArray
@@ -239,13 +256,7 @@ public class ExcelUtil {
ExcelSheet excelSheet = new ExcelSheet();
excelSheet.setSheetName("export sheet1");
//获取excel表头
List<HeadVO> excelHeader = new ArrayList<>();
for(ExportExcelFormat exportExcelFormat : exportExcelFormats)
{
HeadVO headVO = HeadVO.builder().build();
headVO.setKey(exportExcelFormat.getTitle());
excelHeader.add(headVO);
}
List<HeadVO> excelHeader = getExcelHeader(exportExcelFormats);
excelSheet.setHeads(excelHeader);
//获取excel表行数据
@@ -274,4 +285,185 @@ public class ExcelUtil {
exportExcel(excelSheets,response);
}
static class ParaseData
{
List<String> keyValues = new ArrayList<>();
int lines;
ParaseData parent;
List<ParaseData> children = new ArrayList<>();
public void increaseLine()
{
lines++;
if(parent!=null)
{
parent.increaseLine();
}
}
public void addchildren(ParaseData paraseData)
{
paraseData.parent=this;
children.add(paraseData);
}
}
/**
* 解析excel字段数据
* @param jsonObject
* @param exportExcelFormats
* @param parent
* @return
*/
private static ParaseData paraseJsonObject(JSONObject jsonObject,List<ExportExcelFormat> exportExcelFormats,ParaseData parent)
{
List<ExportExcelFormat> excelFormats = exportExcelFormats.subList(0,exportExcelFormats.size());
Iterator<ExportExcelFormat> iterator = excelFormats.iterator();
boolean bMatch = false;
ParaseData paraseData = null;
while (iterator.hasNext()) {
ExportExcelFormat format = iterator.next();
String key = format.getKey();
if(jsonObject.containsKey(key))
{
if(!bMatch)
{
bMatch = true;
paraseData = new ParaseData();
if(parent != null)
{
parent.addchildren(paraseData);
}
paraseData.increaseLine();
}
String value = jsonObject.getString(key);
JSONObject dictObj = format.getDictData();
if(dictObj != null)
{
value = dictObj.getString(value);
}
paraseData.keyValues.add(value);
iterator.remove();
}
else
{
JSONArray children = jsonObject.getJSONArray("children");
if(children != null)
{
for(int i=0;i<children.size();i++)
{
JSONObject child = children.getJSONObject(i);
ParaseData parentData = paraseData;
if(parentData == null)
parentData = parent;
paraseJsonObject(child,exportExcelFormats,parentData);
}
}
}
}
return paraseData;
}
/**
* 将parase data树整理成excel列
* @param paraseData
* @param paraseDataMap
* @param beginColumn
* @param beginRow
*/
private static void combineParaseData(ParaseData paraseData,Map<Integer,List<ExcelCellValue>> paraseDataMap,int beginColumn,int beginRow)
{
for(int index=0;index<paraseData.keyValues.size();index++)
{
String key = paraseData.keyValues.get(index);
int columnIndex = beginColumn+index;
List<ExcelCellValue> cellValues = paraseDataMap.get(columnIndex);
if(cellValues==null)
{
cellValues = new ArrayList<>();
paraseDataMap.put(columnIndex,cellValues);
}
boolean isMerged = paraseData.lines > 1;
if(isMerged)
{
for (int rowIndex = 0; rowIndex < paraseData.lines; rowIndex++) {
ExcelCellValue cellValue = new ExcelCellValue();
cellValue.setValue(key);
cellValue.setMerge(true);
cellValue.setFirstRow(beginRow+rowIndex);
cellValue.setLastRow(beginRow+paraseData.lines-1);
cellValues.add(cellValue);
}
}
else
{
ExcelCellValue cellValue = new ExcelCellValue();
cellValue.setValue(key);
cellValue.setMerge(false);
cellValues.add(cellValue);
}
}
int rowNum = 0;
for(ParaseData paseData : paraseData.children)
{
combineParaseData(paseData,paraseDataMap,beginColumn+paraseData.keyValues.size(),rowNum);
if(paseData.lines>1)
{
rowNum += paraseData.lines;
}
else
{
rowNum++;
}
}
}
/**
* 导出有合并单元格excel
* @param dataArray
* @param exportExcelFormats
* @param response
*/
public static void exportExcelWithMerge(JSONArray dataArray,List<ExportExcelFormat> exportExcelFormats,HttpServletResponse response)
{
ExcelSheet excelSheet = new ExcelSheet();
excelSheet.setSheetName("export sheet1");
List<HeadVO> excelHeader = getExcelHeader(exportExcelFormats);
excelSheet.setHeads(excelHeader);
Map<Integer,List<ExcelCellValue>> paraseDataMap = new HashMap<>();
int rowNum = 0;
for(int index=0;index<dataArray.size();index++)
{
JSONObject jsonObject = dataArray.getJSONObject(index);
ParaseData data = paraseJsonObject(jsonObject,exportExcelFormats,null);
combineParaseData(data,paraseDataMap,0,rowNum);
if(data.lines>1)
{
rowNum += data.lines;
}
else
{
rowNum++;
}
}
int columnSize = paraseDataMap.size();
int rowIndex = 0;
LOOP: while(true)
{
RowValue rowValue = new RowValue();
for (int i = 0; i < columnSize; i++) {
List<ExcelCellValue> cellValues = paraseDataMap.get(i);
if(rowIndex > cellValues.size())
break LOOP;
rowValue.getCells().add(cellValues.get(rowIndex));
}
excelSheet.getRowValues().add(rowValue);
rowIndex++;
}
List<ExcelSheet> excelSheets = new ArrayList<>();
excelSheets.add(excelSheet);
exportExcel(excelSheets,response);
}
}