This commit is contained in:
2026-03-30 09:27:37 +08:00
25 changed files with 449 additions and 123 deletions

View File

@@ -60,6 +60,10 @@ public class SimulationReportTemplateServiceImpl extends ServiceImpl<SimulationR
@Override
public SdmResponse createReportTemplate(ReportTemplateDto templateDto) {
if (!CollectionUtils.isEmpty(this.lambdaQuery().eq(SimulationReportTemplate::getTemplateName, templateDto.getTemplateName()).list())) {
return SdmResponse.failed("同名报告模板已存在,请修改");
}
SimulationReportTemplate reportTemplate = new SimulationReportTemplate();
BeanUtils.copyProperties(templateDto, reportTemplate);
reportTemplate.setUuid(generateUuid("report_template_"));

View File

@@ -1,8 +1,12 @@
package com.sdm.common.entity.req.data;
import com.alibaba.fastjson2.annotation.JSONField;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
import java.util.Map;
@Data
public class CopyFileToTaskReq {
@@ -14,4 +18,25 @@ public class CopyFileToTaskReq {
@Schema(description = "要复制到的文件夹id")
private Long parentDirId;
// ----------------------------------------------------------------
// 很重要用于设置标签通过autoFillDictTags切面 设置dictTagIdsCache
/**
* 字典标签查询结果缓存
* key: dictClass字典类
* value: Map<dictValue, dictId>(字典值 -> 字典ID
* 此字段由服务层填充,前端不需传入
*/
@Schema(description = "字典标签查询结果缓存", hidden = true)
@JSONField(serialize = false)
private Map<String, Map<String, Integer>> dictTagIdsCache;
@Schema(description = "字典标签查询列表,格式:['fileTypeDictClass','fileTypeDictValue','disciplineTypeDictClass','disciplineDictValue']")
private List<String> dictTags;
@Schema(description = "文件类型字典类")
private String fileTypeDictClass;
@Schema(description = "文件类型字典值")
private String fileTypeDictValue;
}

View File

@@ -84,4 +84,18 @@ public class SimulationTaskFeignClientImpl implements ISimulationTaskFeignClient
return SdmResponse.failed("内部调用根据学科获取所有的任务失败");
}
}
@Override
public SdmResponse<List<String>> getUuidsByTag1s(String tag1s) {
try {
SdmResponse<List<String>> response = simulationTaskFeignClient.getUuidsByTag1s(tag1s);
if(!response.isSuccess() || ObjectUtils.isEmpty(response.getData())){
return SdmResponse.failed("内部调用根据tag1获取任务uuid列表失败");
}
return response;
} catch (Exception e) {
log.error("内部调用根据tag1获取任务uuid列表失败", e);
return SdmResponse.failed("内部调用根据tag1获取任务uuid列表失败");
}
}
}

View File

@@ -47,4 +47,12 @@ public interface ISimulationTaskFeignClient {
*/
@PostMapping("/task/getAllTasksByDiscipline")
SdmResponse<Map<String, List<String>>> getAllTasksByDiscipline(@RequestBody GetAllTasksByDisciplineReq req);
/**
* 根据tag1列表获取对应的任务uuid列表
* @param tag1s tag1列表(逗号分隔)
* @return 任务uuid列表
*/
@GetMapping("/task/getUuidsByTag1s")
SdmResponse<List<String>> getUuidsByTag1s(@RequestParam(value = "tag1s") String tag1s);
}

View File

@@ -100,5 +100,7 @@ public class SysLogDTO {
private String businessId;
// 文件id
private Long fileId;
// 项目uuid列表(逗号分隔),用于筛选指定项目下的文件下载和预览次数
private String tag1;
}

View File

