From 061c75b5d6aace084b859a9fe894b90bd16ab637 Mon Sep 17 00:00:00 2001 From: zhuxinru Date: Tue, 31 Mar 2026 16:21:59 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E9=A1=B9=E7=9B=AE=E4=B8=AA=E4=BA=BA?= =?UTF-8?q?=E7=BD=AE=E9=A1=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../project/common/NodeMemberTypeEnum.java | 3 +- .../controller/SimulationNodeController.java | 9 +- .../sdm/project/dao/SimulationNodeMapper.java | 10 +- .../sdm/project/model/req/NodePinnedReq.java | 21 ++++ .../com/sdm/project/model/vo/SpdmNodeVo.java | 10 ++ .../com/sdm/project/service/INodeService.java | 5 + .../project/service/impl/NodeServiceImpl.java | 108 +++++++++++++++++- .../resources/mapper/SimulationNodeMapper.xml | 19 ++- 8 files changed, 173 insertions(+), 12 deletions(-) create mode 100644 project/src/main/java/com/sdm/project/model/req/NodePinnedReq.java diff --git a/project/src/main/java/com/sdm/project/common/NodeMemberTypeEnum.java b/project/src/main/java/com/sdm/project/common/NodeMemberTypeEnum.java index bc2c76f8..12d29884 100644 --- a/project/src/main/java/com/sdm/project/common/NodeMemberTypeEnum.java +++ b/project/src/main/java/com/sdm/project/common/NodeMemberTypeEnum.java @@ -7,7 +7,8 @@ public enum NodeMemberTypeEnum { MANAGER("项目经理", 0), - ATTENTION("项目关注人", 1); + ATTENTION("项目关注人", 1), + PINNED("项目置顶人", 2); private final String name; diff --git a/project/src/main/java/com/sdm/project/controller/SimulationNodeController.java b/project/src/main/java/com/sdm/project/controller/SimulationNodeController.java index 7ab4a5e4..6d8081a9 100644 --- a/project/src/main/java/com/sdm/project/controller/SimulationNodeController.java +++ b/project/src/main/java/com/sdm/project/controller/SimulationNodeController.java @@ -156,7 +156,7 @@ public class SimulationNodeController implements ISimulationNodeFeignClient { */ @PostMapping("/getAllNodeByNodeType") @Operation(summary = "根据节点类型获取所有节点", description = "根据节点类型获取所有节点") - public SdmResponse> getAllNodeByNodeTypeAndUUidsWithKeyWord(@RequestBody GetAllNodeByNodeTypeReq req) { + public SdmResponse> getAllNodeByNodeTypeAndUUidsWithKeyWord(@RequestBody GetAllNodeByNodeTypeReq req) { return nodeService.getAllNodeByNodeTypeAndUUidsWithKeyWord(req.getNodeType(), req.getUuids(), req.getKeyword()); } @@ -448,5 +448,12 @@ public class SimulationNodeController implements ISimulationNodeFeignClient { return nodeService.unFollowProject(req.getNodeId()); } + @SysLog("置顶/取消置顶项目") + @PostMapping("/togglePinProject") + @Operation(summary = "置顶/取消置顶项目", description = "置顶/取消置顶项目") + public SdmResponse togglePinProject(@RequestBody @Validated NodePinnedReq req) { + return nodeService.togglePinProject(req.getNodeId(), req.getPinned()); + } + } diff --git a/project/src/main/java/com/sdm/project/dao/SimulationNodeMapper.java b/project/src/main/java/com/sdm/project/dao/SimulationNodeMapper.java index b3fefaef..4459bdb9 100644 --- a/project/src/main/java/com/sdm/project/dao/SimulationNodeMapper.java +++ b/project/src/main/java/com/sdm/project/dao/SimulationNodeMapper.java @@ -43,19 +43,19 @@ public interface SimulationNodeMapper extends BaseMapper { List getNodeList(@Param("nodeType") String nodeType, @Param("nodeSubType") String nodeSubType, @Param("exeStatus") String exeStatus, @Param("nodeCode") String nodeCode, - @Param("manager") String manager, @Param("nodeName") String nodeName, @Param("tenantId") Long tenantId, @Param("pos") int pos, @Param("limit") int limit); + @Param("manager") String manager, @Param("nodeName") String nodeName, @Param("tenantId") Long tenantId, @Param("pos") int pos, @Param("limit") int limit, @Param("userId") Long userId); List getResponsibleNodeList(@Param("nodeType") String nodeType, @Param("nodeSubType") String nodeSubType, @Param("exeStatus") String exeStatus, @Param("nodeCode") String nodeCode, - @Param("manager") String manager, @Param("nodeName") String nodeName, @Param("tenantId") Long tenantId, @Param("pos") int pos, @Param("limit") int limit); + @Param("manager") String manager, @Param("nodeName") String nodeName, @Param("tenantId") Long tenantId, @Param("pos") int pos, @Param("limit") int limit); List getNodeListNoPermission(@Param("nodeType") String nodeType, @Param("nodeSubType") String nodeSubType, @Param("exeStatus") String exeStatus, @Param("nodeCode") String nodeCode, - @Param("manager") String manager, @Param("nodeName") String nodeName, @Param("tenantId") Long tenantId, @Param("pos") int pos, @Param("limit") int limit); + @Param("manager") String manager, @Param("nodeName") String nodeName, @Param("tenantId") Long tenantId, @Param("pos") int pos, @Param("limit") int limit); int getNodeListCount(@Param("nodeType") String nodeType, @Param("nodeSubType") String nodeSubType, @Param("exeStatus") String exeStatus, @Param("nodeCode") String nodeCode, @Param("manager") String manager, @Param("nodeName") String nodeName, @Param("tenantId") Long tenantId); int getNodeListCountNoPermission(@Param("nodeType") String nodeType, @Param("nodeSubType") String nodeSubType, @Param("exeStatus") String exeStatus, @Param("nodeCode") String nodeCode, - @Param("manager") String manager, @Param("nodeName") String nodeName, @Param("tenantId") Long tenantId); + @Param("manager") String manager, @Param("nodeName") String nodeName, @Param("tenantId") Long tenantId); List getNodeExtraListByNodeIdList(@Param("nodeIdList") List nodeIdList); @@ -118,7 +118,7 @@ public interface SimulationNodeMapper extends BaseMapper { List getNodeListByUserId(@Param("nodeType") String nodeType, @Param("nodeSubType") String nodeSubType, @Param("exeStatus") String exeStatus, @Param("nodeCode") String nodeCode, - @Param("manager") String manager, @Param("nodeName") String nodeName, @Param("tenantId") Long tenantId, @Param("pos") int pos, @Param("limit") int limit, @Param("userId") Long userId,@Param("type") Integer type); + @Param("manager") String manager, @Param("nodeName") String nodeName, @Param("tenantId") Long tenantId, @Param("pos") int pos, @Param("limit") int limit, @Param("userId") Long userId,@Param("type") Integer type); int getNodeListCountByUserId(@Param("nodeType") String nodeType, @Param("nodeSubType") String nodeSubType, @Param("exeStatus") String exeStatus, @Param("nodeCode") String nodeCode, @Param("manager") String manager, @Param("nodeName") String nodeName, @Param("tenantId") Long tenantId, @Param("userId") Long userId,@Param("type") Integer type); diff --git a/project/src/main/java/com/sdm/project/model/req/NodePinnedReq.java b/project/src/main/java/com/sdm/project/model/req/NodePinnedReq.java new file mode 100644 index 00000000..ed197928 --- /dev/null +++ b/project/src/main/java/com/sdm/project/model/req/NodePinnedReq.java @@ -0,0 +1,21 @@ +package com.sdm.project.model.req; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Data +public class NodePinnedReq { + + /** + * 项目节点ID + */ + @NotBlank(message = "项目ID不能为空") + private String nodeId; + + /** + * true=置顶,false=取消置顶 + */ + @NotNull(message = "置顶状态不能为空") + private Boolean pinned; +} diff --git a/project/src/main/java/com/sdm/project/model/vo/SpdmNodeVo.java b/project/src/main/java/com/sdm/project/model/vo/SpdmNodeVo.java index 13f9d819..67caa74a 100644 --- a/project/src/main/java/com/sdm/project/model/vo/SpdmNodeVo.java +++ b/project/src/main/java/com/sdm/project/model/vo/SpdmNodeVo.java @@ -116,6 +116,16 @@ public class SpdmNodeVo extends BaseEntity { */ private Integer attentionFlag = 0; + /** + * 置顶类型:0-未置顶,1-个人置顶 + */ + private Integer pinnedType = 0; + + /** + * 置顶时间 + */ + private String pinnedTime; + /** * 任务总数 */ diff --git a/project/src/main/java/com/sdm/project/service/INodeService.java b/project/src/main/java/com/sdm/project/service/INodeService.java index 2694aef0..9c5c06f8 100644 --- a/project/src/main/java/com/sdm/project/service/INodeService.java +++ b/project/src/main/java/com/sdm/project/service/INodeService.java @@ -100,6 +100,11 @@ public interface INodeService extends IService { */ SdmResponse followProject(String nodeId); + /** + * 设置/取消个人置顶 + */ + SdmResponse togglePinProject(String nodeId, Boolean pinned); + /** * 取消关注项目(批量为多个用户删除项目关注关系) * diff --git a/project/src/main/java/com/sdm/project/service/impl/NodeServiceImpl.java b/project/src/main/java/com/sdm/project/service/impl/NodeServiceImpl.java index 3049de18..1bddf3f0 100644 --- a/project/src/main/java/com/sdm/project/service/impl/NodeServiceImpl.java +++ b/project/src/main/java/com/sdm/project/service/impl/NodeServiceImpl.java @@ -517,7 +517,7 @@ public class NodeServiceImpl extends ServiceImpl nodeIdList = nodeList.stream().map(SpdmNodeVo::getUuid).toList(); @@ -675,18 +675,81 @@ public class NodeServiceImpl extends ServiceImpl nodeList) { + if (CollectionUtils.isEmpty(nodeList)) { + return; + } + + Long userId = ThreadLocalContext.getUserId(); + + // 查询当前用户的所有置顶记录 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper() + .eq(SimulationNodeMember::getUserId, userId) + .eq(SimulationNodeMember::getType, NodeMemberTypeEnum.PINNED.getCode()); + List pinnedList = simulationNodeMemberService.list(queryWrapper); + + if (CollectionUtils.isNotEmpty(pinnedList)) { + // 构建 nodeId -> createTime 的映射 + Map pinnedMap = pinnedList.stream() + .collect(Collectors.toMap( + SimulationNodeMember::getNodeId, + SimulationNodeMember::getCreateTime, + (a, b) -> a + )); + + // 设置置顶信息 + for (SpdmNodeVo node : nodeList) { + String pinnedTime = pinnedMap.get(node.getUuid()); + if (pinnedTime != null) { + node.setPinnedType(1); + node.setPinnedTime(pinnedTime); + } + } + } + + // 排序:置顶项目排前面(按置顶时间倒序),非置顶项目保持原有顺序 + nodeList.sort((a, b) -> { + int typeA = a.getPinnedType() != null ? a.getPinnedType() : 0; + int typeB = b.getPinnedType() != null ? b.getPinnedType() : 0; + + // 置顶的排前面 + if (typeA != typeB) { + return typeB - typeA; + } + + // 都置顶,按置顶时间倒序 + if (typeA == 1) { + String timeA = a.getPinnedTime(); + String timeB = b.getPinnedTime(); + if (timeA != null && timeB != null) { + return timeB.compareTo(timeA); + } + if (timeA != null) return -1; + if (timeB != null) return 1; + } + + return 0; + }); + } + /** * 设置项目当前阶段信息 * @param nodeList @@ -4696,6 +4759,49 @@ public class NodeServiceImpl extends ServiceImpl queryWrapper = new LambdaQueryWrapper() + .eq(SimulationNodeMember::getNodeId, nodeId) + .eq(SimulationNodeMember::getUserId, userId) + .eq(SimulationNodeMember::getType, NodeMemberTypeEnum.PINNED.getCode()); + SimulationNodeMember existing = simulationNodeMemberService.getOne(queryWrapper); + if (existing != null) { + return SdmResponse.success("已置顶"); + } + + // 添加置顶记录 + SimulationNodeMember member = new SimulationNodeMember(); + member.setNodeId(nodeId); + member.setUserId(userId); + member.setType(NodeMemberTypeEnum.PINNED.getCode()); + member.setCreator(userId); + member.setCreateTime(LocalDateTimeUtil.format(LocalDateTime.now(), DatePattern.NORM_DATETIME_PATTERN)); + simulationNodeMemberService.save(member); + + return SdmResponse.success("置顶成功"); + } else { + // 取消置顶 + simulationNodeMemberService.lambdaUpdate() + .eq(SimulationNodeMember::getNodeId, nodeId) + .eq(SimulationNodeMember::getUserId, userId) + .eq(SimulationNodeMember::getType, NodeMemberTypeEnum.PINNED.getCode()) + .remove(); + + return SdmResponse.success("取消置顶成功"); + } + } + @Override public SdmResponse exportProjectByScript(HttpServletResponse httpServletResponse, ProjectExportExcelFormat projectExportExcelFormat) { SpdmNodeListReq req = new SpdmNodeListReq(); diff --git a/project/src/main/resources/mapper/SimulationNodeMapper.xml b/project/src/main/resources/mapper/SimulationNodeMapper.xml index 0719e26c..2fcaae47 100644 --- a/project/src/main/resources/mapper/SimulationNodeMapper.xml +++ b/project/src/main/resources/mapper/SimulationNodeMapper.xml @@ -227,7 +227,13 @@ @@ -877,10 +883,15 @@