Merge remote-tracking branch 'origin/main'

This commit is contained in:
2026-01-24 17:27:44 +08:00
5 changed files with 820 additions and 519 deletions

View File

@@ -0,0 +1 @@
ALTER TABLE simulation_node ADD referenceItem varchar(100) NULL COMMENT '参考项目';

View File

@@ -0,0 +1,47 @@
package com.sdm.project.config.thread;
import com.sdm.common.mdc.MdcTaskDecorator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 用于批量创建需求、需求拓展属性、更新权限(最好直接使用批量更新接口)使用的线程池
*/
@Configuration
public class ProjectTaskThreadPool {
@Value("${projectTaskPool.coreSize:20}")
private int CORE_POOL_SIZE ;
@Value("${projectTaskPool.maxSize:60}")
private int MAX_POOL_SIZE ;
@Value("${projectTaskPool.queueSize:5000}")
private int QUEUE_CAPACITY ;
@Value("${projectTaskPool.keepLive:60}")
private int KEEP_ALIVE_SECONDS ;
@Value("${projectTaskPool.threadName:projectTaskPool-}")
private String THREAD_NAME_PREFIX ;
@Bean(name = "projectTaskThreadPoolExecutor")
public Executor projectTaskThreadPoolExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(CORE_POOL_SIZE);
executor.setMaxPoolSize(MAX_POOL_SIZE);
executor.setQueueCapacity(QUEUE_CAPACITY);
executor.setKeepAliveSeconds(KEEP_ALIVE_SECONDS);
executor.setThreadNamePrefix(THREAD_NAME_PREFIX);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setTaskDecorator(new MdcTaskDecorator());
executor.initialize();
return executor;
}
}

View File

@@ -155,7 +155,7 @@ public class SimulationNode implements Serializable {
@TableField("commitmentDeadline")
private String commitmentDeadline;
@TableField("reference_item")
@TableField("referenceItem")
private String referenceItem;
}

View File

