This commit is contained in:
2026-01-13 15:14:08 +08:00
21 changed files with 339 additions and 26 deletions

View File

@@ -87,7 +87,7 @@ public class ReportTemplateDto {
@Schema(description = "分页参数,一页几条")
private int size;
@Schema(description = "是否复制模板")
private boolean isCopied;
@Schema(description = "是否复制模板 Y/N")
private String copyFlag;
}

View File

@@ -12,19 +12,23 @@ import com.sdm.capability.service.ISimulationReportTemplateService;
import com.sdm.common.common.SdmResponse;
import com.sdm.common.common.ThreadLocalContext;
import com.sdm.common.entity.bo.DataPageInfo;
import com.sdm.common.entity.constants.CommonConstants;
import com.sdm.common.entity.constants.NumberConstants;
import com.sdm.common.entity.enums.ApproveStatusEnum;
import com.sdm.common.entity.enums.ApproveTypeEnum;
import com.sdm.common.entity.req.data.DelFileReq;
import com.sdm.common.entity.req.data.GetFileBaseInfoReq;
import com.sdm.common.entity.req.data.UpdateScriptAndReportReq;
import com.sdm.common.entity.req.data.UploadFilesReq;
import com.sdm.common.entity.req.system.LaunchApproveReq;
import com.sdm.common.entity.resp.data.FileMetadataInfoResp;
import com.sdm.common.feign.impl.data.DataClientFeignClientImpl;
import com.sdm.common.feign.impl.system.ApproveFeignClientImpl;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
@@ -54,12 +58,23 @@ public class SimulationReportTemplateServiceImpl extends ServiceImpl<SimulationR
reportTemplate.setTenantId(ThreadLocalContext.getTenantId());
// 复制模板的话 先根据fileId查到模板 再重新上传
MultipartFile file = null;
if (templateDto.isCopied()) {
SdmResponse<MultipartFile> response = dataFeignClient.getMultipartFileByFileId(templateDto.getFileId());
if (CommonConstants.GENERAL_Y.equals(templateDto.getCopyFlag())) {
GetFileBaseInfoReq getFileBaseInfoReq = new GetFileBaseInfoReq();
getFileBaseInfoReq.setFileId(templateDto.getFileId());
SdmResponse<FileMetadataInfoResp> fileBaseInfoResp = dataFeignClient.getFileBaseInfo(getFileBaseInfoReq);
if (!fileBaseInfoResp.isSuccess() || fileBaseInfoResp.getData() == null) {
return SdmResponse.failed("获取文件失败");
}
SdmResponse<byte[]> response = dataFeignClient.getMultipartFileByFileId(templateDto.getFileId());
if (!response.isSuccess()) {
return response;
}
file = response.getData();
file = new MockMultipartFile(
fileBaseInfoResp.getData().getOriginalName(),
fileBaseInfoResp.getData().getOriginalName(),
"application/octet-stream",
response.getData()
);
} else {
file = templateDto.getFile();
}

View File

@@ -26,7 +26,9 @@ public class CacheConfig {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Arrays.asList(
// 用户名缓存
new ConcurrentMapCache("userNames")
new ConcurrentMapCache("userNames"),
// 查询任务树列表缓存
new ConcurrentMapCache("taskTreeListCache")
// 可以添加其他缓存配置
));
return cacheManager;

View File

@@ -10,4 +10,8 @@ public class CommonConstants {
*/
public static final String ROLE_CODE_GENERAL = "GENERAL_USER";
public static final String GENERAL_Y = "Y";
public static final String GENERAL_N = "N";
}

View File

@@ -247,8 +247,8 @@ public class DataClientFeignClientImpl implements IDataFeignClient {
}
@Override
public SdmResponse<MultipartFile> getMultipartFileByFileId(Long fileId) {
SdmResponse<MultipartFile> response;
public SdmResponse<byte[]> getMultipartFileByFileId(Long fileId) {
SdmResponse<byte[]> response;
try {
response = dataClient.getMultipartFileByFileId(fileId);
return response;

View File

@@ -95,6 +95,6 @@ public interface IDataFeignClient {
SdmResponse<Long> updateReportTemplateFile(UpdateScriptAndReportReq req);
@GetMapping("/data/getMultipartFileByFileId")
SdmResponse<MultipartFile> getMultipartFileByFileId(@RequestParam(value = "fileId") @Validated Long fileId);
SdmResponse<byte[]> getMultipartFileByFileId(@RequestParam(value = "fileId") @Validated Long fileId);
}

View File

@@ -489,7 +489,7 @@ public class DataFileController implements IDataFeignClient {
@GetMapping("/getMultipartFileByFileId")
@Operation(summary = "根据fileId获取MultipartFile文件", description = "根据fileId获取MultipartFile文件")
public SdmResponse<MultipartFile> getMultipartFileByFileId(@RequestParam(value = "fileId") @Validated Long fileId) {
public SdmResponse<byte[]> getMultipartFileByFileId(@RequestParam(value = "fileId") @Validated Long fileId) {
return IDataFileService.getMultipartFileByFileId(fileId);
}

View File

@@ -357,7 +357,7 @@ public interface IDataFileService {
SdmResponse downloadFileForEdit(Long fileId);
SdmResponse<MultipartFile> getMultipartFileByFileId(Long fileId);
SdmResponse<byte[]> getMultipartFileByFileId(Long fileId);
default SdmResponse<List<FileMetadataInfoResp>> queryFileListByIdList(QueryFileReq queryFileReq){return null;}

View File

@@ -2996,7 +2996,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService {
}
@Override
public SdmResponse<MultipartFile> getMultipartFileByFileId(Long fileId) {
public SdmResponse<byte[]> getMultipartFileByFileId(Long fileId) {
try {
FileMetadataInfo fileMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getId, fileId).one();
if (ObjectUtils.isEmpty(fileMetadataInfo)) {
@@ -3005,14 +3005,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService {
String fileObjectKey = fileMetadataInfo.getObjectKey();
// 从MinIO下载文件
byte[] fileData = minioService.downloadFile(fileObjectKey,fileMetadataInfo.getBucketName());
String contentType = getContentTypeByFileName(fileMetadataInfo.getOriginalName());
MultipartFile multipartFile = new MockMultipartFile(
fileMetadataInfo.getOriginalName(),
fileMetadataInfo.getOriginalName(),
contentType,
fileData
);
return SdmResponse.success(multipartFile);
return SdmResponse.success(fileData);
} catch (Exception e) {
log.error("获取文件失败", e);
}

View File

@@ -1620,5 +1620,10 @@ public class SystemFileIDataFileServiceImpl implements IDataFileService {
return null;
}
@Override
public SdmResponse<byte[]> getMultipartFileByFileId(Long fileId) {
return null;
}
}

View File

@@ -108,4 +108,12 @@ public class LyricVProjectToDM {
@TableField(value = "machine_type")
public String machineType;
@Schema(description = "订单量")
@TableField(value = "order_quantity")
public Integer orderQuantity;
@Schema(description = "签单数量")
@TableField(value = "sign_num")
public Integer signNum;
}

View File

@@ -27,7 +27,7 @@ public class HkUploadFileReq {
public long componentInstId = 8000004142460000204L;
@Schema(description = "表名称")
public String tableName = "oa_threee_d_review";
public String tableName = "oa_three_d_review";
@Schema(description = "字段名称")
public String columnName = "simulation_table;";

View File

@@ -489,9 +489,6 @@ public class LyricIntegrateService {
return response;
}
public String getHkToken(String jobNo) {
return getHKCloudToken(jobNo);
}
}

View File

@@ -5,6 +5,7 @@ import com.sdm.common.entity.req.task.TaskExportExcelFormat;
import com.sdm.common.entity.req.task.TaskTreeExportExcelFormat;
import com.sdm.common.log.annotation.SysLog;
import com.sdm.project.model.bo.ModifyProjectNode;
import com.sdm.project.model.req.ProjectTreeListReq;
import com.sdm.project.model.req.ProjectTreeTagReq;
import com.sdm.project.model.req.SpdmNodeReq;
import com.sdm.project.service.IProjectService;
@@ -68,5 +69,18 @@ public class SimulationProjectController {
return taskService.newExportTaskTree(taskTreeExportExcelFormat, httpservletResponse);
}
/**
* 任务分析项树
*
* @param req
* @return
*/
@SysLog("获取多个项目的任务分析项树")
@PostMapping("/getTaskTreeList")
@Operation(summary = "获取多个项目的任务分析项树", description = "获取多个项目的任务分析项树")
public SdmResponse getTaskTreeList(@RequestBody ProjectTreeListReq req) {
return projectService.getTaskTreeList(req);
}
}

View File

@@ -181,4 +181,7 @@ public interface SimulationProjectMapper {
List<SimulationNodeResp> querySimulationNodeByUuids(@Param("list")List<String> uuids);
List<ProjectNodePo> getNodeByIdList(@Param("nodeIdList") List<Long> nodeIdList);
List<ProjectNodePo> getNodeListByType(@Param("nodeTypeList") List<String> nodeTypeList);
}

View File

@@ -0,0 +1,19 @@
package com.sdm.project.model.req;
import com.sdm.project.model.bo.TaskNodeTag;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
import java.util.List;
@Data
public class ProjectTreeListReq {
private String projectNodeId;
private String phaseNodeId;
@NotEmpty(message = "tagMap不能为空")
private List<TaskNodeTag> tagMap;
}

View File

@@ -28,4 +28,6 @@ public interface IProjectService {
SdmResponse exportTaskTree(TaskTreeExportExcelFormat taskTreeExportExcelFormat, HttpServletResponse httpservletResponse);
SdmResponse getTaskTreeList(ProjectTreeListReq req);
}

View File

@@ -1171,7 +1171,7 @@ public class LyricInternalServiceImpl implements ILyricInternalService {
uploadFileReq.setSysId(1691399963692630016L);
uploadFileReq.setFormId(1847115435993071616L);
uploadFileReq.setComponentInstId(8000004142460000204L);
uploadFileReq.setTableName("oa_threee_d_review");
uploadFileReq.setTableName("oa_three_d_review");
uploadFileReq.setColumnName("simulation_table");
uploadFileReq.setXmh(req.getProjectCode());
uploadFileReq.setGwh(req.getWorkspaceCode());
@@ -1435,7 +1435,7 @@ public class LyricInternalServiceImpl implements ILyricInternalService {
if (cidUserRespSdmResponse.getData() == null) {
return SdmResponse.failed("根据工号获取username失败");
}
String token = lyricIntegrateService.getHkToken(cidUserRespSdmResponse.getData().getUsername());
String token = lyricIntegrateService.getHKCloudToken(cidUserRespSdmResponse.getData().getUsername());
return SdmResponse.success(token);
}

View File

@@ -36,6 +36,7 @@ import com.sdm.project.model.req.*;
import com.sdm.project.model.vo.SpdmExportNewTaskVo;
import com.sdm.project.model.vo.SpdmNewTaskVo;
import com.sdm.project.model.vo.SpdmNodeExtraVo;
import com.sdm.project.model.vo.SpdmNodeVo;
import com.sdm.project.service.IProjectService;
import com.sdm.project.service.ISimulationPerformanceExtraService;
import com.sdm.project.service.ISimulationPerformanceService;
@@ -48,6 +49,8 @@ import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
@@ -139,6 +142,15 @@ public class ProjectServiceImpl extends BaseService implements IProjectService {
private static final String PERFORMANCE_CODE = "指标编号";
private static final String TAG1 = "tag1";
private static final String TAG2 = "tag2";
private static final String NODE_TYPE_PROJECT = NodeTypeEnum.PROJECT.getValue();
private static final String NODE_TYPE_PHASE = NodeTypeEnum.PHASE.getValue();
private static final List<TaskNodeTag> EMPTY_TAG_LIST = Collections.emptyList();
@Autowired
private CacheManager cacheManager;
@Override
@Transactional
public SdmResponse add(JSONObject jsonObject) {
@@ -2508,4 +2520,234 @@ public class ProjectServiceImpl extends BaseService implements IProjectService {
return projectNodePo;
}
/**
* 调用getTaskTree并统一处理返回结果
* @param req
* @return
*/
private List<NodeAllBase> getTaskTreeData(ProjectTreeTagReq req) {
if (req == null || CollectionUtils.isEmpty(req.getIdMap())) {
return new ArrayList<>();
}
// 构建缓存key唯一标识本次查询
String cacheKey = "taskTree_" + req.getIdMap().stream().map(t -> t.getKey() + "_" + t.getValue()).collect(Collectors.joining(","));
Cache cache = cacheManager.getCache("taskTreeListCache");
if (cache != null && cache.get(cacheKey) != null) {
// 缓存命中直接返回不调用getTaskTree()
log.info("--------------------------命中缓存--------------------------");
return (List<NodeAllBase>) cache.get(cacheKey).get();
}
// 缓存未命中,执行原逻辑
SdmResponse taskTree = getTaskTree(req);
if (taskTree == null || taskTree.getData() == null) {
return new ArrayList<>();
}
if (!(taskTree.getData() instanceof List)) {
return new ArrayList<>();
}
List<?> dataList = (List<?>) taskTree.getData();
if (dataList.stream().noneMatch(NodeAllBase.class::isInstance)) {
return new ArrayList<>();
}
List<NodeAllBase> result = dataList.stream().map(NodeAllBase.class::cast).collect(Collectors.toList());
// 存入缓存设置过期时间根据业务调整比如5分钟避免数据不一致
if (cache != null) {
cache.put(cacheKey, result);
}
return result;
}
public SdmResponse getTaskTreeList(ProjectTreeListReq req) {
// 1. 提取入参,统一变量
String projectNodeId = req.getProjectNodeId();
String phaseNodeId = req.getPhaseNodeId();
List<TaskNodeTag> tagMap = req.getTagMap();
// 2. 四种业务场景
if (StringUtils.isNotBlank(projectNodeId) && StringUtils.isNotBlank(phaseNodeId)) {
return handleBothIdsSpecified(projectNodeId, phaseNodeId, tagMap);
} else if (StringUtils.isBlank(projectNodeId) && StringUtils.isBlank(phaseNodeId)) {
return handleNoIdsSpecified(tagMap);
} else if (StringUtils.isBlank(projectNodeId)) {
return handleOnlyPhaseSpecified(phaseNodeId, tagMap);
} else if (StringUtils.isBlank(phaseNodeId)) {
return handleOnlyProjectSpecified(projectNodeId, tagMap);
}
return SdmResponse.success(Collections.emptyList());
}
/**
* 构建ProjectTreeTagReq请求对象
*/
private ProjectTreeTagReq buildProjectTreeTagReq(List<TaskNodeTag> idMapList, List<TaskNodeTag> tagMap) {
ProjectTreeTagReq req = new ProjectTreeTagReq();
req.setIdMap(CollectionUtils.isEmpty(idMapList) ? EMPTY_TAG_LIST : idMapList);
req.setTagMap(CollectionUtils.isEmpty(tagMap) ? EMPTY_TAG_LIST : tagMap);
return req;
}
/**
* 创建单个TaskNodeTag对象
*/
private TaskNodeTag createTaskNodeTag(String key, String value) {
TaskNodeTag tag = new TaskNodeTag();
tag.setKey(key);
tag.setValue(value);
return tag;
}
private void batchSetNodeExtras(List<ProjectNodePo> nodeList) {
if (CollectionUtils.isEmpty(nodeList)) {
return;
}
// 1. 提取所有需要查询的节点UUID
List<String> nodeUuids = nodeList.stream()
.map(ProjectNodePo::getUuid)
.filter(StringUtils::isNotBlank)
.distinct() // 去重避免重复查询相同UUID
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(nodeUuids)) {
return;
}
// 2. 批量查询所有扩展信息
List<SpdmNodeExtraVo> allExtras = nodeMapper.getNodeExtraListByNodeIdList(nodeUuids);
if (CollectionUtils.isEmpty(allExtras)) {
return;
}
// 3. 转Map映射
Map<String, List<SpdmNodeExtraVo>> extraMap = allExtras.stream()
.collect(Collectors.groupingBy(SpdmNodeExtraVo::getNodeId));
// 4. 批量赋值
nodeList.forEach(node -> node.setExtras(extraMap.getOrDefault(node.getUuid(), new ArrayList<>())));
}
/**
* 场景1项目ID+阶段ID 都传的情况
*/
private SdmResponse handleBothIdsSpecified(String projectNodeId, String phaseNodeId, List<TaskNodeTag> tagMap) {
// 构建查询参数 - 复用常量,减少对象创建
List<TaskNodeTag> idMap = Arrays.asList(
createTaskNodeTag(projectNodeId, TAG1),
createTaskNodeTag(phaseNodeId, TAG2)
);
ProjectTreeTagReq req = buildProjectTreeTagReq(idMap, tagMap);
// 获取任务树数据
List<NodeAllBase> nodeAllBaseList = getTaskTreeData(req);
if (CollectionUtils.isEmpty(nodeAllBaseList)) {
return SdmResponse.success(Collections.emptyList());
}
// 查询节点信息
List<String> nodeIds = Arrays.asList(projectNodeId, phaseNodeId);
List<ProjectNodePo> nodeList = mapper.getNodeListByNodeIdList(nodeIds);
if (CollectionUtils.isEmpty(nodeList) || nodeList.size() != 2) {
log.error("【双ID查询】节点数量异常, projectNodeId={}, phaseNodeId={}, 数量={}", projectNodeId, phaseNodeId, nodeList.size());
return SdmResponse.success(Collections.emptyList());
}
// 流式过滤+Optional处理
ProjectNodePo projectNode = nodeList.stream().filter(node -> NODE_TYPE_PROJECT.equals(node.getNodeType())).findFirst().orElse(null);
ProjectNodePo phaseNode = nodeList.stream().filter(node -> NODE_TYPE_PHASE.equals(node.getNodeType())).findFirst().orElse(null);
if (projectNode == null || phaseNode == null) {
log.error("【双ID查询】未匹配到对应类型节点, projectNodeId={}, phaseNodeId={}", projectNodeId, phaseNodeId);
return SdmResponse.success(Collections.emptyList());
}
// 组装树形结构 + 【批量赋值扩展信息】仅1次查询
batchSetNodeExtras(Arrays.asList(projectNode, phaseNode));
phaseNode.setChildren(nodeAllBaseList);
projectNode.setChildren(Collections.singletonList(phaseNode));
return SdmResponse.success(Collections.singletonList(projectNode));
}
/**
* 场景2项目ID+阶段ID 都不传的情况(查所有项目)
*/
private SdmResponse handleNoIdsSpecified(List<TaskNodeTag> tagMap) {
List<ProjectNodePo> projectNodeList = mapper.getNodeListByType(Collections.singletonList(NODE_TYPE_PROJECT));
if (CollectionUtils.isEmpty(projectNodeList)) {
log.error("【全量查询】未查询到任何项目节点");
return SdmResponse.success(Collections.emptyList());
}
// 批量设置扩展信息
batchSetNodeExtras(projectNodeList);
projectNodeList.forEach(projectNode -> {
List<TaskNodeTag> idMap = Collections.singletonList(createTaskNodeTag(projectNode.getUuid(), TAG1));
ProjectTreeTagReq req = buildProjectTreeTagReq(idMap, tagMap);
List<NodeAllBase> children = getTaskTreeData(req);
if (!CollectionUtils.isEmpty(children)) {
projectNode.setChildren(children);
}
});
return SdmResponse.success(projectNodeList);
}
/**
* 场景3只传阶段ID不传项目ID查所有项目的指定阶段
*/
private SdmResponse handleOnlyPhaseSpecified(String phaseNodeId, List<TaskNodeTag> tagMap) {
List<ProjectNodePo> phaseNodeList = mapper.getNodeListByNodeIdList(Collections.singletonList(phaseNodeId));
if (CollectionUtils.isEmpty(phaseNodeList)) {
log.error("【仅阶段查询】未查询到阶段节点, phaseNodeId={}", phaseNodeId);
return SdmResponse.success(Collections.emptyList());
}
// 提取项目ID+批量查询项目节点 + 转MAP
List<String> projectIdList = phaseNodeList.stream()
.map(ProjectNodePo::getTag1)
.filter(StringUtils::isNotBlank)
.distinct()
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(projectIdList)) {
log.error("【仅阶段查询】无关联的项目ID, phaseNodeId={}", phaseNodeId);
return SdmResponse.success(Collections.emptyList());
}
List<ProjectNodePo> projectNodeList = mapper.getNodeListByNodeIdList(projectIdList);
if (CollectionUtils.isEmpty(projectNodeList)) {
log.error("【仅阶段查询】未查询到关联项目节点, projectIdList={}", projectIdList);
return SdmResponse.success(Collections.emptyList());
}
Map<String, ProjectNodePo> projectNodeMap = projectNodeList.stream()
.collect(Collectors.toMap(ProjectNodePo::getUuid, p -> p, (k1, k2) -> k1)); // 解决重复key问题
// 批量设置所有节点的扩展信息
batchSetNodeExtras(phaseNodeList);
batchSetNodeExtras(projectNodeList);
phaseNodeList.forEach(phaseNode -> {
List<TaskNodeTag> idMap = Arrays.asList(
createTaskNodeTag(phaseNode.getUuid(), TAG1),
createTaskNodeTag(phaseNode.getUuid(), TAG2)
);
ProjectTreeTagReq req = buildProjectTreeTagReq(idMap, tagMap);
List<NodeAllBase> children = getTaskTreeData(req);
if (!CollectionUtils.isEmpty(children)) {
phaseNode.setChildren(children);
}
// MAP直接取值O(1)效率
ProjectNodePo projectNode = projectNodeMap.get(phaseNode.getTag1());
if (projectNode != null) {
projectNode.setChildren(Collections.singletonList(phaseNode));
}
});
return SdmResponse.success(projectNodeList);
}
/**
* 场景4只传项目ID不传阶段ID查指定项目的全量数据
*/
private SdmResponse handleOnlyProjectSpecified(String projectNodeId, List<TaskNodeTag> tagMap) {
List<ProjectNodePo> projectNodeList = mapper.getNodeListByNodeIdList(Collections.singletonList(projectNodeId));
if (CollectionUtils.isEmpty(projectNodeList)) {
log.error("【仅项目查询】未查询到项目节点, projectNodeId={}", projectNodeId);
return SdmResponse.success(Collections.emptyList());
}
// 批量设置扩展信息
batchSetNodeExtras(projectNodeList);
projectNodeList.forEach(projectNode -> {
List<TaskNodeTag> idMap = Collections.singletonList(createTaskNodeTag(projectNode.getUuid(), TAG1));
ProjectTreeTagReq req = buildProjectTreeTagReq(idMap, tagMap);
List<NodeAllBase> children = getTaskTreeData(req);
if (!CollectionUtils.isEmpty(children)) {
projectNode.setChildren(children);
}
});
return SdmResponse.success(projectNodeList);
}
}

View File

@@ -124,6 +124,7 @@ security:
- /run/getSimulationKeyResultFileIds
- /run/generateReportInternal
- /dataManager/tree/node/listUserByIds
- /node/updateApprovalStatus
#logging:
# config: ./config/logback.xml

View File

@@ -713,6 +713,14 @@
)
</select>
<select id="getNodeListByType" resultType="com.sdm.project.model.po.ProjectNodePo">
select * from simulation_node where nodeType in (
<foreach collection='nodeTypeList' item='nodeType' index='index' separator=','>
#{nodeType}
</foreach>
)
</select>
<!-- <select id="getTaskList" resultType="com.sdm.project.entity.vo.SpdmTaskVo">-->
<!-- select * from simulation_task where tenant_id = #{tenantId}-->
<!-- <if test="taskName != null and taskName != ''">-->