@@ -1810,7 +1810,14 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService {
uploadFilesReq.setSize(sourceMetadataInfo.getFileSize());
List<Long> ancestorDirIds = collectAncestorDirIds(targetParentMetadataInfo.getId());
saveFileStorageStats(fileInfo, uploadFilesReq, ancestorDirIds);
saveCopyFileTags(uploadFilesReq, fileInfo, sourceMetadataInfo, ancestorDirIds);
if (StringUtil.isEmpty(req.getFileTypeDictValue())) {
saveCopyFileTags(uploadFilesReq, fileInfo, sourceMetadataInfo, ancestorDirIds);
} else {
UploadFilesReq uploadFilesReq1 = new UploadFilesReq();
BeanUtils.copyProperties(req, uploadFilesReq1);
uploadFilesReq1.setSize(fileInfo.getFileSize());
saveFileTags(uploadFilesReq1, fileInfo, sourceMetadataInfo, ancestorDirIds);
}
createFilePermission(fileInfo.getId());
return SdmResponse.success(fileInfo.getId());

View File

@@ -0,0 +1,23 @@
package com.sdm.pbs.config;
/**
* PBS HPC 通用常量类
* 统一管理状态、枚举值、固定配置
*/
public class PbsCommonConstant {
private PbsCommonConstant() {
// 私有构造,禁止实例化
}
// ======================== 任务类型 type ========================
public static final String TASK_TYPE_SINGLE = "single"; // 单独任务
public static final String TASK_TYPE_BATCH = "batch"; // 批量任务
public static final String TASK_TYPE_FLOWABLE = "flowable"; // 流程任务
public static final String EXECUTE_MODE_WEB_SINGLE = "WEB_SINGLE";
public static final String EXECUTE_MODE_WEB_BATCH = "WEB_BATCH";
// ======================== 删除标识 delFlag ========================
public static final String DEL_FLAG_NO = "N"; // 未删除
public static final String DEL_FLAG_YES = "Y"; // 已删除
}

View File

@@ -6,11 +6,13 @@ import com.sdm.common.common.ThreadLocalContext;
import com.sdm.common.entity.req.pbs.SubmitHpcTaskRemoteReq;
import com.sdm.common.feign.inter.pbs.ITaskFeignClient;
import com.sdm.common.utils.FilesUtil;
import com.sdm.pbs.config.PbsCommonConstant;
import com.sdm.pbs.model.bo.BatchWebSubmitResp;
import com.sdm.pbs.model.entity.SimulationJob;
import com.sdm.pbs.model.req.BatchHpcTaskReq;
import com.sdm.pbs.model.req.OneHpcTaskReq;
import com.sdm.pbs.model.req.SubmitHpcTaskReq;
import com.sdm.pbs.model.req.WebSubmitReq;
import com.sdm.pbs.service.ISimulationJobService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -37,8 +39,6 @@ public class TaskAdapter implements ITaskFeignClient {
private static final String EXECUTE_MODE_AUTO = "AUTO";
private static final String EXECUTE_MODE_MANUAL = "MANUAL";
private static final String EXECUTE_MODE_WEB_SINGLE = "WEB_SINGLE";
private static final String EXECUTE_MODE_WEB_BATCH = "WEB_BATCH";
private static final String FLOWABLE_SIMULATION_BASEDIR = "/home/simulation/";
@Autowired
@@ -50,30 +50,17 @@ public class TaskAdapter implements ITaskFeignClient {
@Value("${hpc.webSubmit.basePath:/home/simulation/hpc}")
private String BASE_STORAGE_PATH ;
@Value("${testEnStr:}")
private String enStr;
@Value("${testEnStr2:}")
private String testEnStr2;
@Value("${pbs.task.impl}")
private String pbsImpl;
@GetMapping("/testEn")
@Operation(summary = "作业提交")
public SdmResponse<Map<String,Object>> testEn() {
Map<String, Object> map = new HashMap<>();
map.put("enStr", enStr);
map.put("pbsImpl", pbsImpl);
map.put("testEnStr2", testEnStr2);
return SdmResponse.success(map);
}
@PostMapping("/adapterSubmitHpcJob")
@Operation(summary = "作业提交")
public SdmResponse<String> adapterSubmitHpcJob(@RequestBody SubmitHpcTaskRemoteReq req) {
// 从flowable 过来的请求
// 预插入数据
WebSubmitReq webSubmitReq = buildWebSubmitReq(req,"flowable发起任务","flowable");
SdmResponse<SimulationJob> preResp = taskController.preSingleHpcJobSubmit(webSubmitReq,false);
if(!preResp.isSuccess()) {
log.error("flowable作业提交失败插入任务数据异常:{}",JSONObject.toJSONString(req));
throw new RuntimeException("hpc作业提交失败插入任务数据异常");
}
// spdm 回传路径
// 求解文件获取,可以在这一层分片上传,然后拿到对应的路径
SubmitHpcTaskReq submitHpcTaskReq = new SubmitHpcTaskReq();
@@ -86,7 +73,7 @@ public class TaskAdapter implements ITaskFeignClient {
@Operation(summary = "独立网页hpc作业提交")
public SdmResponse<String> webSubmit(@RequestBody OneHpcTaskReq req) {
// 预插入数据
SdmResponse<SimulationJob> preResp = taskController.preSingleHpcJobSubmit(req);
SdmResponse<SimulationJob> preResp = taskController.preSingleHpcJobSubmit(req,false);
if(!preResp.isSuccess()) {
log.error("hpc作业提交失败插入任务数据异常:{}",JSONObject.toJSONString(req));
throw new RuntimeException("hpc作业提交失败插入任务数据异常");
@@ -98,7 +85,7 @@ public class TaskAdapter implements ITaskFeignClient {
getSimulationFile(remoteReq,submitHpcTaskReq,"");
// 公共提交接口
BeanUtils.copyProperties(remoteReq,submitHpcTaskReq);
submitHpcTaskReq.setFrom(EXECUTE_MODE_WEB_SINGLE);
submitHpcTaskReq.setFrom(PbsCommonConstant.EXECUTE_MODE_WEB_SINGLE);
Long userId = ThreadLocalContext.getUserId();
Long tenantId = ThreadLocalContext.getTenantId();
// 异步发起失败后数据更改todo
@@ -109,8 +96,8 @@ public class TaskAdapter implements ITaskFeignClient {
@PostMapping("/batchWebSubmit")
@Operation(summary = "批量网页hpc作业提交")
public SdmResponse<String> batchWebSubmit(@RequestBody BatchHpcTaskReq req) {
// 预插入数据
SdmResponse<SimulationJob> preResp = taskController.preSingleHpcJobSubmit(req);
// 预插入数据
SdmResponse<SimulationJob> preResp = taskController.preSingleHpcJobSubmit(req,true);
if(!preResp.isSuccess()) {
log.error("batchWebSubmit作业提交失败插入任务数据异常:{}",JSONObject.toJSONString(req));
throw new RuntimeException("hpc作业提交失败插入任务数据异常");
@@ -174,7 +161,7 @@ public class TaskAdapter implements ITaskFeignClient {
submitHpcTaskReq.setInputFilePaths(inputFilePaths);
}
// 网页单一任务
if (Objects.equals(req.getExecuteMode(),EXECUTE_MODE_WEB_SINGLE)) {
if (Objects.equals(req.getExecuteMode(), PbsCommonConstant.EXECUTE_MODE_WEB_SINGLE)) {
// 文件的基础路径,就是本次任务的总目录
// /hpc/{userId}/{jobName}/{uuid}
Long userId = ThreadLocalContext.getUserId();
@@ -188,7 +175,7 @@ public class TaskAdapter implements ITaskFeignClient {
req.setStdoutSpdmNasFilePath(fileBasePath);
}
// 网页批量任务
if(Objects.equals(req.getExecuteMode(),EXECUTE_MODE_WEB_BATCH)){
if(Objects.equals(req.getExecuteMode(),PbsCommonConstant.EXECUTE_MODE_WEB_BATCH)){
Pair<String, List<String>> pair = FilesUtil.getSingleSubmitFiles(batchFilePath);
// 本地主文件
submitHpcTaskReq.setMasterFilePath(pair.getLeft());
@@ -215,7 +202,7 @@ public class TaskAdapter implements ITaskFeignClient {
remoteReq.setIndependence(jobDb.getIndependence());
remoteReq.setUuid(req.getUuid());
// 网页单一任务
remoteReq.setExecuteMode(EXECUTE_MODE_WEB_SINGLE);
remoteReq.setExecuteMode(PbsCommonConstant.EXECUTE_MODE_WEB_SINGLE);
return remoteReq;
}
@@ -237,7 +224,7 @@ public class TaskAdapter implements ITaskFeignClient {
remoteReq.setSoftware(jobDb.getSoftware());
remoteReq.setJobType(jobDb.getJobType());
// 网页批量任务
remoteReq.setExecuteMode(EXECUTE_MODE_WEB_BATCH);
remoteReq.setExecuteMode(PbsCommonConstant.EXECUTE_MODE_WEB_BATCH);
return remoteReq;
}
@@ -335,10 +322,17 @@ public class TaskAdapter implements ITaskFeignClient {
String lastDirName = FilesUtil.getLastDirectoryName(taskDirAllPath);
// 构建远程请求参数
SubmitHpcTaskRemoteReq remoteReq = batchBuildCommonRemoteReq(req, jobDb, lastDirName);
// 批量任务的子任务预插入数据
WebSubmitReq prewebSubmitReq = buildWebSubmitReq(remoteReq, remoteReq.getJobDesc(), remoteReq.getJobType());
SdmResponse<SimulationJob> preResp = taskController.preSingleHpcJobSubmit(prewebSubmitReq,false);
if(!preResp.isSuccess()) {
log.error("批量子作业提交失败,插入任务数据异常:{}",JSONObject.toJSONString(req));
throw new RuntimeException("批量子作业提交失败,插入任务数据异常");
}
// 构建任务提交请求
SubmitHpcTaskReq submitHpcTaskReq = buildSubmitTaskReq(remoteReq,taskDirAllPath);
// 设置批量提交来源
submitHpcTaskReq.setFrom(EXECUTE_MODE_WEB_BATCH);
submitHpcTaskReq.setFrom(PbsCommonConstant.EXECUTE_MODE_WEB_BATCH);
// 调用提交接口
SdmResponse<String> response = taskController.submitHpcJob(submitHpcTaskReq);
// 处理成功结果
@@ -395,6 +389,27 @@ public class TaskAdapter implements ITaskFeignClient {
log.info("doBatchSubmitHpcTasks back:{}",JSONObject.toJSONString(results));
}
/**
* 封装Flowable请求为WebSubmitReq对象
* @param req 前端/Flowable传入的请求
* @return 封装好的WebSubmitReq
*/
private WebSubmitReq buildWebSubmitReq(SubmitHpcTaskRemoteReq req,String jobDesc,String type) {
WebSubmitReq webSubmitReq = new WebSubmitReq();
// 生成无横杠UUID
String uuid = UUID.randomUUID().toString().replace("-", "");
req.setUuid(uuid);
// 赋值
webSubmitReq.setUuid(uuid);
webSubmitReq.setSoftwareId(req.getSoftwareId());
webSubmitReq.setSoftware(req.getSoftware());
webSubmitReq.setJobName(req.getJobName());
webSubmitReq.setCoreNum(req.getCoreNum());
webSubmitReq.setJobDesc(jobDesc);
webSubmitReq.setHpcGroup("HPC_PACK");
webSubmitReq.setType(type);
return webSubmitReq;
}
}

View File

@@ -289,8 +289,9 @@ public class TaskController {
return pbsService.downloadFile(req.getJobId(),req.getFileName());
}
public SdmResponse<SimulationJob> preSingleHpcJobSubmit(WebSubmitReq req) {
return pbsService.preSingleHpcJobSubmit(req);
// firstBatch 批量任务总数据是true,其他false
public SdmResponse<SimulationJob> preSingleHpcJobSubmit(WebSubmitReq req,boolean firstBatch) {
return pbsService.preSingleHpcJobSubmit(req,firstBatch);
}
}

View File

@@ -0,0 +1,17 @@
package com.sdm.pbs.model.entity;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum HpcFileStatus {
uplinking("文件上传中-上行"),
upsuccend("文件上行成功"),
upfailed("文件上行异常"),
generating("任务运行文件生成中"),
uploading("文件回传中-下行"),
finished("文件回传完成"),
failed("文件回传失败");
private final String desc;
}

View File

@@ -0,0 +1,19 @@
package com.sdm.pbs.model.entity;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum HpcJobStatus {
Preparing("准备"),
Configuring("配置中"),
Queued("队列中"),
// 表示:调度器已为任务分配好计算资源,正在与目标节点通信以启动任务执行
Dispatching("调度中"),
Running("执行中"),
Canceled("已取消"),
Finished("已完成"),
Failed("任务失败");
private final String desc;
}

View File

@@ -47,7 +47,7 @@ public class WebSubmitReq implements Serializable {
private String hpcGroup;
/**
* single :单独任务 or batch :批量任务
* single :单独任务 or batch :批量任务flowable 工作流发起的任务
*/
private String type;

View File

@@ -62,6 +62,6 @@ public interface IPbsService {
SdmResponse<String> stopStreamTaskLog(String clientToken);
SdmResponse<SimulationJob> preSingleHpcJobSubmit(WebSubmitReq req);
SdmResponse<SimulationJob> preSingleHpcJobSubmit(WebSubmitReq req,boolean firstBatch);
}

View File

@@ -148,7 +148,7 @@ public class IPbsHpcServiceImpl implements IPbsService {
}
@Override
public SdmResponse<SimulationJob> preSingleHpcJobSubmit(WebSubmitReq req) {
public SdmResponse<SimulationJob> preSingleHpcJobSubmit(WebSubmitReq req,boolean firstBatch) {
return SdmResponse.success();
}

View File

@@ -25,10 +25,11 @@ import com.sdm.common.utils.PageUtils;
import com.sdm.pbs.model.bo.CommandResult;
import com.sdm.pbs.model.bo.HpcJobStatusInfo;
import com.sdm.pbs.model.bo.HpcResouceInfo;
import com.sdm.pbs.model.entity.SimulationHpcCommand;
import com.sdm.pbs.model.entity.SimulationHpcCommandPlaceholder;
import com.sdm.pbs.model.entity.SimulationJob;
import com.sdm.pbs.model.req.*;
import com.sdm.pbs.model.entity.*;
import com.sdm.pbs.model.req.JobFileCallBackReq;
import com.sdm.pbs.model.req.QueryJobReq;
import com.sdm.pbs.model.req.SubmitHpcTaskReq;
import com.sdm.pbs.model.req.WebSubmitReq;
import com.sdm.pbs.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
@@ -66,8 +67,6 @@ import java.util.stream.Collectors;
@ConditionalOnProperty(name = "pbs.task.impl", havingValue = "hpc")
public class PbsServiceDecorator implements IPbsServiceDecorator {
private static final String EXECUTE_MODE_WEB_SINGLE = "WEB_SINGLE";
@Autowired
private HpcCommandExcuteUtil hpcCommandExcuteUtil;
@@ -156,7 +155,13 @@ public class PbsServiceDecorator implements IPbsServiceDecorator {
String subDir = generateHpcSubDir(req);
// 0.处理hpc文件
String masterFilePath = handleHpcFileUpload(req, subDir);
// 重新赋值用于command拼接
// 更新上行文件状态
String fileStatus=StringUtils.isNotBlank(masterFilePath)? HpcFileStatus.upsuccend.name():HpcFileStatus.upfailed.name();
updateFileStatusByUuid(req.getUuid(),fileStatus);
if(StringUtils.isBlank(masterFilePath)){
throw new RuntimeException("hpc文件上行失败");
}
// 重新赋值用于command拼接
req.setMasterFilePath(masterFilePath);
// 任务输出的文件夹
Pair<String, String> pair = extractDirectoryAndFileName(masterFilePath);
@@ -347,7 +352,7 @@ public class PbsServiceDecorator implements IPbsServiceDecorator {
simulationJob.setTotalElapsedTime(null);
// 标识及状态
// simulationJob.setUuid(null);
simulationJob.setFileStatus("generating");
simulationJob.setFileStatus(HpcFileStatus.generating.name());
// 审计字段
Long userId = ThreadLocalContext.getUserId();
Long tenantId = ThreadLocalContext.getTenantId();
@@ -369,15 +374,18 @@ public class PbsServiceDecorator implements IPbsServiceDecorator {
if(StringUtils.isNotBlank(req.getJobDesc())){
simulationJob.setJobDesc(req.getJobDesc());
}
// 独立单一任务
if(Objects.equals(req.getFrom(),EXECUTE_MODE_WEB_SINGLE)){
SimulationJob jobDb = simulationJobService.lambdaQuery().eq(SimulationJob::getUuid, req.uuid).one();
simulationJob.setId(jobDb.getId());
simulationJobService.updateById(simulationJob);
}else {
simulationJobService.save(simulationJob);
}
SimulationJob jobDb = simulationJobService.lambdaQuery().eq(SimulationJob::getUuid, req.uuid).one();
simulationJob.setId(jobDb.getId());
boolean b = simulationJobService.updateById(simulationJob);
log.info("hpc submit end,update :{},jobId:{}",b,jobId);
// // 批量的任务
// if(Objects.equals(req.getFrom(), PbsCommonConstant.EXECUTE_MODE_WEB_BATCH)){
// simulationJobService.save(simulationJob);
// }else {
// SimulationJob jobDb = simulationJobService.lambdaQuery().eq(SimulationJob::getUuid, req.uuid).one();
// simulationJob.setId(jobDb.getId());
// simulationJobService.updateById(simulationJob);
// }
}
}
@@ -622,7 +630,7 @@ public class PbsServiceDecorator implements IPbsServiceDecorator {
}
@Override
public SdmResponse<SimulationJob> preSingleHpcJobSubmit(WebSubmitReq req) {
public SdmResponse<SimulationJob> preSingleHpcJobSubmit(WebSubmitReq req,boolean firstBatch) {
// 预提交数据入库
SimulationJob simulationJob = new SimulationJob();
// 基础字段
@@ -633,16 +641,18 @@ public class PbsServiceDecorator implements IPbsServiceDecorator {
// job 的类型
simulationJob.setJobType(req.getHpcGroup());
// 0flow 任务 1独立提交单一任务2独立提交批量任务
int independence = Objects.equals(req.getType(),"single") ? 1:2;
int independence = getIndependenceByType(req.getType());
simulationJob.setIndependence(independence);
// 软件及文件关联
simulationJob.setSoftwareId(req.getSoftwareId());
// 任务装备状态
simulationJob.setJobStatus("Preparing");
simulationJob.setJobStatus(HpcJobStatus.Preparing.name());
// 求解器名称
simulationJob.setSolverName(req.getSoftware());
// 文件上行处理中
simulationJob.setFileStatus("uplinking");
// 文件上行处理中,批量的总数据,不存文件状态
if(!firstBatch){
simulationJob.setFileStatus(HpcFileStatus.uplinking.name());
}
// 文件详情
simulationJob.setJobDesc(req.getJobDesc());
simulationJob.setUuid(req.getUuid());
@@ -1080,5 +1090,34 @@ public class PbsServiceDecorator implements IPbsServiceDecorator {
}
return resultMap;
}
/**
* 根据任务类型获取独立性标识
* @param type 任务类型 single/batch/flowable
* @return independence 1/2/0
*/
private int getIndependenceByType(String type) {
if (Objects.equals(type, "single")) {
return 1;
}
if (Objects.equals(type, "batch")) {
return 2;
}
// flowable 或其他未知类型,默认返回 0
return 0;
}
/**
* 根据UUID更新文件状态
* @param uuid 唯一标识
* @param fileStatus 状态值generating、uploading、finished、failed
*/
private boolean updateFileStatusByUuid(String uuid, String fileStatus) {
boolean update = simulationJobService.lambdaUpdate()
.eq(SimulationJob::getUuid, uuid)
.set(SimulationJob::getFileStatus, fileStatus)
.update();
log.info("submit hpc file up end:{}", update);
return update;
}
}

View File

@@ -319,4 +319,13 @@ public class SimulationTaskController implements ISimulationTaskFeignClient {
return taskService.mergeQueryNode(req);
}
/**
* 根据tag1列表获取对应的任务uuid列表
*/
@GetMapping("/getUuidsByTag1s")
@Operation(summary = "根据tag1获取任务uuid列表", description = "根据tag1(项目uuid)列表获取对应的任务uuid列表")
public SdmResponse<List<String>> getUuidsByTag1s(@RequestParam(value = "tag1s") String tag1s) {
return SdmResponse.success(simulationTaskService.getUuidsByTag1s(tag1s));
}
}

View File

@@ -4,6 +4,8 @@ import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.List;
/**
* 任务 完成情况统计请求参数(工位、学科)
*/
@@ -48,4 +50,17 @@ public class TaskCompleteStatisticsReq {
@Schema(description = "标签10")
private String tag10;
// 以下为解析后的List字段支持逗号分隔的多个值
// 注意tag6 用于过滤 discipline 列,所以不需要单独的 tag6List
private List<String> tag1List;
private List<String> tag2List;
private List<String> tag3List;
private List<String> tag4List;
private List<String> tag5List;
private List<String> tag7List;
private List<String> tag8List;
private List<String> tag9List;
private List<String> tag10List;
private List<String> disciplineList;
}

View File

@@ -27,4 +27,11 @@ public interface ISimulationTaskService extends IService<SimulationTask> {
void batchCreateTaskFromDemand(List<SimulationDemand> demandList, Boolean isDownloadFlag, Map<String,String> fileNameMap);
/**
* 根据tag1列表获取对应的任务uuid列表
* @param tag1s tag1列表(逗号分隔)
* @return 任务uuid列表
*/
List<String> getUuidsByTag1s(String tag1s);
}

View File

@@ -2913,9 +2913,15 @@ public class SimulationRunServiceImpl extends ServiceImpl<SimulationRunMapper, S
CopyFileToTaskReq copyFileToTaskReq = new CopyFileToTaskReq();
copyFileToTaskReq.setSourceFileId(fileId);
// 设置tag 因为归档用户可以再选文件类型
copyFileToTaskReq.setFileTypeDictClass(req.getFileTypeDictClass());
copyFileToTaskReq.setFileTypeDictValue(req.getFileTypeDictValue());
copyFileToTaskReq.setDictTags(req.getDictTags());
Long parentDirId = getParentDirId(simulationRun.getTaskId(), fileBizTypeService.getFileName(String.valueOf(req.getFileType())));
copyFileToTaskReq.setParentDirId(parentDirId);
SdmResponse response = dataFeignClient.copyFileToTask(copyFileToTaskReq);
log.info("[syncKeyResultToTask] copyFileToTask req:{},resp:{}", JSON.toJSONString(copyFileToTaskReq), JSON.toJSONString(response));
if (!response.isSuccess()) {
return SdmResponse.failed("归档输入输出文件失败");
}

View File

@@ -701,4 +701,17 @@ public class SimulationTaskServiceImpl extends ServiceImpl<SimulationTaskMapper,
}
}
@Override
public List<String> getUuidsByTag1s(String tag1s) {
if (StringUtils.isBlank(tag1s)) {
return Collections.emptyList();
}
List<String> tag1List = Arrays.asList(tag1s.split(","));
LambdaQueryWrapper<SimulationTask> wrapper = new LambdaQueryWrapper<>();
wrapper.in(SimulationTask::getTag1, tag1List);
wrapper.select(SimulationTask::getUuid);
List<SimulationTask> tasks = this.list(wrapper);
return tasks.stream().map(SimulationTask::getUuid).collect(Collectors.toList());
}
}

View File

@@ -4029,6 +4029,47 @@ public class TaskServiceImpl implements ITaskService {
@Override
public SdmResponse getTaskAchieveStatistics(TaskCompleteStatisticsReq req) {
// 处理 discipline 和 tag1-tag10 字段,支持逗号分隔的多个值
// 注意tag6 用于过滤 discipline 列,所以将 tag6 和 discipline 合并到 disciplineList
List<String> disciplineList = new ArrayList<>();
if (StringUtils.isNotBlank(req.getDiscipline())) {
disciplineList.addAll(Arrays.asList(req.getDiscipline().split(",")));
}
if (StringUtils.isNotBlank(req.getTag6())) {
disciplineList.addAll(Arrays.asList(req.getTag6().split(",")));
}
if (!disciplineList.isEmpty()) {
req.setDisciplineList(disciplineList);
}
if (StringUtils.isNotBlank(req.getTag1())) {
req.setTag1List(Arrays.asList(req.getTag1().split(",")));
}
if (StringUtils.isNotBlank(req.getTag2())) {
req.setTag2List(Arrays.asList(req.getTag2().split(",")));
}
if (StringUtils.isNotBlank(req.getTag3())) {
req.setTag3List(Arrays.asList(req.getTag3().split(",")));
}
if (StringUtils.isNotBlank(req.getTag4())) {
req.setTag4List(Arrays.asList(req.getTag4().split(",")));
}
if (StringUtils.isNotBlank(req.getTag5())) {
req.setTag5List(Arrays.asList(req.getTag5().split(",")));
}
if (StringUtils.isNotBlank(req.getTag7())) {
req.setTag7List(Arrays.asList(req.getTag7().split(",")));
}
if (StringUtils.isNotBlank(req.getTag8())) {
req.setTag8List(Arrays.asList(req.getTag8().split(",")));
}
if (StringUtils.isNotBlank(req.getTag9())) {
req.setTag9List(Arrays.asList(req.getTag9().split(",")));
}
if (StringUtils.isNotBlank(req.getTag10())) {
req.setTag10List(Arrays.asList(req.getTag10().split(",")));
}
// 所有任务达成状态
Set<String> allAchieveStatus = new HashSet<>();
Long userId = ThreadLocalContext.getUserId();

View File

@@ -695,45 +695,65 @@
stm.user_id = #{userId}
AND task.tenant_id = #{tenantId}
AND task.exe_status is not null
<if test="req.tag1 != null and req.tag1 !='' ">
<bind name="searchKey1" value="'%' + req.tag1 + '%'"/>
and task.tag1 like #{searchKey1}
<if test="req.tag1List != null and req.tag1List.size() > 0">
and task.tag1 in
<foreach collection="req.tag1List" item="tag1Item" open="(" separator="," close=")">
#{tag1Item}
</foreach>
</if>
<if test="req.tag2 != null and req.tag2 !='' ">
<bind name="searchKey2" value="'%' + req.tag2 + '%'"/>
and task.tag2 like #{searchKey2}
<if test="req.tag2List != null and req.tag2List.size() > 0">
and task.tag2 in
<foreach collection="req.tag2List" item="tag2Item" open="(" separator="," close=")">
#{tag2Item}
</foreach>
</if>
<if test="req.tag3 != null and req.tag3 !='' ">
<bind name="searchKey3" value="'%' + req.tag3 + '%'"/>
and task.tag3 like #{searchKey3}
<if test="req.tag3List != null and req.tag3List.size() > 0">
and task.tag3 in
<foreach collection="req.tag3List" item="tag3Item" open="(" separator="," close=")">
#{tag3Item}
</foreach>
</if>
<if test="req.tag4 != null and req.tag4 !='' ">
<bind name="searchKey4" value="'%' + req.tag4 + '%'"/>
and task.tag4 like #{searchKey4}
<if test="req.tag4List != null and req.tag4List.size() > 0">
and task.tag4 in
<foreach collection="req.tag4List" item="tag4Item" open="(" separator="," close=")">
#{tag4Item}
</foreach>
</if>
<if test="req.tag5 != null and req.tag5 !='' ">
<bind name="searchKey5" value="'%' + req.tag5 + '%'"/>
and task.tag5 like #{searchKey5}
<if test="req.tag5List != null and req.tag5List.size() > 0">
and task.tag5 in
<foreach collection="req.tag5List" item="tag5Item" open="(" separator="," close=")">
#{tag5Item}
</foreach>
</if>
<if test="req.tag6 != null and req.tag6 !='' ">
<bind name="searchKey6" value="'%' + req.tag6 + '%'"/>
and task.discipline like #{searchKey6}
<if test="req.disciplineList != null and req.disciplineList.size() > 0">
and task.discipline in
<foreach collection="req.disciplineList" item="disciplineItem" open="(" separator="," close=")">
#{disciplineItem}
</foreach>
</if>
<if test="req.tag7 != null and req.tag7 !='' ">
<bind name="searchKey7" value="'%' + req.tag7 + '%'"/>
and task.tag7 like #{searchKey7}
<if test="req.tag7List != null and req.tag7List.size() > 0">
and task.tag7 in
<foreach collection="req.tag7List" item="tag7Item" open="(" separator="," close=")">
#{tag7Item}
</foreach>
</if>
<if test="req.tag8 != null and req.tag8 !='' ">
<bind name="searchKey8" value="'%' + req.tag8 + '%'"/>
and task.tag8 like #{searchKey8}
<if test="req.tag8List != null and req.tag8List.size() > 0">
and task.tag8 in
<foreach collection="req.tag8List" item="tag8Item" open="(" separator="," close=")">
#{tag8Item}
</foreach>
</if>
<if test="req.tag9 != null and req.tag9 !='' ">
<bind name="searchKey9" value="'%' + req.tag9 + '%'"/>
and task.tag9 like #{searchKey9}
<if test="req.tag9List != null and req.tag9List.size() > 0">
and task.tag9 in
<foreach collection="req.tag9List" item="tag9Item" open="(" separator="," close=")">
#{tag9Item}
</foreach>
</if>
<if test="req.tag10 != null and req.tag10 !='' ">
<bind name="searchKey10" value="'%' + req.tag10 + '%'"/>
and task.tag10 like #{searchKey10}
<if test="req.tag10List != null and req.tag10List.size() > 0">
and task.tag10 in
<foreach collection="req.tag10List" item="tag10Item" open="(" separator="," close=")">
#{tag10Item}
</foreach>
</if>
</where>
@@ -748,45 +768,65 @@
snm.user_id = #{userId}
AND task.tenant_id = #{tenantId}
AND task.exe_status is not null
<if test="req.tag1 != null and req.tag1 !='' ">
<bind name="searchKey1" value="'%' + req.tag1 + '%'"/>
and task.tag1 like #{searchKey1}
<if test="req.tag1List != null and req.tag1List.size() > 0">
and task.tag1 in
<foreach collection="req.tag1List" item="tag1Item" open="(" separator="," close=")">
#{tag1Item}
</foreach>
</if>
<if test="req.tag2 != null and req.tag2 !='' ">
<bind name="searchKey2" value="'%' + req.tag2 + '%'"/>
and task.tag2 like #{searchKey2}
<if test="req.tag2List != null and req.tag2List.size() > 0">
and task.tag2 in
<foreach collection="req.tag2List" item="tag2Item" open="(" separator="," close=")">
#{tag2Item}
</foreach>
</if>
<if test="req.tag3 != null and req.tag3 !='' ">
<bind name="searchKey3" value="'%' + req.tag3 + '%'"/>
and task.tag3 like #{searchKey3}
<if test="req.tag3List != null and req.tag3List.size() > 0">
and task.tag3 in
<foreach collection="req.tag3List" item="tag3Item" open="(" separator="," close=")">
#{tag3Item}
</foreach>
</if>
<if test="req.tag4 != null and req.tag4 !='' ">
<bind name="searchKey4" value="'%' + req.tag4 + '%'"/>
and task.tag4 like #{searchKey4}
<if test="req.tag4List != null and req.tag4List.size() > 0">
and task.tag4 in
<foreach collection="req.tag4List" item="tag4Item" open="(" separator="," close=")">
#{tag4Item}
</foreach>
</if>
<if test="req.tag5 != null and req.tag5 !='' ">
<bind name="searchKey5" value="'%' + req.tag5 + '%'"/>
and task.tag5 like #{searchKey5}
<if test="req.tag5List != null and req.tag5List.size() > 0">
and task.tag5 in
<foreach collection="req.tag5List" item="tag5Item" open="(" separator="," close=")">
#{tag5Item}
</foreach>
</if>
<if test="req.tag6 != null and req.tag6 !='' ">
<bind name="searchKey6" value="'%' + req.tag6 + '%'"/>
and task.discipline like #{searchKey6}
<if test="req.disciplineList != null and req.disciplineList.size() > 0">
and task.discipline in
<foreach collection="req.disciplineList" item="disciplineItem" open="(" separator="," close=")">
#{disciplineItem}
</foreach>
</if>
<if test="req.tag7 != null and req.tag7 !='' ">
<bind name="searchKey7" value="'%' + req.tag7 + '%'"/>
and task.tag7 like #{searchKey7}
<if test="req.tag7List != null and req.tag7List.size() > 0">
and task.tag7 in
<foreach collection="req.tag7List" item="tag7Item" open="(" separator="," close=")">
#{tag7Item}
</foreach>
</if>
<if test="req.tag8 != null and req.tag8 !='' ">
<bind name="searchKey8" value="'%' + req.tag8 + '%'"/>
and task.tag8 like #{searchKey8}
<if test="req.tag8List != null and req.tag8List.size() > 0">
and task.tag8 in
<foreach collection="req.tag8List" item="tag8Item" open="(" separator="," close=")">
#{tag8Item}
</foreach>
</if>
<if test="req.tag9 != null and req.tag9 !='' ">
<bind name="searchKey9" value="'%' + req.tag9 + '%'"/>
and task.tag9 like #{searchKey9}
<if test="req.tag9List != null and req.tag9List.size() > 0">
and task.tag9 in
<foreach collection="req.tag9List" item="tag9Item" open="(" separator="," close=")">
#{tag9Item}
</foreach>
</if>
<if test="req.tag10 != null and req.tag10 !='' ">
<bind name="searchKey10" value="'%' + req.tag10 + '%'"/>
and task.tag10 like #{searchKey10}
<if test="req.tag10List != null and req.tag10List.size() > 0">
and task.tag10 in
<foreach collection="req.tag10List" item="tag10Item" open="(" separator="," close=")">
#{tag10Item}
</foreach>
</if>
</where>
</select>

View File

@@ -81,6 +81,6 @@ public class SystemLogController implements ISysLogFeignClient {
*/
@PostMapping("/getDailyOperateStatistics")
public SdmResponse<List<DailyOperateStatsResp>> getDailyOperateStatistics(@RequestBody SysLogDTO sysLog) {
return logService.getDailyOperateStatistics(sysLog.getCreateTimeArr());
return logService.getDailyOperateStatistics(sysLog.getCreateTimeArr(), sysLog.getTag1());
}
}

View File

@@ -50,8 +50,9 @@ public interface ISysLogService extends IService<SysLog> {
/**
* 统计每日下载和预览次数
* @param createTimeArr 时间数组 [startDate, endDate]格式yyyy-MM-dd
* @param tag1 项目uuid列表(逗号分隔),用于筛选指定项目下的文件下载和预览次数
* @return 每日操作统计列表
*/
SdmResponse<List<DailyOperateStatsResp>> getDailyOperateStatistics(String[] createTimeArr);
SdmResponse<List<DailyOperateStatsResp>> getDailyOperateStatistics(String[] createTimeArr, String tag1);
}

View File

@@ -14,6 +14,8 @@ import com.sdm.common.entity.req.system.UserQueryReq;
import com.sdm.common.entity.resp.PageDataResp;
import com.sdm.common.entity.resp.system.CIDUserResp;
import com.sdm.common.feign.impl.system.SysUserFeignClientImpl;
import com.sdm.common.feign.impl.project.SimulationTaskFeignClientImpl;
import org.apache.commons.lang3.StringUtils;
import com.sdm.common.log.dto.SysLogDTO;
import com.sdm.common.utils.CidSysUserUtil;
import com.sdm.common.utils.PageUtils;
@@ -53,6 +55,9 @@ public class ISysLogServiceImpl extends ServiceImpl<SysLogMapper, SysLog> implem
@Autowired
SysUserFeignClientImpl sysUserFeignClient;
@Autowired
SimulationTaskFeignClientImpl simulationTaskFeignClient;
@Override
public SdmResponse<PageDataResp<List<SysLogDTO>>> getLogByPage(SysLogDTO sysLog) {
LambdaQueryWrapper<SysLog> wrapper = Wrappers.lambdaQuery();
@@ -323,7 +328,7 @@ public class ISysLogServiceImpl extends ServiceImpl<SysLogMapper, SysLog> implem
}
@Override
public SdmResponse<List<DailyOperateStatsResp>> getDailyOperateStatistics(String[] createTimeArr) {
public SdmResponse<List<DailyOperateStatsResp>> getDailyOperateStatistics(String[] createTimeArr, String tag1) {
Long tenantId = ThreadLocalContext.getTenantId();
// 解析时间参数
@@ -338,6 +343,21 @@ public class ISysLogServiceImpl extends ServiceImpl<SysLogMapper, SysLog> implem
wrapper.ge(SysLog::getCreateTime, startTime);
wrapper.le(SysLog::getCreateTime, endTime);
wrapper.in(SysLog::getOperateType, "download", "preview");
// 如果传入了tag1参数需要根据tag1筛选对应的任务uuid列表然后通过businessId筛选日志
List<String> taskUuids = null;
if (StringUtils.isNotBlank(tag1)) {
SdmResponse<List<String>> uuidsResponse = simulationTaskFeignClient.getUuidsByTag1s(tag1);
if (uuidsResponse.isSuccess() && CollectionUtils.isNotEmpty(uuidsResponse.getData())) {
taskUuids = uuidsResponse.getData();
wrapper.in(SysLog::getBusinessId, taskUuids);
} else {
// 如果没有找到对应的任务,返回空统计结果
taskUuids = Collections.emptyList();
wrapper.in(SysLog::getBusinessId, Collections.singletonList(""));
}
}
List<SysLog> logList = this.list(wrapper);
// 按日期分组统计