1、调整关注/取消关注项目接口的逻辑

This commit is contained in:
2026-02-13 11:34:20 +08:00
parent 3ec6cfda91
commit cd6a3fc312
4 changed files with 23 additions and 100 deletions

View File

@@ -424,14 +424,14 @@ public class SimulationNodeController implements ISimulationNodeFeignClient {
@PostMapping("/followProject") @PostMapping("/followProject")
@Operation(summary = "关注项目", description = "关注项目") @Operation(summary = "关注项目", description = "关注项目")
public SdmResponse followProject(@RequestBody SpdmProjectFollowReq req) { public SdmResponse followProject(@RequestBody SpdmProjectFollowReq req) {
return nodeService.followProject(req.getNodeId(), req.getUserIdList()); return nodeService.followProject(req.getNodeId());
} }
@SysLog("取消关注项目") @SysLog("取消关注项目")
@PostMapping("/unFollowProject") @PostMapping("/unFollowProject")
@Operation(summary = "关注项目", description = "关注项目") @Operation(summary = "关注项目", description = "关注项目")
public SdmResponse unFollowProject(@RequestBody SpdmProjectFollowReq req) { public SdmResponse unFollowProject(@RequestBody SpdmProjectFollowReq req) {
return nodeService.unFollowProject(req.getNodeId(), req.getUserIdList()); return nodeService.unFollowProject(req.getNodeId());
} }

View File

@@ -13,9 +13,4 @@ public class SpdmProjectFollowReq extends BaseEntity {
*/ */
private String nodeId; private String nodeId;
/**
* 关注项目的用户id集合
*/
List<Long> userIdList;
} }

View File

@@ -96,18 +96,16 @@ public interface INodeService extends IService<SimulationNode> {
* 关注项目(批量为多个用户添加项目关注关系) * 关注项目(批量为多个用户添加项目关注关系)
* *
* @param nodeId 项目节点IDuuid * @param nodeId 项目节点IDuuid
* @param userIdList 要关注项目的用户ID列表
* @return 统一响应结果 * @return 统一响应结果
*/ */
SdmResponse followProject(String nodeId, List<Long> userIdList); SdmResponse followProject(String nodeId);
/** /**
* 取消关注项目(批量为多个用户删除项目关注关系) * 取消关注项目(批量为多个用户删除项目关注关系)
* *
* @param nodeId 项目节点IDuuid * @param nodeId 项目节点IDuuid
* @param userIdList 要关注项目的用户ID列表
* @return 统一响应结果 * @return 统一响应结果
*/ */
SdmResponse unFollowProject(String nodeId, List<Long> userIdList); SdmResponse unFollowProject(String nodeId);
} }

View File