@@ -39,6 +39,7 @@ import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
@@ -52,6 +53,7 @@ import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@@ -70,6 +72,12 @@ public class LyricInternalServiceImpl implements ILyricInternalService {
private static final String ZIP_SUFFIX = ".zip";
private static final String CACHE_NAME_SYNC_TODO = "syncTodo";
private static final String CACHE_KEY_TODO = "todo";
private static final long SYNC_INTERVAL_MS = 30 * 1000L;
private static final String DATE_FORMAT_PATTERN = "yyyy-MM-dd HH:mm:ss";
private static final int UUID_LENGTH = 32;
@Autowired
private LyricIntegrateService lyricIntegrateService;
@@ -112,6 +120,10 @@ public class LyricInternalServiceImpl implements ILyricInternalService {
@Autowired
private CacheManager cacheManager;
@Autowired
@Qualifier("projectTaskThreadPoolExecutor")
private Executor projectTaskThreadPoolExecutor;
/**
* 判断字符串是否可以安全转换为Long类型
*
@@ -186,374 +198,385 @@ public class LyricInternalServiceImpl implements ILyricInternalService {
return lyricIntegrateService.getProcessData(req);
}
@Override
public SdmResponse getTodoList() {
Cache todoCache = cacheManager.getCache("syncTodo");
/**
* 校验同步缓存:控制同步频率
*/
private SdmResponse checkSyncCache() {
Cache todoCache = cacheManager.getCache(CACHE_NAME_SYNC_TODO);
if (todoCache == null) {
return SdmResponse.failed("未查询到缓存");
}
boolean flag = false;
if (todoCache.get("todo") == null) {
todoCache.put("todo",System.currentTimeMillis());
flag = true;
boolean isFirstSync = todoCache.get(CACHE_KEY_TODO) == null;
if (isFirstSync) {
todoCache.put(CACHE_KEY_TODO, System.currentTimeMillis());
return null;
}
Long lastSyncTime = (Long) todoCache.get("todo").get();
if (!flag && ObjectUtils.isNotEmpty(lastSyncTime) && System.currentTimeMillis() - lastSyncTime < 30 * 1000) {
Long lastSyncTime = (Long) todoCache.get(CACHE_KEY_TODO).get();
if (ObjectUtils.isNotEmpty(lastSyncTime)
&& System.currentTimeMillis() - lastSyncTime < SYNC_INTERVAL_MS) {
return SdmResponse.failed("刚刚已同步过需求,请稍后再试");
}
todoCache.put("todo",System.currentTimeMillis());
List<SimulationNode> nodeList = nodeService.lambdaQuery()
.eq(SimulationNode::getNodeType,NodeTypeEnum.PROJECT.getValue()).eq(SimulationNode::getProjectSource, SYNC_PROJECT_SOURCE).list();
if (CollectionUtils.isEmpty(nodeList)) {
log.info("getTodoList中nodeList为空");
return SdmResponse.success();
}
List<String> nodeCodeList = nodeList.stream().map(SimulationNode::getNodeCode).toList();
log.info("nodeCodeList为{}", nodeCodeList);
if (CollectionUtils.isEmpty(nodeCodeList)) {
log.info("getTodoList中nodeCodeList为空");
return SdmResponse.success();
}
// 查询
List<LyricVTodoEmulationInfoDM> todoInfoList = lyricVTodoInfoService.lambdaQuery().in(LyricVTodoEmulationInfoDM::getProject, nodeCodeList)
// 更新同步时间
todoCache.put(CACHE_KEY_TODO, System.currentTimeMillis());
return null;
}
/**
* 查询有效的项目节点列表
*/
private List<SimulationNode> queryProjectNodeList() {
return nodeService.lambdaQuery()
.eq(SimulationNode::getNodeType, NodeTypeEnum.PROJECT.getValue())
.eq(SimulationNode::getProjectSource, SYNC_PROJECT_SOURCE)
.list()
.stream()
.filter(node -> StringUtils.isNotBlank(node.getNodeCode())) // 过滤无效编码
.collect(Collectors.toList());
}
/**
* 查询待办数据并过滤已同步的数据
*/
private List<LyricVTodoEmulationInfoDM> queryAndFilterTodoList(List<String> projectCodeList) {
// 查询原始待办数据
List<LyricVTodoEmulationInfoDM> todoInfoList = lyricVTodoInfoService.lambdaQuery()
.in(LyricVTodoEmulationInfoDM::getProject, projectCodeList)
.list();
// 过滤掉数据库中已经有的需求
if (CollectionUtils.isEmpty(todoInfoList)) {
log.info("手动同步待办");
return SdmResponse.success();
log.info("查询到待同步待办数据");
return Collections.emptyList();
}
log.info("手动同步到{}条待办", todoInfoList.size());
// 过滤已同步的待办
List<SpdmDemandVo> allDemandList = demandMapper.getAllList();
log.info("allDemandList:{}", allDemandList);
if (CollectionUtils.isNotEmpty(allDemandList)) {
List<String> demandCodeList = allDemandList.stream().map(SpdmDemandVo::getDemandCode).toList();
todoInfoList = todoInfoList.stream().filter(todoInfo -> !demandCodeList.contains(String.valueOf(todoInfo.getTodoId()))).toList();
if (CollectionUtils.isEmpty(todoInfoList)) {
log.info("本次手动同步到的待办都已经进行过同步");
return SdmResponse.success();
}
if (CollectionUtils.isEmpty(allDemandList)) {
return todoInfoList;
}
Set<String> existDemandCodeSet = allDemandList.stream()
.map(SpdmDemandVo::getDemandCode)
.collect(Collectors.toSet());
List<LyricVTodoEmulationInfoDM> filterTodoList = todoInfoList.stream()
.filter(todoInfo -> !existDemandCodeSet.contains(String.valueOf(todoInfo.getTodoId())))
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(filterTodoList)) {
log.info("本次手动同步到的待办都已经进行过同步");
}
return filterTodoList;
}
/**
* 核心同步逻辑:构建数据并执行批量操作
*/
private SdmResponse syncTodoData(List<SimulationNode> projectNodeList,
List<LyricVTodoEmulationInfoDM> todoInfoList) {
Long tenantId = ThreadLocalContext.getTenantId();
Long jobNumber = ThreadLocalContext.getUserId();
String uuid;
String curDateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
String curDateStr = new SimpleDateFormat(DATE_FORMAT_PATTERN).format(new Date());
// 构建批量创建文件夹参数 + 权限更新参数
List<BatchCreateDirItem> createDirItemList = new ArrayList<>();
List<UpdatePermissionReq> currentUpdatePermissionList = new ArrayList<>();
for (LyricVTodoEmulationInfoDM emulation : todoInfoList) {
List<UpdatePermissionReq> updatePermissionList = new ArrayList<>();
// 按项目分组处理待办
Map<String, List<LyricVTodoEmulationInfoDM>> projectTodoMap = todoInfoList.stream()
.collect(Collectors.groupingBy(LyricVTodoEmulationInfoDM::getProject));
for (SimulationNode projectNode : projectNodeList) {
String projectCode = projectNode.getNodeCode();
List<LyricVTodoEmulationInfoDM> projectTodoList = projectTodoMap.getOrDefault(projectCode, Collections.emptyList());
if (CollectionUtils.isEmpty(projectTodoList)) {
continue;
}
// 构建项目级文件夹参数
BatchCreateDirItem projectCreateItem = buildProjectDirItem(projectNode);
List<DirNodeInfo> demandDirNodeList = new ArrayList<>();
// 处理当前项目下的所有待办
for (LyricVTodoEmulationInfoDM todo : projectTodoList) {
try {
String demandUuid = RandomUtil.generateString(UUID_LENGTH);
// 1. 构建需求基础参数 + 成员 + 权限
SpdmAddDemandReq demandReq = buildDemandReq(todo, demandUuid, curDateStr);
List<SpdmDemandRelateMemberReq> memberList = buildDemandMemberList(todo, demandUuid, jobNumber, curDateStr);
// 2. 构建权限更新参数
buildPermissionReqList(demandUuid, jobNumber, todo, updatePermissionList);
// 3. 构建需求文件夹节点
demandDirNodeList.add(buildDemandDirNode(demandUuid, projectNode, demandReq.getDemandName()));
// 4. 异步保存需求数据
asyncSaveDemandData(demandReq, memberList, tenantId, jobNumber);
} catch (Exception e) {
log.error("处理项目[{}]下待办[{}]异常: ", projectCode, todo.getTodoId(), e);
}
}
// 绑定需求文件夹到项目
projectCreateItem.setChildDirNodeInfos(demandDirNodeList);
createDirItemList.add(projectCreateItem);
}
// 执行批量操作
executeBatchOperations(createDirItemList, updatePermissionList);
return SdmResponse.success();
}
/**
* 构建项目级文件夹创建参数
*/
private BatchCreateDirItem buildProjectDirItem(SimulationNode projectNode) {
BatchCreateDirItem item = new BatchCreateDirItem();
DirNodeInfo projectDirNode = new DirNodeInfo();
projectDirNode.setUuId(projectNode.getUuid());
projectDirNode.setParentUuId(null);
projectDirNode.setUuIdOwnType(NodeTypeEnum.PROJECT.getValue());
projectDirNode.setDirName(projectNode.getNodeName());
item.setParentDirNodeInfo(projectDirNode);
return item;
}
/**
* 构建需求基础参数
*/
private SpdmAddDemandReq buildDemandReq(LyricVTodoEmulationInfoDM todo,
String demandUuid,
String curDateStr) {
SpdmAddDemandReq req = new SpdmAddDemandReq();
req.setUuid(demandUuid);
req.setDemandName(todo.getSubject());
req.setDemandCode(ObjectUtils.isNotEmpty(todo.getTodoId()) ? String.valueOf(todo.getTodoId()) : null);
req.setDemandType(todo.getEmulationType());
req.setSimType(todo.getRelevanceTask());
req.setDemandStatus("0");
req.setAchieveStatus("0");
req.setProgress(0);
req.setBeginTime(todo.getPlanStartTime());
req.setEndTime(todo.getClosedTime());
req.setCreateTime(curDateStr);
req.setDemandSource(SYNC_PROJECT_SOURCE);
// 补充项目/阶段/工位ID
fillProjectPhaseWorkspaceId(req, todo);
return req;
}
/**
* 填充需求的项目/阶段/工位ID
*/
private void fillProjectPhaseWorkspaceId(SpdmAddDemandReq req, LyricVTodoEmulationInfoDM todo) {
List<SimulationNode> allNodeList = nodeService.lambdaQuery()
.in(SimulationNode::getNodeCode, Arrays.asList(todo.getProject(), todo.getProjectStage(), todo.getStationNum()))
.list();
if (CollectionUtils.isEmpty(allNodeList)) {
return;
}
allNodeList.stream()
.filter(node -> NodeTypeEnum.PROJECT.getValue().equals(node.getNodeType())
&& node.getNodeCode().equals(todo.getProject()))
.findFirst()
.ifPresent(node -> req.setProjectId(node.getUuid()));
allNodeList.stream()
.filter(node -> NodeTypeEnum.PHASE.getValue().equals(node.getNodeType())
&& node.getNodeCode().equals(todo.getProjectStage()))
.findFirst()
.ifPresent(node -> req.setPhaseId(node.getUuid()));
allNodeList.stream()
.filter(node -> NodeTypeEnum.WORKSPACE.getValue().equals(node.getNodeType())
&& node.getNodeCode().equals(todo.getStationNum()))
.findFirst()
.ifPresent(node -> req.setNodeId(node.getUuid()));
}
/**
* 构建需求关联成员列表
*/
private List<SpdmDemandRelateMemberReq> buildDemandMemberList(LyricVTodoEmulationInfoDM todo,
String demandUuid,
Long jobNumber,
String curDateStr) {
List<SpdmDemandRelateMemberReq> memberList = new ArrayList<>();
// 仿真负责人
addDemandMember(todo.getEmulationPerformer(), 0, demandUuid, jobNumber, curDateStr, memberList);
// 仿真执行人
addDemandMember(todo.getEmulationExecutor(), 1, demandUuid, jobNumber, curDateStr, memberList);
// 3D负责人
addDemandMember(todo.getThreeDimensionalPerformer(), 2, demandUuid, jobNumber, curDateStr, memberList);
return memberList;
}
/**
* 通用添加需求成员方法
*/
private void addDemandMember(String memberStr,
int type,
String demandUuid,
Long jobNumber,
String curDateStr,
List<SpdmDemandRelateMemberReq> memberList) {
if (StringUtils.isBlank(memberStr)) {
return;
}
SpdmDemandRelateMemberReq memberReq = new SpdmDemandRelateMemberReq();
memberReq.setDemandId(demandUuid);
memberReq.setType(type);
memberReq.setCreateTime(curDateStr);
memberReq.setCreator(jobNumber);
String userIdStr = memberStr.split("-")[0];
if (isConvertibleToLong(userIdStr)) {
memberReq.setUserId(Long.valueOf(userIdStr));
}
memberList.add(memberReq);
}
/**
* 构建权限更新请求列表
*/
private void buildPermissionReqList(String demandUuid,
Long currentUserId,
LyricVTodoEmulationInfoDM todo,
List<UpdatePermissionReq> permissionList) {
// 当前用户权限
permissionList.add(buildUpdatePermissionReq(demandUuid, currentUserId));
// 仿真负责人权限
addPermissionReq(todo.getEmulationPerformer(), demandUuid, permissionList);
// 仿真执行人权限
addPermissionReq(todo.getEmulationExecutor(), demandUuid, permissionList);
}
/**
* 构建单个权限更新请求
*/
private UpdatePermissionReq buildUpdatePermissionReq(String uuid, Long userId) {
UpdatePermissionReq req = new UpdatePermissionReq();
req.setUserId(userId);
req.setUuid(uuid);
Map<Long, Byte> permissions = new HashMap<>();
permissions.put(userId, FilePermissionEnum.ALL.getValue());
req.setUserPermissions(permissions);
return req;
}
/**
* 添加成员权限请求
*/
private void addPermissionReq(String memberStr, String demandUuid, List<UpdatePermissionReq> permissionList) {
if (StringUtils.isBlank(memberStr)) {
return;
}
String userIdStr = memberStr.split("-")[0];
if (isConvertibleToLong(userIdStr)) {
permissionList.add(buildUpdatePermissionReq(demandUuid, Long.valueOf(userIdStr)));
}
}
/**
* 构建需求文件夹节点
*/
private DirNodeInfo buildDemandDirNode(String demandUuid,
SimulationNode projectNode,
String demandName) {
DirNodeInfo dirNode = new DirNodeInfo();
dirNode.setUuId(demandUuid);
dirNode.setParentUuId(projectNode.getUuid());
dirNode.setUuIdOwnType(NodeTypeEnum.DEMAND.getValue());
dirNode.setDirName(demandName);
return dirNode;
}
/**
* 异步保存需求数据(基础信息+成员+扩展属性)
*/
private void asyncSaveDemandData(SpdmAddDemandReq demandReq,
List<SpdmDemandRelateMemberReq> memberList,
Long tenantId,
Long jobNumber) {
CompletableFuture.runAsync(() -> {
try {
List<SpdmDemandExtraReq> extras = new ArrayList<>();
List<SpdmDemandRelateMemberReq> allMemberList = new ArrayList<>();
// 需求的基础属性
SpdmAddDemandReq spdmAddDemandReq = new SpdmAddDemandReq();
uuid = RandomUtil.generateString(32);
spdmAddDemandReq.setUuid(uuid);
spdmAddDemandReq.setDemandName(emulation.getSubject());
if (ObjectUtils.isNotEmpty(emulation.getTodoId())) {
spdmAddDemandReq.setDemandCode(String.valueOf(emulation.getTodoId()));
}
spdmAddDemandReq.setDemandType(emulation.getEmulationType());
spdmAddDemandReq.setSimType(emulation.getRelevanceTask());
spdmAddDemandReq.setDemandStatus("0");
spdmAddDemandReq.setAchieveStatus("0");
spdmAddDemandReq.setProgress(0);
spdmAddDemandReq.setBeginTime(emulation.getPlanStartTime());
spdmAddDemandReq.setEndTime(emulation.getClosedTime());
List<String> projectCodeList = todoInfoList.stream().map(LyricVTodoEmulationInfoDM::getProject).filter(StringUtils::isNotBlank).toList();
List<SimulationNode> allNodeList = new ArrayList<>();
if (CollectionUtils.isNotEmpty(projectCodeList)) {
List<SimulationNode> allProjectNodeList = nodeService.lambdaQuery().in(SimulationNode::getNodeCode, projectCodeList).list();
if (CollectionUtils.isNotEmpty(allNodeList)) {
List<String> projectIdList = allProjectNodeList.stream().map(SimulationNode::getUuid).toList();
allNodeList = nodeService.lambdaQuery().in(SimulationNode::getTag1,projectIdList).list();
}
}
if (CollectionUtils.isNotEmpty(allNodeList)) {
String projectNode = emulation.getProject();
String phaseNode = emulation.getProjectStage();
String workspaceNode = emulation.getStationNum();
Optional<SimulationNode> projectOptional = allNodeList.stream().filter(node -> node.getNodeCode().equals(projectNode)
&& NodeTypeEnum.PROJECT.getValue().equals(node.getNodeType())).findFirst();
Optional<SimulationNode> phaseOptional = allNodeList.stream().filter(node -> node.getNodeCode().equals(phaseNode)
&& NodeTypeEnum.PHASE.getValue().equals(node.getNodeType())).findFirst();
Optional<SimulationNode> workspaceOptional = allNodeList.stream().filter(node -> node.getNodeCode().equals(workspaceNode)
&& NodeTypeEnum.WORKSPACE.getValue().equals(node.getNodeType())).findFirst();
// 转换为project的uuid
projectOptional.ifPresent(simulationNode -> spdmAddDemandReq.setProjectId(simulationNode.getUuid()));
phaseOptional.ifPresent(simulationNode -> spdmAddDemandReq.setPhaseId(simulationNode.getUuid()));
workspaceOptional.ifPresent(simulationNode -> spdmAddDemandReq.setNodeId(simulationNode.getUuid()));
}
spdmAddDemandReq.setCreateTime(curDateStr);
spdmAddDemandReq.setDemandSource(SYNC_PROJECT_SOURCE);
// 需求的额外属性
// SpdmDemandExtraReq extraReq1 = new SpdmDemandExtraReq();
// extraReq1.setDemandId(uuid);
// extraReq1.setPropertyName("emulationResult");
// extraReq1.setPropertyValue(emulation.getEmulationResult());
// extras.add(extraReq1);
// SpdmDemandExtraReq extraReq2 = new SpdmDemandExtraReq();
// extraReq2.setDemandId(uuid);
// extraReq2.setPropertyName("robotBrand");
// extraReq2.setPropertyValue(emulation.getRobotBrand());
// extras.add(extraReq2);
// SpdmDemandExtraReq extraReq3 = new SpdmDemandExtraReq();
// extraReq3.setDemandId(uuid);
// extraReq3.setPropertyName("axis");
// extraReq3.setPropertyValue(emulation.getAxis());
// extras.add(extraReq3);
// SpdmDemandExtraReq extraReq4 = new SpdmDemandExtraReq();
// extraReq4.setDemandId(uuid);
// extraReq4.setPropertyName("beatRequirements");
// extraReq4.setPropertyValue(emulation.getBeatRequirements());
// extras.add(extraReq4);
// SpdmDemandExtraReq extraReq5 = new SpdmDemandExtraReq();
// extraReq5.setDemandId(uuid);
// extraReq5.setPropertyName("threeDimensionalReposito");
// extraReq5.setPropertyValue(emulation.getThreeDimensionalRepositoryPath());
// extras.add(extraReq5);
// SpdmDemandExtraReq extraReq6 = new SpdmDemandExtraReq();
// extraReq6.setDemandId(uuid);
// extraReq6.setPropertyName("resultPath");
// extraReq6.setPropertyValue(emulation.getResultPath());
// extras.add(extraReq6);
// SpdmDemandExtraReq extraReq7 = new SpdmDemandExtraReq();
// extraReq7.setDemandId(uuid);
// extraReq7.setPropertyName("resultDescribe");
// extraReq7.setPropertyValue(emulation.getResultDescribe());
// extras.add(extraReq7);
// SpdmDemandExtraReq extraReq8 = new SpdmDemandExtraReq();
// extraReq8.setDemandId(uuid);
// extraReq8.setPropertyName("analysisType");
// extraReq8.setPropertyValue(emulation.getAnalysisType());
// extras.add(extraReq8);
// SpdmDemandExtraReq extraReq11 = new SpdmDemandExtraReq();
// extraReq11.setDemandId(uuid);
// extraReq11.setPropertyName("software");
// extraReq11.setPropertyValue(emulation.getSoftware());
// extras.add(extraReq11);
// SpdmDemandExtraReq extraReq12 = new SpdmDemandExtraReq();
// extraReq12.setDemandId(uuid);
// extraReq12.setPropertyName("type");
// extraReq12.setPropertyValue(emulation.getType());
// extras.add(extraReq12);
// SpdmDemandExtraReq extraReq13 = new SpdmDemandExtraReq();
// extraReq13.setDemandId(uuid);
// extraReq13.setPropertyName("resultFileId");
// extraReq13.setPropertyValue(emulation.getResultFileId());
// extras.add(extraReq13);
// SpdmDemandExtraReq extraReq14 = new SpdmDemandExtraReq();
// extraReq14.setDemandId(uuid);
// extraReq14.setPropertyName("difficulty");
// extraReq14.setPropertyValue(emulation.getDifficulty());
// extras.add(extraReq14);
// SpdmDemandExtraReq extraReq15 = new SpdmDemandExtraReq();
// extraReq15.setDemandId(uuid);
// extraReq15.setPropertyName("requiredTime");
// extraReq15.setPropertyValue(emulation.getRequiredTime());
// extras.add(extraReq15);
//// SpdmDemandExtraReq extraReq20 = new SpdmDemandExtraReq();
//// extraReq20.setDemandId(uuid);
//// extraReq20.setPropertyName("subject");
//// extraReq20.setPropertyValue(emulation.getSubject());
//// extras.add(extraReq20);
// SpdmDemandExtraReq extraReq21 = new SpdmDemandExtraReq();
// extraReq21.setDemandId(uuid);
// extraReq21.setPropertyName("todoNum");
// extraReq21.setPropertyValue(emulation.getTodoNum());
// extras.add(extraReq21);
// SpdmDemandExtraReq extraReq22 = new SpdmDemandExtraReq();
// extraReq22.setDemandId(uuid);
// extraReq22.setPropertyName("status");
// extraReq22.setPropertyValue(emulation.getStatus());
// extras.add(extraReq22);
// SpdmDemandExtraReq extraReq26 = new SpdmDemandExtraReq();
// extraReq26.setDemandId(uuid);
// extraReq26.setPropertyName("projectName");
// extraReq26.setPropertyValue(emulation.getProjectName());
// extras.add(extraReq26);
// SpdmDemandExtraReq extraReq27 = new SpdmDemandExtraReq();
// extraReq27.setDemandId(uuid);
// extraReq27.setPropertyName("projectModel");
// extraReq27.setPropertyValue(emulation.getProjectModel());
// extras.add(extraReq27);
// SpdmDemandExtraReq extraReq28 = new SpdmDemandExtraReq();
// extraReq28.setDemandId(uuid);
// extraReq28.setPropertyName("projectType");
// extraReq28.setPropertyValue(emulation.getProjectType());
// extras.add(extraReq28);
// SpdmDemandExtraReq extraReq31 = new SpdmDemandExtraReq();
// extraReq31.setDemandId(uuid);
// extraReq31.setPropertyName("stationNum");
// extraReq31.setPropertyValue(emulation.getStationNum());
// extras.add(extraReq31);
// SpdmDemandExtraReq extraReq32 = new SpdmDemandExtraReq();
// extraReq32.setDemandId(uuid);
// extraReq32.setPropertyName("planStartTime");
// extraReq32.setPropertyValue(emulation.getPlanStartTime());
// extras.add(extraReq32);
// SpdmDemandExtraReq extraReq33 = new SpdmDemandExtraReq();
// extraReq33.setDemandId(uuid);
// extraReq33.setPropertyName("requirementsTime");
// extraReq33.setPropertyValue(emulation.getRequiredTime());
// extras.add(extraReq33);
// SpdmDemandExtraReq extraReq34 = new SpdmDemandExtraReq();
// extraReq34.setDemandId(uuid);
// extraReq34.setPropertyName("pausedTime");
// extraReq34.setPropertyValue(emulation.getPausedTime());
// extras.add(extraReq34);
// SpdmDemandExtraReq extraReq35 = new SpdmDemandExtraReq();
// extraReq35.setDemandId(uuid);
// extraReq35.setPropertyName("performer");
// extraReq35.setPropertyValue(emulation.getPerformer());
// extras.add(extraReq35);
// SpdmDemandExtraReq extraReq36 = new SpdmDemandExtraReq();
// extraReq36.setDemandId(uuid);
// extraReq36.setPropertyName("performerName");
// extraReq36.setPropertyValue(emulation.getPerformerName());
// extras.add(extraReq36);
// SpdmDemandExtraReq extraReq37 = new SpdmDemandExtraReq();
// extraReq37.setDemandId(uuid);
// extraReq37.setPropertyName("verifier");
// extraReq37.setPropertyValue(emulation.getVerifier());
// extras.add(extraReq37);
// SpdmDemandExtraReq extraReq39 = new SpdmDemandExtraReq();
// extraReq39.setDemandId(uuid);
// extraReq39.setPropertyName("workHourPlan");
// extraReq39.setPropertyValue(emulation.getWorkHourPlan());
// extras.add(extraReq39);
// SpdmDemandExtraReq extraReq40 = new SpdmDemandExtraReq();
// extraReq40.setDemandId(uuid);
// extraReq40.setPropertyName("realWorkHour");
// extraReq40.setPropertyValue(emulation.getRealWorkHour());
// extras.add(extraReq40);
// SpdmDemandExtraReq extraReq41 = new SpdmDemandExtraReq();
// extraReq41.setDemandId(uuid);
// extraReq41.setPropertyName("standardWorkHour");
// extraReq41.setPropertyValue(emulation.getStandardWorkHour());
// extras.add(extraReq41);
// SpdmDemandExtraReq extraReq42 = new SpdmDemandExtraReq();
// extraReq42.setDemandId(uuid);
// extraReq42.setPropertyName("turnDownReason");
// extraReq42.setPropertyValue(emulation.getTurnDownReason());
// extras.add(extraReq42);
// extras = extras.stream().filter(extra -> StringUtils.isNotBlank(extra.getPropertyValue())).toList();
// 需求的成员
// 仿真负责人
// 当前用户也授予权限
UpdatePermissionReq updatePermissionReq = new UpdatePermissionReq();
updatePermissionReq.setUserId(jobNumber);
updatePermissionReq.setUuid(uuid);
Map<Long, Byte> userPermissions = new HashMap<>();
userPermissions.put(jobNumber, FilePermissionEnum.ALL.getValue());
updatePermissionReq.setUserPermissions(userPermissions);
currentUpdatePermissionList.add(updatePermissionReq);
String emulationPerformer = emulation.getEmulationPerformer();
if (StringUtils.isNotBlank(emulationPerformer)) {
SpdmDemandRelateMemberReq pMemberReq = new SpdmDemandRelateMemberReq();
pMemberReq.setDemandId(uuid);
pMemberReq.setType(0);
String pUserId = emulationPerformer.split("-")[0];
if (isConvertibleToLong(pUserId)) {
pMemberReq.setUserId(Long.valueOf(pUserId));
UpdatePermissionReq pUpdatePermissionReq = new UpdatePermissionReq();
pUpdatePermissionReq.setUserId(pMemberReq.getUserId());
pUpdatePermissionReq.setUuid(uuid);
Map<Long, Byte> pUserPermissions = new HashMap<>();
pUserPermissions.put(pMemberReq.getUserId(), FilePermissionEnum.ALL.getValue());
pUpdatePermissionReq.setUserPermissions(pUserPermissions);
currentUpdatePermissionList.add(pUpdatePermissionReq);
}
pMemberReq.setCreateTime(curDateStr);
pMemberReq.setCreator(jobNumber);
allMemberList.add(pMemberReq);
}
// 仿真执行人
String emulationExecutor = emulation.getEmulationExecutor();
if (StringUtils.isNotBlank(emulationExecutor)) {
SpdmDemandRelateMemberReq eMemberReq = new SpdmDemandRelateMemberReq();
eMemberReq.setDemandId(uuid);
eMemberReq.setType(1);
String eUserId = emulationExecutor.split("-")[0];
if (isConvertibleToLong(eUserId)) {
eMemberReq.setUserId(Long.valueOf(eUserId));
UpdatePermissionReq eUpdatePermissionReq = new UpdatePermissionReq();
eUpdatePermissionReq.setUserId(eMemberReq.getUserId());
eUpdatePermissionReq.setUuid(uuid);
Map<Long, Byte> eUserPermissions = new HashMap<>();
eUserPermissions.put(eMemberReq.getUserId(), FilePermissionEnum.ALL.getValue());
eUpdatePermissionReq.setUserPermissions(eUserPermissions);
currentUpdatePermissionList.add(eUpdatePermissionReq);
}
eMemberReq.setCreateTime(curDateStr);
eMemberReq.setCreator(jobNumber);
allMemberList.add(eMemberReq);
}
// 3D负责人
String threeDimensionalPerformer = emulation.getThreeDimensionalPerformer();
if (StringUtils.isNotBlank(emulationExecutor)) {
SpdmDemandRelateMemberReq tMemberReq = new SpdmDemandRelateMemberReq();
tMemberReq.setDemandId(uuid);
tMemberReq.setType(2);
String tUserId = threeDimensionalPerformer.split("-")[0];
if (isConvertibleToLong(tUserId)) {
tMemberReq.setUserId(Long.valueOf(tUserId));
}
tMemberReq.setCreateTime(curDateStr);
tMemberReq.setCreator(jobNumber);
allMemberList.add(tMemberReq);
}
BatchCreateDirItem batchCreateDirItem = new BatchCreateDirItem();
DirNodeInfo dirNodeInfo = new DirNodeInfo();
dirNodeInfo.setUuId(uuid);
dirNodeInfo.setParentUuId(null);
dirNodeInfo.setUuIdOwnType(NodeTypeEnum.DEMAND.getValue());
dirNodeInfo.setDirName(spdmAddDemandReq.getDemandName());
batchCreateDirItem.setParentDirNodeInfo(dirNodeInfo);
createDirItemList.add(batchCreateDirItem);
CompletableFuture.runAsync(() -> {
demandMapper.addDemand(spdmAddDemandReq, tenantId, jobNumber);
demandMapper.addDemandMember(allMemberList);
});
if (CollectionUtils.isNotEmpty(extras)) {
for (SpdmDemandExtraReq extra : extras) {
extra.setCreateTime(curDateStr);
extra.setCreator(jobNumber);
}
demandMapper.addDemandExtra(extras);
demandMapper.addDemand(demandReq, tenantId, jobNumber);
if (CollectionUtils.isNotEmpty(memberList)) {
demandMapper.addDemandMember(memberList);
}
} catch (Exception e) {
log.error("手动同步代办时有异常:{}", e.getMessage());
log.error("异步保存需求[{}]数据异常: ", demandReq.getDemandCode(), e);
}
}
// 批量创建文件夹
BatchCreateDirReq batchCreateDirReq = new BatchCreateDirReq();
batchCreateDirReq.setItems(createDirItemList);
batchCreateDirReq.setDirType(DirTypeEnum.PROJECT_NODE_DIR.getValue());
log.info("手动同步待办创建需求时,调用批量创建文件夹的参数为:{}", batchCreateDirReq);
SdmResponse response = dataFeignClient.batchCreateDir(batchCreateDirReq);
log.info("手动同步代办创建需求时,调用批量创建文件夹的返回值为:{}", response);
},projectTaskThreadPoolExecutor);
}
if (CollectionUtils.isNotEmpty(currentUpdatePermissionList)) {
/**
* 执行批量操作:创建文件夹 + 更新权限
*/
private void executeBatchOperations(List<BatchCreateDirItem> createDirItemList,
List<UpdatePermissionReq> updatePermissionList) {
// 批量创建文件夹
if (CollectionUtils.isNotEmpty(createDirItemList)) {
BatchCreateDirReq batchCreateDirReq = new BatchCreateDirReq();
batchCreateDirReq.setItems(createDirItemList);
batchCreateDirReq.setDirType(DirTypeEnum.PROJECT_NODE_DIR.getValue());
log.info("手动同步待办创建文件夹参数: {}", batchCreateDirReq);
SdmResponse dirCreateResp = dataFeignClient.batchCreateDir(batchCreateDirReq);
log.info("手动同步待办创建文件夹响应: {}", dirCreateResp);
}
// 批量更新权限(异步)
if (CollectionUtils.isNotEmpty(updatePermissionList)) {
CompletableFuture.runAsync(() -> {
for (UpdatePermissionReq permissionReq : currentUpdatePermissionList) {
// 更新文件权限
for (UpdatePermissionReq permissionReq : updatePermissionList) {
try {
SdmResponse updatePermissionResponse = dataFeignClient.updatePermission(permissionReq);
log.info("手动同步代办时,更新用户权限的返回值为:{}", updatePermissionResponse);
}catch (Exception e) {
log.error(e.getMessage());
SdmResponse permissionResp = dataFeignClient.updatePermission(permissionReq);
log.info("更新需求[{}]权限响应: {}", permissionReq.getUuid(), permissionResp);
} catch (Exception e) {
log.error("更新需求[{}]权限异常: ", permissionReq.getUuid(), e);
}
}
});
},projectTaskThreadPoolExecutor);
}
return SdmResponse.success();
}
@Override
public SdmResponse getTodoList() {
// 1. 缓存校验
SdmResponse cacheCheckResponse = checkSyncCache();
if (cacheCheckResponse != null) {
return cacheCheckResponse;
}
// 2. 查询项目节点:提前过滤无效数据
List<SimulationNode> projectNodeList = queryProjectNodeList();
if (CollectionUtils.isEmpty(projectNodeList)) {
log.info("getTodoList中无有效项目节点数据");
return SdmResponse.success();
}
List<String> projectCodeList = projectNodeList.stream()
.map(SimulationNode::getNodeCode)
.collect(Collectors.toList());
// 3. 查询待办数据并过滤已同步数据
List<LyricVTodoEmulationInfoDM> todoInfoList = queryAndFilterTodoList(projectCodeList);
if (CollectionUtils.isEmpty(todoInfoList)) {
log.info("本次无待同步的待办数据");
return SdmResponse.success();
}
// 4. 构建同步数据并执行批量操作
return syncTodoData(projectNodeList, todoInfoList);
}
@Override

View File

@@ -61,7 +61,9 @@ 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.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
@@ -73,6 +75,7 @@ import java.text.SimpleDateFormat;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -148,6 +151,12 @@ public class NodeServiceImpl extends ServiceImpl<SimulationNodeMapper, Simulatio
@Autowired
private LyricVProjectStationPlanToDmService lyricVProjectStationPlanToDmService;
private static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Autowired
@Qualifier("projectTaskThreadPoolExecutor")
private Executor projectTaskThreadPoolExecutor;
@Transactional
@Override
@@ -189,18 +198,16 @@ public class NodeServiceImpl extends ServiceImpl<SimulationNodeMapper, Simulatio
Map<Long, Byte> userPermissions = new HashMap<>();
userPermissions.put(userId, FilePermissionEnum.ALL.getValue());
updatePermissionReq.setUserPermissions(userPermissions);
log.info("dirItem目阶段时,更新用户权限的参数为:{}",updatePermissionReq);
log.info("创建项目阶段时,更新用户权限的参数为:{}",updatePermissionReq);
SdmResponse updatePermissionResponse = dataFeignClient.updatePermission(updatePermissionReq);
log.info("dirItem目阶段时,更新用户权限的返回值为:{}",updatePermissionResponse);
log.info("创建项目阶段时,更新用户权限的返回值为:{}",updatePermissionResponse);
}
for (SpdmProjectNodeEditReq addNode : addNodeList) {
String projectSource = addNode.getProjectSource();
String nodeType = addNode.getNodeType();
if (SYNC_PROJECT_SOURCE.equals(projectSource) && NodeTypeEnum.PROJECT.getValue().equals(nodeType)) {
// 同步待办信息
getTodoListByProjectNum(addNode.getNodeCode(),addNodeList);
// TODO 同步主计划信息
// getMainPlanListByProjectId(addNode.getProjectId(),addNode.getUuid());
getTodoListByProjectNum(addNode.getNodeCode(),addNodeList, addNode.getUuid(),addNode.getNodeName());
}
}
return SdmResponse.success(addNodeList);
@@ -442,7 +449,6 @@ public class NodeServiceImpl extends ServiceImpl<SimulationNodeMapper, Simulatio
private List<SpdmProjectNodeEditReq> addNode(List<SpdmProjectNodeEditReq> addNodeList, List<TaskNodeTag> tagMap, Long tenantId, Long jobNumber) {
Long currentUserId = ThreadLocalContext.getUserId();
log.info("addNode参数为{}", addNodeList);
String curDateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
String nodeManagerList;
@@ -450,7 +456,6 @@ public class NodeServiceImpl extends ServiceImpl<SimulationNodeMapper, Simulatio
List<SpdmNodeExtraReq> extraList;
List<SpdmNodeExtraReq> allExtraList = new ArrayList<>();
List<SpdmProjectNodeEditReq> currentNodeList;
// 如果项目当前阶段下存在机台、工位的信息则直接拉取到DM系统中
Optional<SpdmProjectNodeEditReq> projectOptional = addNodeList.stream().filter(node -> NodeTypeEnum.PROJECT.getValue().equals(node.getNodeType())).findFirst();
if (projectOptional.isPresent()) {
@@ -591,7 +596,7 @@ public class NodeServiceImpl extends ServiceImpl<SimulationNodeMapper, Simulatio
}
// 设置 当前节点所属项目根节点uuid
// addNode.getPid()为空时,在dirItemownRootNodeUuid就是addNode本身uuid
// addNode.getPid()为空时,在创建项ownRootNodeUuid就是addNode本身uuid
// addNode.getPid()不为空时在创建阶段ownRootNodeUuid就是入参的pid父节点
addNode.setOwnRootNodeUuid(ObjectUtils.isEmpty(addNode.getPid()) ? addNode.getUuid() : addNode.getPid());
addNode.setCreateTime(curDateStr);
@@ -1247,9 +1252,9 @@ public class NodeServiceImpl extends ServiceImpl<SimulationNodeMapper, Simulatio
createDirReq.setParentUuId(ObjectUtils.isNotEmpty(parentUuid) ? parentUuid : null);
createDirReq.setDirName(dirName);
createDirReq.setDirType(DirTypeEnum.PROJECT_NODE_DIR.getValue());
log.info("dirItem目阶段时,调用创建文件夹的参数为:{}", createDirReq);
log.info("创建项目阶段时,调用创建文件夹的参数为:{}", createDirReq);
SdmResponse response = dataClientFeignClient.createDir(createDirReq);
log.info("dirItem目阶段时,调用创建文件夹的返回值为:{}", response);
log.info("创建项目阶段时,调用创建文件夹的返回值为:{}", response);
return response;
}
@@ -2182,7 +2187,7 @@ public class NodeServiceImpl extends ServiceImpl<SimulationNodeMapper, Simulatio
return resp;
}
try {
// dirItem目节点的文件夹
// 创建项目节点的文件夹
SdmResponse response = createDir(spdmProjectNodeEditReq.getUuid(), spdmProjectNodeEditReq.getNodeType(), null, spdmProjectNodeEditReq.getNodeName());
if (ObjectUtils.isEmpty(response) || response.getCode() != ResultCode.SUCCESS.getCode()) {
log.error("同步CID项目{}时,创建文件夹失败,原因为:{}",req.getProjectId() + " " + req.getProjectName(),response.getMessage());
@@ -2240,184 +2245,409 @@ public class NodeServiceImpl extends ServiceImpl<SimulationNodeMapper, Simulatio
return SdmResponse.success();
}
public SdmResponse getTodoListByProjectNum(String projectNum,List<SpdmProjectNodeEditReq> allNodeList) {
log.info("同步代办时,项目号为:{}", projectNum);
/**
* 根据项目编号同步待办事项并创建需求
*
* @param projectNum 项目编号
* @param allNodeList 所有项目节点列表
* @param projectNodeId 项目节点ID
* @param projectNodeName 项目节点名称
* @return 同步结果响应
*/
public SdmResponse getTodoListByProjectNum(String projectNum,
List<SpdmProjectNodeEditReq> allNodeList,
String projectNodeId,
String projectNodeName) {
// 1. 入参校验
log.info("开始同步待办事项,项目号:{}", projectNum);
if (StringUtils.isBlank(projectNum)) {
log.error("同步待办时,项目号不能为空");
return SdmResponse.failed("同步待办时,项目号不能为空");
String errorMsg = "同步待办时,项目号不能为空";
log.error(errorMsg);
return SdmResponse.failed(errorMsg);
}
// 查询
List<LyricVTodoEmulationInfoDM> todoInfoList = lyricVTodoInfoService.lambdaQuery().eq(LyricVTodoEmulationInfoDM::getProject, projectNum)
.list();
// 过滤掉数据库中已经有的需求
if (StringUtils.isBlank(projectNodeId)) {
String errorMsg = "同步待办时项目节点ID不能为空";
log.error(errorMsg);
return SdmResponse.failed(errorMsg);
}
// 2. 查询待办列表
List<LyricVTodoEmulationInfoDM> todoInfoList = queryTodoListByProjectNum(projectNum);
if (CollectionUtils.isEmpty(todoInfoList)) {
log.info("未同步到待办");
log.info("项目{}未查询到待办事项,无需同步", projectNum);
return SdmResponse.success();
}
log.info("同步到{}条待办", todoInfoList.size());
List<SpdmDemandVo> allDemandList = demandMapper.getAllList();
log.info("allDemandList:{}", allDemandList);
if (CollectionUtils.isNotEmpty(allDemandList)) {
List<String> demandCodeList = allDemandList.stream().map(SpdmDemandVo::getDemandCode).toList();
todoInfoList = todoInfoList.stream().filter(todoInfo -> !demandCodeList.contains(String.valueOf(todoInfo.getTodoId()))).toList();
if (CollectionUtils.isEmpty(todoInfoList)) {
log.info("本次同步到的待办都已经进行过同步");
return SdmResponse.success();
}
log.info("项目{}查询到{}条待办事项", projectNum, todoInfoList.size());
// 3. 过滤已同步的待办
todoInfoList = filterSyncedTodoList(todoInfoList);
if (CollectionUtils.isEmpty(todoInfoList)) {
log.info("项目{}本次同步到的待办都已经进行过同步,无需处理", projectNum);
return SdmResponse.success();
}
log.info("项目{}过滤后待同步的待办数量:{}", projectNum, todoInfoList.size());
// 4. 准备基础数据
Long tenantId = ThreadLocalContext.getTenantId();
Long jobNumber = ThreadLocalContext.getUserId();
String uuid;
String curDateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
List<BatchCreateDirItem> createDirItemList = new ArrayList<>();
List<UpdatePermissionReq> currentUpdatePermissionList = new ArrayList<>();
for (LyricVTodoEmulationInfoDM emulation : todoInfoList) {
Long currentUserId = ThreadLocalContext.getUserId();
String currentTimeStr = DATE_FORMATTER.format(new Date());
// 预构建节点映射,避免重复遍历
Map<String, SpdmProjectNodeEditReq> projectNodeMap = buildProjectNodeMap(allNodeList);
// 5. 处理待办事项,构建创建需求所需数据
SyncTodoProcessResult processResult = processTodoItems(
todoInfoList, projectNodeMap, currentUserId, currentTimeStr, tenantId);
// 6. 批量创建文件夹
SdmResponse dirCreateResponse = batchCreateDemandDirs(
processResult.getDemandDirNodeList(), projectNodeId, projectNodeName);
if (!dirCreateResponse.isSuccess()) {
log.error("项目{}批量创建需求文件夹失败:{}", projectNum, dirCreateResponse.getMessage());
return SdmResponse.failed("同步待办失败:创建文件夹失败");
}
// 7. 批量更新权限(异步)
updateDemandPermissionsAsync(processResult.getPermissionReqList());
log.info("项目{}待办事项同步完成,共处理{}条待办", projectNum, todoInfoList.size());
return SdmResponse.success();
}
/**
* 查询指定项目的待办列表
*/
private List<LyricVTodoEmulationInfoDM> queryTodoListByProjectNum(String projectNum) {
try {
return lyricVTodoInfoService.lambdaQuery()
.eq(LyricVTodoEmulationInfoDM::getProject, projectNum)
.list();
} catch (Exception e) {
log.error("查询项目{}待办列表失败", projectNum, e);
return Collections.emptyList();
}
}
/**
* 过滤已经同步过的待办事项
*/
private List<LyricVTodoEmulationInfoDM> filterSyncedTodoList(List<LyricVTodoEmulationInfoDM> todoInfoList) {
List<SpdmDemandVo> allDemandList = demandMapper.getAllList();
log.info("已存在的需求列表数量:{}", CollectionUtils.size(allDemandList));
if (CollectionUtils.isEmpty(allDemandList)) {
return todoInfoList;
}
// 提取已存在的需求编码
Set<String> existingDemandCodeSet = allDemandList.stream()
.map(SpdmDemandVo::getDemandCode)
.filter(StringUtils::isNotBlank)
.collect(Collectors.toSet());
// 过滤已同步的待办
return todoInfoList.stream()
.filter(todoInfo -> {
String todoIdStr = String.valueOf(todoInfo.getTodoId());
return !existingDemandCodeSet.contains(todoIdStr);
})
.collect(Collectors.toList());
}
/**
* 构建项目节点映射表,避免重复遍历
*/
private Map<String, SpdmProjectNodeEditReq> buildProjectNodeMap(List<SpdmProjectNodeEditReq> allNodeList) {
Map<String, SpdmProjectNodeEditReq> nodeMap = new HashMap<>();
if (CollectionUtils.isEmpty(allNodeList)) {
return nodeMap;
}
// 按节点类型+节点编码构建唯一键
for (SpdmProjectNodeEditReq node : allNodeList) {
String key = node.getNodeType() + "_" + node.getNodeCode();
nodeMap.put(key, node);
}
return nodeMap;
}
/**
* 处理待办事项,构建创建需求所需的各类数据
*/
private SyncTodoProcessResult processTodoItems(List<LyricVTodoEmulationInfoDM> todoInfoList,
Map<String, SpdmProjectNodeEditReq> projectNodeMap,
Long currentUserId,
String currentTimeStr,
Long tenantId) {
List<DirNodeInfo> demandDirNodeList = new ArrayList<>();
List<UpdatePermissionReq> permissionReqList = new ArrayList<>();
for (LyricVTodoEmulationInfoDM todoItem : todoInfoList) {
try {
List<SpdmDemandExtraReq> extras = new ArrayList<>();
List<SpdmDemandRelateMemberReq> allMemberList = new ArrayList<>();
// 需求的基础属性
SpdmAddDemandReq spdmAddDemandReq = new SpdmAddDemandReq();
uuid = RandomUtil.generateString(32);
spdmAddDemandReq.setUuid(uuid);
spdmAddDemandReq.setDemandName(emulation.getSubject());
if (ObjectUtils.isNotEmpty(emulation.getTodoId())) {
spdmAddDemandReq.setDemandCode(String.valueOf(emulation.getTodoId()));
}
spdmAddDemandReq.setDemandType(emulation.getEmulationType());
spdmAddDemandReq.setSimType(emulation.getRelevanceTask());
spdmAddDemandReq.setDemandStatus("0");
spdmAddDemandReq.setAchieveStatus("0");
spdmAddDemandReq.setProgress(0);
spdmAddDemandReq.setBeginTime(emulation.getPlanStartTime());
spdmAddDemandReq.setEndTime(emulation.getClosedTime());
String projectNode = emulation.getProject();
String phaseNode = emulation.getProjectStage();
String workspaceNode = emulation.getStationNum();
Optional<SpdmProjectNodeEditReq> projectOptional = allNodeList.stream().filter(node -> node.getNodeCode().equals(projectNode)
&& NodeTypeEnum.PROJECT.getValue().equals(node.getNodeType())).findFirst();
Optional<SpdmProjectNodeEditReq> phaseOptional = allNodeList.stream().filter(node -> node.getNodeCode().equals(phaseNode)
&& NodeTypeEnum.PHASE.getValue().equals(node.getNodeType())).findFirst();
Optional<SpdmProjectNodeEditReq> workspaceOptional = allNodeList.stream().filter(node -> node.getNodeCode().equals(workspaceNode)
&& NodeTypeEnum.WORKSPACE.getValue().equals(node.getNodeType())).findFirst();
// 转换为project的uuid
projectOptional.ifPresent(simulationNode -> spdmAddDemandReq.setProjectId(simulationNode.getUuid()));
phaseOptional.ifPresent(simulationNode -> spdmAddDemandReq.setPhaseId(simulationNode.getUuid()));
workspaceOptional.ifPresent(simulationNode -> spdmAddDemandReq.setNodeId(simulationNode.getUuid()));
spdmAddDemandReq.setCreateTime(curDateStr);
spdmAddDemandReq.setDemandSource(SYNC_PROJECT_SOURCE);
// 需求的成员
// 当前用户也授予权限
UpdatePermissionReq updatePermissionReq = new UpdatePermissionReq();
updatePermissionReq.setUserId(jobNumber);
updatePermissionReq.setUuid(uuid);
Map<Long, Byte> userPermissions = new HashMap<>();
userPermissions.put(jobNumber, FilePermissionEnum.ALL.getValue());
updatePermissionReq.setUserPermissions(userPermissions);
currentUpdatePermissionList.add(updatePermissionReq);
// 仿真负责人
String emulationPerformer = emulation.getEmulationPerformer();
if (StringUtils.isNotBlank(emulationPerformer)) {
SpdmDemandRelateMemberReq pMemberReq = new SpdmDemandRelateMemberReq();
pMemberReq.setDemandId(uuid);
pMemberReq.setType(0);
String pUserId = emulationPerformer.split("-")[0];
if (isConvertibleToLong(pUserId)) {
pMemberReq.setUserId(Long.valueOf(pUserId));
UpdatePermissionReq pUpdatePermissionReq = new UpdatePermissionReq();
pUpdatePermissionReq.setUserId(pMemberReq.getUserId());
pUpdatePermissionReq.setUuid(uuid);
Map<Long, Byte> pUserPermissions = new HashMap<>();
pUserPermissions.put(pMemberReq.getUserId(), FilePermissionEnum.ALL.getValue());
pUpdatePermissionReq.setUserPermissions(pUserPermissions);
currentUpdatePermissionList.add(pUpdatePermissionReq);
}
pMemberReq.setCreateTime(curDateStr);
pMemberReq.setCreator(jobNumber);
allMemberList.add(pMemberReq);
}
String demandUuid = RandomUtil.generateString(32);
// 仿真执行人
String emulationExecutor = emulation.getEmulationExecutor();
if (StringUtils.isNotBlank(emulationExecutor)) {
SpdmDemandRelateMemberReq eMemberReq = new SpdmDemandRelateMemberReq();
eMemberReq.setDemandId(uuid);
eMemberReq.setType(1);
String eUserId = emulationExecutor.split("-")[0];
if (isConvertibleToLong(eUserId)) {
eMemberReq.setUserId(Long.valueOf(eUserId));
UpdatePermissionReq eUpdatePermissionReq = new UpdatePermissionReq();
eUpdatePermissionReq.setUserId(eMemberReq.getUserId());
eUpdatePermissionReq.setUuid(uuid);
Map<Long, Byte> eUserPermissions = new HashMap<>();
eUserPermissions.put(eMemberReq.getUserId(), FilePermissionEnum.ALL.getValue());
eUpdatePermissionReq.setUserPermissions(eUserPermissions);
currentUpdatePermissionList.add(eUpdatePermissionReq);
}
eMemberReq.setCreateTime(curDateStr);
eMemberReq.setCreator(jobNumber);
allMemberList.add(eMemberReq);
}
// 1. 构建基础需求信息
SpdmAddDemandReq demandAddReq = buildDemandAddReq(todoItem, demandUuid, projectNodeMap, currentTimeStr);
// 3D负责人
String threeDimensionalPerformer = emulation.getThreeDimensionalPerformer();
if (StringUtils.isNotBlank(emulationExecutor)) {
SpdmDemandRelateMemberReq tMemberReq = new SpdmDemandRelateMemberReq();
tMemberReq.setDemandId(uuid);
tMemberReq.setType(2);
String tUserId = threeDimensionalPerformer.split("-")[0];
if (isConvertibleToLong(tUserId)) {
tMemberReq.setUserId(Long.valueOf(tUserId));
}
tMemberReq.setCreateTime(curDateStr);
tMemberReq.setCreator(jobNumber);
allMemberList.add(tMemberReq);
}
// 2. 构建成员信息和权限信息
List<SpdmDemandRelateMemberReq> memberList = buildDemandMemberList(
todoItem, demandUuid, currentUserId, currentTimeStr, permissionReqList);
// 3. 构建文件夹节点信息
DirNodeInfo demandDirNode = buildDemandDirNode(demandUuid, todoItem.getSubject(), projectNodeMap.get(NodeTypeEnum.PROJECT.getValue() + "_" + todoItem.getProject()).getUuid());
demandDirNodeList.add(demandDirNode);
// 4. 异步保存需求和成员信息
saveDemandAndMembersAsync(demandAddReq, memberList, tenantId, currentUserId);
BatchCreateDirItem batchCreateDirItem = new BatchCreateDirItem();
DirNodeInfo dirNodeInfo = new DirNodeInfo();
dirNodeInfo.setUuId(uuid);
dirNodeInfo.setParentUuId(null);
dirNodeInfo.setUuIdOwnType(NodeTypeEnum.DEMAND.getValue());
dirNodeInfo.setDirName(spdmAddDemandReq.getDemandName());
batchCreateDirItem.setParentDirNodeInfo(dirNodeInfo);
createDirItemList.add(batchCreateDirItem);
CompletableFuture.runAsync(() -> {
demandMapper.addDemand(spdmAddDemandReq, tenantId, jobNumber);
demandMapper.addDemandMember(allMemberList);
});
if (CollectionUtils.isNotEmpty(extras)) {
for (SpdmDemandExtraReq extra : extras) {
extra.setCreateTime(curDateStr);
extra.setCreator(jobNumber);
}
demandMapper.addDemandExtra(extras);
}
} catch (Exception e) {
log.error("同步代办时有异常:{}", e.getMessage());
log.error("处理待办事项失败待办ID{},错误信息:{}", todoItem.getTodoId(), e.getMessage(), e);
}
}
// 批量创建文件夹
BatchCreateDirReq batchCreateDirReq = new BatchCreateDirReq();
batchCreateDirReq.setItems(createDirItemList);
batchCreateDirReq.setDirType(DirTypeEnum.PROJECT_NODE_DIR.getValue());
log.info("同步待办创建需求时,调用批量创建文件夹的参数为:{}", batchCreateDirReq);
SdmResponse response = dataFeignClient.batchCreateDir(batchCreateDirReq);
log.info("同步代办创建需求时,调用批量创建文件夹的返回值为:{}", response);
// TODO 更新用户权限也需要批量接口来优化
if (CollectionUtils.isNotEmpty(currentUpdatePermissionList)) {
CompletableFuture.runAsync(() -> {
for (UpdatePermissionReq permissionReq : currentUpdatePermissionList) {
// 更新文件权限
try {
SdmResponse updatePermissionResponse = dataFeignClient.updatePermission(permissionReq);
log.info("同步需求时,更新用户权限的返回值为:{}", updatePermissionResponse);
}catch (Exception e) {
log.error(e.getMessage());
}
}
});
return new SyncTodoProcessResult(demandDirNodeList, permissionReqList);
}
/**
* 构建新增需求的基础信息
*/
private SpdmAddDemandReq buildDemandAddReq(LyricVTodoEmulationInfoDM todoItem,
String demandUuid,
Map<String, SpdmProjectNodeEditReq> projectNodeMap,
String currentTimeStr) {
SpdmAddDemandReq demandAddReq = new SpdmAddDemandReq();
// 基础属性
demandAddReq.setUuid(demandUuid);
demandAddReq.setDemandName(todoItem.getSubject());
demandAddReq.setDemandCode(ObjectUtils.isNotEmpty(todoItem.getTodoId()) ? String.valueOf(todoItem.getTodoId()) : null);
demandAddReq.setDemandType(todoItem.getEmulationType());
demandAddReq.setSimType(todoItem.getRelevanceTask());
demandAddReq.setDemandStatus("0");
demandAddReq.setAchieveStatus("0");
demandAddReq.setProgress(0);
demandAddReq.setBeginTime(todoItem.getPlanStartTime());
demandAddReq.setEndTime(todoItem.getClosedTime());
demandAddReq.setCreateTime(currentTimeStr);
demandAddReq.setDemandSource(SYNC_PROJECT_SOURCE);
// 节点信息
demandAddReq.setProjectId(getNodeUuid(projectNodeMap, NodeTypeEnum.PROJECT.getValue(), todoItem.getProject()));
demandAddReq.setPhaseId(getNodeUuid(projectNodeMap, NodeTypeEnum.PHASE.getValue(), todoItem.getProjectStage()));
demandAddReq.setNodeId(getNodeUuid(projectNodeMap, NodeTypeEnum.WORKSPACE.getValue(), todoItem.getStationNum()));
return demandAddReq;
}
/**
* 获取节点UUID
*/
private String getNodeUuid(Map<String, SpdmProjectNodeEditReq> projectNodeMap, String nodeType, String nodeCode) {
if (StringUtils.isBlank(nodeType) || StringUtils.isBlank(nodeCode)) {
return null;
}
SpdmProjectNodeEditReq node = projectNodeMap.get(nodeType + "_" + nodeCode);
return node != null ? node.getUuid() : null;
}
/**
* 构建需求成员列表,并同步构建权限请求
*/
private List<SpdmDemandRelateMemberReq> buildDemandMemberList(LyricVTodoEmulationInfoDM todoItem,
String demandUuid,
Long currentUserId,
String currentTimeStr,
List<UpdatePermissionReq> permissionReqList) {
List<SpdmDemandRelateMemberReq> memberList = new ArrayList<>();
// 添加当前用户权限
addUserPermission(permissionReqList, demandUuid, currentUserId);
// 仿真负责人
addDemandMember(todoItem.getEmulationPerformer(), 0, demandUuid, currentUserId, currentTimeStr, memberList, permissionReqList);
// 仿真执行人
addDemandMember(todoItem.getEmulationExecutor(), 1, demandUuid, currentUserId, currentTimeStr, memberList, permissionReqList);
// 3D负责人修复原代码的bug判断条件错误
addDemandMember(todoItem.getThreeDimensionalPerformer(), 2, demandUuid, currentUserId, currentTimeStr, memberList, permissionReqList);
return memberList;
}
/**
* 添加需求成员
*/
private void addDemandMember(String userInfo, int memberType, String demandUuid,
Long currentUserId, String currentTimeStr,
List<SpdmDemandRelateMemberReq> memberList,
List<UpdatePermissionReq> permissionReqList) {
if (StringUtils.isBlank(userInfo)) {
return;
}
try {
String userIdStr = userInfo.split("-")[0];
if (!isConvertibleToLong(userIdStr)) {
log.warn("用户ID转换失败原始数据{}", userInfo);
return;
}
Long userId = Long.valueOf(userIdStr);
// 构建成员信息
SpdmDemandRelateMemberReq memberReq = new SpdmDemandRelateMemberReq();
memberReq.setDemandId(demandUuid);
memberReq.setType(memberType);
memberReq.setUserId(userId);
memberReq.setCreateTime(currentTimeStr);
memberReq.setCreator(currentUserId);
memberList.add(memberReq);
// 添加权限
addUserPermission(permissionReqList, demandUuid, userId);
} catch (Exception e) {
log.error("添加需求成员失败,用户信息:{},成员类型:{}", userInfo, memberType, e);
}
}
/**
* 添加用户权限
*/
private void addUserPermission(List<UpdatePermissionReq> permissionReqList,
String demandUuid,
Long userId) {
UpdatePermissionReq permissionReq = new UpdatePermissionReq();
permissionReq.setUserId(userId);
permissionReq.setUuid(demandUuid);
Map<Long, Byte> userPermissions = new HashMap<>();
userPermissions.put(userId, FilePermissionEnum.ALL.getValue());
permissionReq.setUserPermissions(userPermissions);
permissionReqList.add(permissionReq);
}
/**
* 构建需求文件夹节点信息
*/
private DirNodeInfo buildDemandDirNode(String demandUuid, String demandName, String parentNodeId) {
DirNodeInfo dirNodeInfo = new DirNodeInfo();
dirNodeInfo.setUuId(demandUuid);
dirNodeInfo.setParentUuId(parentNodeId);
dirNodeInfo.setUuIdOwnType(NodeTypeEnum.DEMAND.getValue());
dirNodeInfo.setDirName(demandName);
return dirNodeInfo;
}
/**
* 异步保存需求和成员信息
*/
private void saveDemandAndMembersAsync(SpdmAddDemandReq demandAddReq,
List<SpdmDemandRelateMemberReq> memberList,
Long tenantId,
Long currentUserId) {
CompletableFuture.runAsync(() -> {
try {
demandMapper.addDemand(demandAddReq, tenantId, currentUserId);
if (CollectionUtils.isNotEmpty(memberList)) {
demandMapper.addDemandMember(memberList);
}
log.info("异步保存需求成功需求ID{}", demandAddReq.getUuid());
} catch (Exception e) {
log.error("异步保存需求失败需求ID{}", demandAddReq.getUuid(), e);
}
}, projectTaskThreadPoolExecutor);
}
/**
* 批量创建需求文件夹
*/
private SdmResponse batchCreateDemandDirs(List<DirNodeInfo> demandDirNodeList,
String projectNodeId,
String projectNodeName) {
if (CollectionUtils.isEmpty(demandDirNodeList)) {
log.info("无需求文件夹需要创建");
return SdmResponse.success();
}
// 构建项目目录节点
DirNodeInfo projectDirNodeInfo = new DirNodeInfo();
projectDirNodeInfo.setUuId(projectNodeId);
projectDirNodeInfo.setParentUuId(null);
projectDirNodeInfo.setUuIdOwnType(NodeTypeEnum.PROJECT.getValue());
projectDirNodeInfo.setDirName(projectNodeName);
// 构建批量创建请求
BatchCreateDirItem demandCreateDirItem = new BatchCreateDirItem();
demandCreateDirItem.setParentDirNodeInfo(projectDirNodeInfo);
demandCreateDirItem.setChildDirNodeInfos(demandDirNodeList);
BatchCreateDirReq batchCreateDirReq = new BatchCreateDirReq();
batchCreateDirReq.setItems(Collections.singletonList(demandCreateDirItem));
batchCreateDirReq.setDirType(DirTypeEnum.PROJECT_NODE_DIR.getValue());
log.info("批量创建需求文件夹请求参数:{}", batchCreateDirReq);
try {
SdmResponse response = dataFeignClient.batchCreateDir(batchCreateDirReq);
log.info("批量创建需求文件夹响应:{}", response);
return response;
} catch (Exception e) {
log.error("调用批量创建文件夹接口失败", e);
return SdmResponse.failed("创建文件夹失败:" + e.getMessage());
}
}
/**
* 异步更新需求权限
*/
private void updateDemandPermissionsAsync(List<UpdatePermissionReq> permissionReqList) {
if (CollectionUtils.isEmpty(permissionReqList)) {
log.info("无权限需要更新");
return;
}
CompletableFuture.runAsync(() -> {
log.info("开始异步更新{}条权限记录", permissionReqList.size());
for (UpdatePermissionReq permissionReq : permissionReqList) {
try {
SdmResponse updateResponse = dataFeignClient.updatePermission(permissionReq);
if (!updateResponse.isSuccess()) {
log.warn("更新权限失败需求ID{}用户ID{},响应:{}",
permissionReq.getUuid(), permissionReq.getUserId(), updateResponse);
}
} catch (Exception e) {
log.error("更新权限异常需求ID{}用户ID{}",
permissionReq.getUuid(), permissionReq.getUserId(), e);
}
}
log.info("异步更新权限完成");
}, projectTaskThreadPoolExecutor);
}
/**
* 内部数据传输对象:待办处理结果
*/
private static class SyncTodoProcessResult {
private final List<DirNodeInfo> demandDirNodeList;
private final List<UpdatePermissionReq> permissionReqList;
public SyncTodoProcessResult(List<DirNodeInfo> demandDirNodeList,
List<UpdatePermissionReq> permissionReqList) {
this.demandDirNodeList = demandDirNodeList;
this.permissionReqList = permissionReqList;
}
public List<DirNodeInfo> getDemandDirNodeList() {
return demandDirNodeList;
}
public List<UpdatePermissionReq> getPermissionReqList() {
return permissionReqList;
}
return SdmResponse.success();
}
public SdmResponse getMainPlanListByProjectId(Integer projectId, String projectUuid) {