@@ -175,7 +175,6 @@ public class NodeServiceImpl extends ServiceImpl<SimulationNodeMapper, Simulatio
}; };
// 常量定义:提示文案 // 常量定义:提示文案
private static final String PARAM_ERROR_MSG = "参数不正确";
private static final String FOLLOW_PROJECT_NOT_FOUND_MSG = "关注项目失败,原因为:未查询到项目"; private static final String FOLLOW_PROJECT_NOT_FOUND_MSG = "关注项目失败,原因为:未查询到项目";
private static final String FOLLOW_SAVE_FAIL_MSG = "批量保存关注关系失败"; private static final String FOLLOW_SAVE_FAIL_MSG = "批量保存关注关系失败";
private static final String PARTIAL_FOLLOW_SUCCESS_MSG = "部分用户关注成功(部分用户已关注)"; private static final String PARTIAL_FOLLOW_SUCCESS_MSG = "部分用户关注成功(部分用户已关注)";
@@ -4014,18 +4013,12 @@ public class NodeServiceImpl extends ServiceImpl<SimulationNodeMapper, Simulatio
* 幂等性保障:先查询已关注记录,避免重复插入 * 幂等性保障:先查询已关注记录,避免重复插入
* *
* @param nodeId 项目节点IDuuid * @param nodeId 项目节点IDuuid
* @param userIdList 要关注项目的用户ID列表
* @return 统一响应结果 * @return 统一响应结果
*/ */
@Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public SdmResponse followProject(String nodeId, List<Long> userIdList) { public SdmResponse followProject(String nodeId) {
// 1. 参数校验 // 1. 查询项目节点是否存在
if (StringUtils.isEmpty(nodeId) || CollectionUtils.isEmpty(userIdList)) {
log.warn("关注项目参数异常nodeId{}userIdList{}", nodeId, userIdList);
return SdmResponse.failed(PARAM_ERROR_MSG);
}
// 2. 查询项目节点是否存在
SimulationNode projectNode = this.lambdaQuery() SimulationNode projectNode = this.lambdaQuery()
.eq(SimulationNode::getUuid, nodeId) .eq(SimulationNode::getUuid, nodeId)
.one(); .one();
@@ -4034,38 +4027,31 @@ public class NodeServiceImpl extends ServiceImpl<SimulationNodeMapper, Simulatio
return SdmResponse.failed(FOLLOW_PROJECT_NOT_FOUND_MSG); return SdmResponse.failed(FOLLOW_PROJECT_NOT_FOUND_MSG);
} }
// 3. 过滤无效用户IDnull/负数) List<Long> validUserIdList = Collections.singletonList(ThreadLocalContext.getUserId());
List<Long> validUserIdList = userIdList.stream()
.filter(userId -> userId != null && userId > 0)
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(validUserIdList)) {
log.info("无有效用户ID需要关注项目nodeId{}", nodeId);
return SdmResponse.success();
}
// 4. 校验幂等性,防止重复关注 // 2. 校验幂等性,防止重复关注
Set<Long> existedFollowedUserIdSet = queryExistedFollowedUserIds(nodeId, validUserIdList); Set<Long> existedFollowedUserIdSet = queryExistedFollowedUserIds(nodeId, validUserIdList);
// 5. 过滤出未关注的用户ID // 3. 过滤出未关注的用户ID
List<Long> toFollowUserIdList = validUserIdList.stream() List<Long> toFollowUserIdList = validUserIdList.stream()
.filter(userId -> !existedFollowedUserIdSet.contains(userId)) .filter(userId -> !existedFollowedUserIdSet.contains(userId))
.collect(Collectors.toList()); .collect(Collectors.toList());
// 6. 处理已全部关注的情况 // 4. 处理已全部关注的情况
if (CollectionUtils.isEmpty(toFollowUserIdList)) { if (CollectionUtils.isEmpty(toFollowUserIdList)) {
log.info("所有用户均已关注项目nodeId{},用户列表:{}", nodeId, validUserIdList); log.info("所有用户均已关注项目nodeId{},用户列表:{}", nodeId, validUserIdList);
return SdmResponse.success(ALL_FOLLOWED_MSG); return SdmResponse.success(ALL_FOLLOWED_MSG);
} }
// 7. 记录重复关注的用户 // 5. 记录重复关注的用户
List<Long> repeatedUserIdList = validUserIdList.stream() List<Long> repeatedUserIdList = validUserIdList.stream()
.filter(existedFollowedUserIdSet::contains) .filter(existedFollowedUserIdSet::contains)
.collect(Collectors.toList()); .collect(Collectors.toList());
if (!CollectionUtils.isEmpty(repeatedUserIdList)) { if (CollectionUtils.isNotEmpty(repeatedUserIdList)) {
log.error("部分用户已关注项目跳过重复关注nodeId{}重复用户ID{}", nodeId, repeatedUserIdList); log.error("部分用户已关注项目跳过重复关注nodeId{}重复用户ID{}", nodeId, repeatedUserIdList);
} }
// 8. 构建批量插入的关注关系列表(仅处理未关注用户) // 6. 构建批量插入的关注关系列表(仅处理未关注用户)
LocalDateTime currentTime = LocalDateTime.now(); LocalDateTime currentTime = LocalDateTime.now();
Long currentUserId = ThreadLocalContext.getUserId(); Long currentUserId = ThreadLocalContext.getUserId();
List<SimulationNodeMember> attentionList = new ArrayList<>(toFollowUserIdList.size()); List<SimulationNodeMember> attentionList = new ArrayList<>(toFollowUserIdList.size());
@@ -4080,7 +4066,7 @@ public class NodeServiceImpl extends ServiceImpl<SimulationNodeMapper, Simulatio
attentionList.add(member); attentionList.add(member);
} }
// 9. 批量保存未关注的用户关系 // 7. 批量保存未关注的用户关系
try { try {
boolean saveResult = simulationNodeMemberService.saveBatch(attentionList); boolean saveResult = simulationNodeMemberService.saveBatch(attentionList);
if (!saveResult) { if (!saveResult) {
@@ -4092,9 +4078,8 @@ public class NodeServiceImpl extends ServiceImpl<SimulationNodeMapper, Simulatio
return SdmResponse.failed("关注项目失败:" + e.getMessage()); return SdmResponse.failed("关注项目失败:" + e.getMessage());
} }
// 10. 返回结果(区分“全部成功”和“部分成功”) // 8. 返回结果(区分“全部成功”和“部分成功”)
String successMsg = CollectionUtils.isEmpty(repeatedUserIdList) ? "项目关注成功" : PARTIAL_FOLLOW_SUCCESS_MSG; String successMsg = CollectionUtils.isEmpty(repeatedUserIdList) ? "项目关注成功" : PARTIAL_FOLLOW_SUCCESS_MSG;
log.info("项目关注处理完成nodeId{},成功关注用户数:{},重复关注用户数:{}", nodeId, toFollowUserIdList.size(), repeatedUserIdList.size());
return SdmResponse.success(successMsg); return SdmResponse.success(successMsg);
} }
@@ -4102,40 +4087,29 @@ public class NodeServiceImpl extends ServiceImpl<SimulationNodeMapper, Simulatio
* 取消关注项目(批量为多个用户删除项目关注关系) * 取消关注项目(批量为多个用户删除项目关注关系)
* *
* @param nodeId 项目节点IDuuid * @param nodeId 项目节点IDuuid
* @param userIdList 要取消关注的用户ID列表
* @return 统一响应结果 * @return 统一响应结果
*/ */
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public SdmResponse unFollowProject(String nodeId, List<Long> userIdList) { public SdmResponse unFollowProject(String nodeId) {
// 1. 参数校验 // 1. 校验项目是否存在
if (StringUtils.isEmpty(nodeId) || CollectionUtils.isEmpty(userIdList)) {
log.warn("取消关注项目参数异常nodeId{}userIdList{}", nodeId, userIdList);
return SdmResponse.failed(PARAM_ERROR_MSG);
}
// 2. 校验项目是否存在
SimulationNode projectNode = this.lambdaQuery().eq(SimulationNode::getUuid, nodeId).one(); SimulationNode projectNode = this.lambdaQuery().eq(SimulationNode::getUuid, nodeId).one();
if (projectNode == null) { if (projectNode == null) {
log.error("取消关注项目失败根据nodeId{} 未查询到项目节点", nodeId); log.error("取消关注项目失败根据nodeId{} 未查询到项目节点", nodeId);
return SdmResponse.failed(UN_FOLLOW_PROJECT_NOT_FOUND_MSG); return SdmResponse.failed(UN_FOLLOW_PROJECT_NOT_FOUND_MSG);
} }
// 3. 过滤无效用户IDnull/负数避免无效SQL条件 List<Long> validUserIdList = Collections.singletonList(ThreadLocalContext.getUserId());
List<Long> validUserIdList = userIdList.stream().filter(userId -> userId != null && userId > 0).collect(Collectors.toList());
if (CollectionUtils.isEmpty(validUserIdList)) {
log.info("取消关注项目无有效用户IDnodeId{}", nodeId);
return SdmResponse.success(NO_UNFOLLOW_DATA_MSG);
}
// 4. 幂等性核心:先查询已存在的关注关系(仅查询需要删除的记录)
// 2. 幂等:先查询已存在的关注关系(仅查询需要删除的记录)
Set<Long> existedFollowedUserIdSet = queryExistedFollowedUserIds(nodeId, validUserIdList); Set<Long> existedFollowedUserIdSet = queryExistedFollowedUserIds(nodeId, validUserIdList);
if (CollectionUtils.isEmpty(existedFollowedUserIdSet)) { if (CollectionUtils.isEmpty(existedFollowedUserIdSet)) {
log.info("取消关注项目失败无已关注的用户记录nodeId{},用户列表:{}", nodeId, validUserIdList); log.info("取消关注项目失败无已关注的用户记录nodeId{},用户列表:{}", nodeId, validUserIdList);
return SdmResponse.success(NO_UNFOLLOW_DATA_MSG); return SdmResponse.success(NO_UNFOLLOW_DATA_MSG);
} }
// 5. 仅删除已存在的关注关系(避免无效删除操作) // 3. 仅删除已存在的关注关系(避免无效删除操作)
try { try {
LambdaUpdateWrapper<SimulationNodeMember> updateWrapper = new LambdaUpdateWrapper<SimulationNodeMember>() LambdaUpdateWrapper<SimulationNodeMember> updateWrapper = new LambdaUpdateWrapper<SimulationNodeMember>()
.eq(SimulationNodeMember::getNodeId, nodeId) .eq(SimulationNodeMember::getNodeId, nodeId)
@@ -4149,25 +4123,10 @@ public class NodeServiceImpl extends ServiceImpl<SimulationNodeMapper, Simulatio
return SdmResponse.failed(UNFOLLOW_DELETE_FAIL_MSG); return SdmResponse.failed(UNFOLLOW_DELETE_FAIL_MSG);
} }
// 6. 返回结果 // 4. 返回结果
return SdmResponse.success(UNFOLLOW_SUCCESS_MSG); return SdmResponse.success(UNFOLLOW_SUCCESS_MSG);
} }
// @Override
// public SdmResponse unFollowProject(String nodeId, List<Long> userIdList) {
// if (StringUtils.isBlank(nodeId) || CollectionUtils.isEmpty(userIdList)) {
// return SdmResponse.failed("参数不正确");
// }
// SimulationNode projectNode = this.lambdaQuery().eq(SimulationNode::getUuid, nodeId).one();
// if (ObjectUtils.isEmpty(projectNode)) {
// log.error("followProject根据nodeId{},未查询到项目", nodeId);
// return SdmResponse.failed("取消关注项目失败,原因为:未查询到项目");
// }
// simulationNodeMemberService.lambdaUpdate().eq(SimulationNodeMember::getNodeId,nodeId).in(SimulationNodeMember::getUserId,userIdList).remove();
// return SdmResponse.success();
// }
/** /**
* 批量查询已关注指定项目的用户ID * 批量查询已关注指定项目的用户ID
* *
@@ -4194,35 +4153,6 @@ public class NodeServiceImpl extends ServiceImpl<SimulationNodeMapper, Simulatio
.collect(Collectors.toSet()); .collect(Collectors.toSet());
} }
// @Override
// public SdmResponse followProject(String nodeId, List<Long> userIdList) {
// if (StringUtils.isBlank(nodeId) || CollectionUtils.isEmpty(userIdList)) {
// return SdmResponse.failed("参数不正确");
// }
// SimulationNode projectNode = this.lambdaQuery().eq(SimulationNode::getUuid, nodeId).one();
// if (ObjectUtils.isEmpty(projectNode)) {
// log.error("followProject根据nodeId{},未查询到项目", nodeId);
// return SdmResponse.failed("关注项目失败,原因为:未查询到项目");
// }
// if (CollectionUtils.isNotEmpty(userIdList)) {
// List<SimulationNodeMember> attentionList = new ArrayList<>();
// String curDateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
// Long jobNumber = ThreadLocalContext.getUserId();
// // 关注
// for (Long userId : userIdList) {
// SimulationNodeMember simulationNodeMember = new SimulationNodeMember();
// simulationNodeMember.setNodeId(nodeId);
// simulationNodeMember.setUserId(userId);
// simulationNodeMember.setType(NodeMemberTypeEnum.ATTENTION.getCode());
// simulationNodeMember.setCreateTime(curDateStr);
// simulationNodeMember.setCreator(jobNumber);
// attentionList.add(simulationNodeMember);
// }
// simulationNodeMemberService.saveBatch(attentionList);
// }
// return SdmResponse.success();
// }
/** /**
* 通用分组统计方法 - 按指定键分组统计状态数量 * 通用分组统计方法 - 按指定键分组统计状态数量
* *