mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-09-30 00:06:38 +08:00
init
This commit is contained in:
@@ -0,0 +1,882 @@
|
||||
package com.admin.service.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.admin.common.dto.ForwardDto;
|
||||
import com.admin.common.dto.ForwardUpdateDto;
|
||||
import com.admin.common.dto.ForwardWithTunnelDto;
|
||||
import com.admin.common.dto.GostDto;
|
||||
import com.admin.common.lang.R;
|
||||
import com.admin.common.task.SaveConfigAsync;
|
||||
import com.admin.common.utils.GostUtil;
|
||||
import com.admin.common.utils.JwtUtil;
|
||||
import com.admin.entity.*;
|
||||
import com.admin.mapper.ForwardMapper;
|
||||
import com.admin.service.*;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.Data;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.swing.*;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 端口转发服务实现类
|
||||
* </p>
|
||||
*
|
||||
* @author QAQ
|
||||
* @since 2025-06-03
|
||||
*/
|
||||
@Service
|
||||
public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> implements ForwardService {
|
||||
|
||||
// 常量定义
|
||||
private static final String GOST_SUCCESS_MSG = "OK";
|
||||
private static final String GOST_NOT_FOUND_MSG = "not found";
|
||||
private static final int ADMIN_ROLE_ID = 0;
|
||||
private static final int TUNNEL_TYPE_PORT_FORWARD = 1;
|
||||
private static final int TUNNEL_TYPE_TUNNEL_FORWARD = 2;
|
||||
private static final int FORWARD_STATUS_ACTIVE = 1;
|
||||
private static final int FORWARD_STATUS_PAUSED = 0;
|
||||
private static final int FORWARD_STATUS_ERROR = -1;
|
||||
private static final int TUNNEL_STATUS_ACTIVE = 1;
|
||||
private static final int FLOW_TYPE_UPLOAD_ONLY = 1;
|
||||
private static final long BYTES_TO_GB = 1024L * 1024L * 1024L;
|
||||
|
||||
@Resource
|
||||
@Lazy
|
||||
private TunnelService tunnelService;
|
||||
|
||||
@Resource
|
||||
UserTunnelService userTunnelService;
|
||||
|
||||
@Resource
|
||||
UserService userService;
|
||||
|
||||
@Resource
|
||||
NodeService nodeService;
|
||||
|
||||
|
||||
@Override
|
||||
public R createForward(ForwardDto forwardDto) {
|
||||
// 1. 获取当前用户信息
|
||||
UserInfo currentUser = getCurrentUserInfo();
|
||||
|
||||
// 2. 检查隧道是否存在和可用
|
||||
Tunnel tunnel = validateTunnel(forwardDto.getTunnelId());
|
||||
if (tunnel == null) {
|
||||
return R.err("隧道不存在");
|
||||
}
|
||||
if (tunnel.getStatus() != TUNNEL_STATUS_ACTIVE) {
|
||||
return R.err("隧道已禁用,无法创建转发");
|
||||
}
|
||||
|
||||
// 3. 普通用户权限和限制检查
|
||||
UserPermissionResult permissionResult = checkUserPermissions(currentUser, tunnel, null);
|
||||
if (permissionResult.isHasError()) {
|
||||
return R.err(permissionResult.getErrorMessage());
|
||||
}
|
||||
|
||||
// 4. 分配端口
|
||||
PortAllocation portAllocation = allocatePorts(tunnel);
|
||||
if (portAllocation.isHasError()) {
|
||||
return R.err(portAllocation.getErrorMessage());
|
||||
}
|
||||
|
||||
// 5. 创建并保存Forward对象
|
||||
Forward forward = createForwardEntity(forwardDto, currentUser, portAllocation);
|
||||
if (!this.save(forward)) {
|
||||
return R.err("端口转发创建失败");
|
||||
}
|
||||
|
||||
// 6. 调用Gost服务创建转发
|
||||
R gostResult = createGostServices(forward, tunnel, permissionResult.getLimiter());
|
||||
if (gostResult.getCode() != 0) {
|
||||
this.removeById(forward.getId());
|
||||
return gostResult;
|
||||
}
|
||||
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Override
|
||||
public R getAllForwards() {
|
||||
UserInfo currentUser = getCurrentUserInfo();
|
||||
|
||||
List<ForwardWithTunnelDto> forwardList;
|
||||
if (currentUser.getRoleId() != ADMIN_ROLE_ID) {
|
||||
forwardList = baseMapper.selectForwardsWithTunnelByUserId(currentUser.getUserId());
|
||||
} else {
|
||||
forwardList = baseMapper.selectAllForwardsWithTunnel();
|
||||
}
|
||||
|
||||
return R.ok(forwardList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public R updateForward(ForwardUpdateDto forwardUpdateDto) {
|
||||
// 1. 获取当前用户信息
|
||||
UserInfo currentUser = getCurrentUserInfo();
|
||||
|
||||
// 2. 检查转发是否存在
|
||||
Forward existForward = validateForwardExists(forwardUpdateDto.getId(), currentUser);
|
||||
if (existForward == null) {
|
||||
return R.err("转发不存在");
|
||||
}
|
||||
|
||||
// 3. 检查隧道是否存在和可用
|
||||
Tunnel tunnel = validateTunnel(forwardUpdateDto.getTunnelId());
|
||||
if (tunnel == null) {
|
||||
return R.err("隧道不存在");
|
||||
}
|
||||
if (tunnel.getStatus() != TUNNEL_STATUS_ACTIVE) {
|
||||
return R.err("隧道已禁用,无法更新转发");
|
||||
}
|
||||
|
||||
// 4. 检查权限和限制(仅当隧道发生变化时)
|
||||
UserPermissionResult permissionResult = null;
|
||||
if (isTunnelChanged(existForward, forwardUpdateDto)) {
|
||||
permissionResult = checkUserPermissions(currentUser, tunnel, forwardUpdateDto.getId());
|
||||
if (permissionResult.isHasError()) {
|
||||
return R.err(permissionResult.getErrorMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 更新Forward对象
|
||||
Forward updatedForward = updateForwardEntity(forwardUpdateDto, existForward, tunnel);
|
||||
|
||||
// 6. 调用Gost服务更新转发
|
||||
R gostResult = updateGostServices(updatedForward, tunnel,
|
||||
permissionResult != null ? permissionResult.getLimiter() : null);
|
||||
if (gostResult.getCode() != 0) {
|
||||
return gostResult;
|
||||
}
|
||||
|
||||
// 7. 保存更新
|
||||
boolean result = this.updateById(updatedForward);
|
||||
return result ? R.ok("端口转发更新成功") : R.err("端口转发更新失败");
|
||||
}
|
||||
|
||||
@Override
|
||||
public R deleteForward(Long id) {
|
||||
// 1. 获取当前用户信息
|
||||
UserInfo currentUser = getCurrentUserInfo();
|
||||
|
||||
// 2. 检查转发是否存在
|
||||
Forward forward = validateForwardExists(id, currentUser);
|
||||
if (forward == null) {
|
||||
return R.err("端口转发不存在");
|
||||
}
|
||||
|
||||
// 3. 获取隧道信息
|
||||
Tunnel tunnel = validateTunnel(forward.getTunnelId());
|
||||
if (tunnel == null) {
|
||||
return R.err("隧道不存在");
|
||||
}
|
||||
|
||||
// 4. 权限检查(仅普通用户需要)
|
||||
if (currentUser.getRoleId() != ADMIN_ROLE_ID) {
|
||||
if (!hasUserTunnelPermission(currentUser.getUserId(), tunnel.getId().intValue())) {
|
||||
return R.err("你没有该隧道权限");
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 调用Gost服务删除转发
|
||||
R gostResult = deleteGostServices(forward, tunnel);
|
||||
if (gostResult.getCode() != 0) {
|
||||
return gostResult;
|
||||
}
|
||||
|
||||
// 6. 删除转发记录
|
||||
boolean result = this.removeById(id);
|
||||
if (result) {
|
||||
// 归还用户转发条数(普通用户才需要归还)
|
||||
returnUserForwardQuota(currentUser);
|
||||
return R.ok("端口转发删除成功");
|
||||
} else {
|
||||
return R.err("端口转发删除失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public R pauseForward(Long id) {
|
||||
return changeForwardStatus(id, FORWARD_STATUS_PAUSED, "暂停", "PauseService");
|
||||
}
|
||||
|
||||
@Override
|
||||
public R resumeForward(Long id) {
|
||||
return changeForwardStatus(id, FORWARD_STATUS_ACTIVE, "恢复", "ResumeService");
|
||||
}
|
||||
|
||||
@Override
|
||||
public R forceDeleteForward(Long id) {
|
||||
// 1. 获取当前用户信息
|
||||
UserInfo currentUser = getCurrentUserInfo();
|
||||
|
||||
// 2. 检查转发是否存在且用户有权限操作
|
||||
Forward forward = validateForwardExists(id, currentUser);
|
||||
if (forward == null) {
|
||||
return R.err("端口转发不存在");
|
||||
}
|
||||
|
||||
// 3. 直接删除转发记录,跳过GOST服务删除
|
||||
boolean result = this.removeById(id);
|
||||
if (result) {
|
||||
// 归还用户转发条数(普通用户才需要归还)
|
||||
returnUserForwardQuota(currentUser);
|
||||
return R.ok("端口转发强制删除成功");
|
||||
} else {
|
||||
return R.err("端口转发强制删除失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 改变转发状态(暂停/恢复)
|
||||
*/
|
||||
private R changeForwardStatus(Long id, int targetStatus, String operation, String gostMethod) {
|
||||
// 1. 获取当前用户信息
|
||||
UserInfo currentUser = getCurrentUserInfo();
|
||||
|
||||
// 2. 检查转发是否存在
|
||||
Forward forward = validateForwardExists(id, currentUser);
|
||||
if (forward == null) {
|
||||
return R.err("转发不存在");
|
||||
}
|
||||
|
||||
// 3. 获取隧道信息
|
||||
Tunnel tunnel = validateTunnel(forward.getTunnelId());
|
||||
if (tunnel == null) {
|
||||
return R.err("隧道不存在");
|
||||
}
|
||||
|
||||
// 4. 恢复服务时需要额外检查
|
||||
if (targetStatus == FORWARD_STATUS_ACTIVE) {
|
||||
if (tunnel.getStatus() != TUNNEL_STATUS_ACTIVE) {
|
||||
return R.err("隧道已禁用,无法恢复服务");
|
||||
}
|
||||
|
||||
// 普通用户需要检查流量和账户状态
|
||||
if (currentUser.getRoleId() != ADMIN_ROLE_ID) {
|
||||
R flowCheckResult = checkUserFlowLimits(currentUser.getUserId(), tunnel);
|
||||
if (flowCheckResult.getCode() != 0) {
|
||||
return flowCheckResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 权限检查(仅普通用户需要)
|
||||
if (currentUser.getRoleId() != ADMIN_ROLE_ID) {
|
||||
if (!hasUserTunnelPermission(currentUser.getUserId(), tunnel.getId().intValue())) {
|
||||
return R.err("你没有该隧道权限");
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 调用Gost服务
|
||||
Node node = nodeService.getNodeById(tunnel.getInNodeId());
|
||||
if (node == null) {
|
||||
return R.err("节点不存在");
|
||||
}
|
||||
|
||||
String serviceName = buildServiceName(forward.getId(), forward.getUserId(), forward.getTunnelId());
|
||||
GostDto gostResult;
|
||||
|
||||
if ("PauseService".equals(gostMethod)) {
|
||||
gostResult = GostUtil.PauseService(node.getIp() + ":" + node.getPort(), serviceName, node.getSecret());
|
||||
|
||||
// 隧道转发需要同时暂停远端服务
|
||||
if (tunnel.getType() == TUNNEL_TYPE_TUNNEL_FORWARD) {
|
||||
Node outNode = nodeService.getNodeById(tunnel.getOutNodeId());
|
||||
if (outNode != null) {
|
||||
GostDto remoteResult = GostUtil.PauseRemoteService(outNode.getIp() + ":" + outNode.getPort(), serviceName, outNode.getSecret());
|
||||
if (!isGostOperationSuccess(remoteResult)) {
|
||||
return R.err(operation + "远端服务失败:" + remoteResult.getMsg());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
gostResult = GostUtil.ResumeService(node.getIp() + ":" + node.getPort(), serviceName, node.getSecret());
|
||||
|
||||
// 隧道转发需要同时恢复远端服务
|
||||
if (tunnel.getType() == TUNNEL_TYPE_TUNNEL_FORWARD) {
|
||||
Node outNode = nodeService.getNodeById(tunnel.getOutNodeId());
|
||||
if (outNode != null) {
|
||||
GostDto remoteResult = GostUtil.ResumeRemoteService(outNode.getIp() + ":" + outNode.getPort(), serviceName, outNode.getSecret());
|
||||
if (!isGostOperationSuccess(remoteResult)) {
|
||||
return R.err(operation + "远端服务失败:" + remoteResult.getMsg());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isGostOperationSuccess(gostResult)) {
|
||||
return R.err(operation + "服务失败:" + gostResult.getMsg());
|
||||
}
|
||||
|
||||
// 7. 更新转发状态
|
||||
forward.setStatus(targetStatus);
|
||||
forward.setUpdatedTime(System.currentTimeMillis());
|
||||
boolean result = this.updateById(forward);
|
||||
|
||||
return result ? R.ok("服务已" + operation) : R.err("更新状态失败");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户信息
|
||||
*/
|
||||
private UserInfo getCurrentUserInfo() {
|
||||
Integer userId = JwtUtil.getUserIdFromToken();
|
||||
Integer roleId = JwtUtil.getRoleIdFromToken();
|
||||
String userName = JwtUtil.getNameFromToken();
|
||||
return new UserInfo(userId, roleId, userName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证隧道是否存在
|
||||
*/
|
||||
private Tunnel validateTunnel(Integer tunnelId) {
|
||||
return tunnelService.getById(tunnelId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证转发是否存在且用户有权限访问
|
||||
*/
|
||||
private Forward validateForwardExists(Long forwardId, UserInfo currentUser) {
|
||||
Forward forward = this.getById(forwardId);
|
||||
if (forward == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 普通用户只能操作自己的转发
|
||||
if (currentUser.getRoleId() != ADMIN_ROLE_ID &&
|
||||
!Objects.equals(currentUser.getUserId(), forward.getUserId())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return forward;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户权限和限制
|
||||
*/
|
||||
private UserPermissionResult checkUserPermissions(UserInfo currentUser, Tunnel tunnel, Long excludeForwardId) {
|
||||
if (currentUser.getRoleId() == ADMIN_ROLE_ID) {
|
||||
return UserPermissionResult.success(null);
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
User userInfo = userService.getById(currentUser.getUserId());
|
||||
if (userInfo.getExpTime() != null && userInfo.getExpTime() <= System.currentTimeMillis()) {
|
||||
return UserPermissionResult.error("当前账号已到期");
|
||||
}
|
||||
|
||||
// 检查用户隧道权限
|
||||
UserTunnel userTunnel = getUserTunnel(currentUser.getUserId(), tunnel.getId().intValue());
|
||||
if (userTunnel == null) {
|
||||
return UserPermissionResult.error("你没有该隧道权限");
|
||||
}
|
||||
|
||||
// 检查隧道权限到期时间
|
||||
if (userTunnel.getExpTime() != null && userTunnel.getExpTime() <= System.currentTimeMillis()) {
|
||||
return UserPermissionResult.error("该隧道权限已到期");
|
||||
}
|
||||
|
||||
// 流量限制检查
|
||||
if (userInfo.getFlow() <= 0) {
|
||||
return UserPermissionResult.error("用户总流量已用完");
|
||||
}
|
||||
if (userTunnel.getFlow() <= 0) {
|
||||
return UserPermissionResult.error("该隧道流量已用完");
|
||||
}
|
||||
|
||||
// 转发数量限制检查
|
||||
R quotaCheckResult = checkForwardQuota(currentUser.getUserId(), tunnel.getId().intValue(), userTunnel, userInfo, excludeForwardId);
|
||||
if (quotaCheckResult.getCode() != 0) {
|
||||
return UserPermissionResult.error(quotaCheckResult.getMsg());
|
||||
}
|
||||
|
||||
return UserPermissionResult.success(userTunnel.getSpeedId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户转发数量限制
|
||||
*/
|
||||
private R checkForwardQuota(Integer userId, Integer tunnelId, UserTunnel userTunnel, User userInfo, Long excludeForwardId) {
|
||||
// 检查用户总转发数量限制
|
||||
long userForwardCount = this.count(new QueryWrapper<Forward>().eq("user_id", userId));
|
||||
if (userForwardCount >= userInfo.getNum()) {
|
||||
return R.err("用户总转发数量已达上限,当前限制:" + userInfo.getNum() + "个");
|
||||
}
|
||||
|
||||
// 检查用户在该隧道的转发数量限制
|
||||
QueryWrapper<Forward> tunnelQuery = new QueryWrapper<Forward>()
|
||||
.eq("user_id", userId)
|
||||
.eq("tunnel_id", tunnelId);
|
||||
|
||||
if (excludeForwardId != null) {
|
||||
tunnelQuery.ne("id", excludeForwardId);
|
||||
}
|
||||
|
||||
long tunnelForwardCount = this.count(tunnelQuery);
|
||||
if (tunnelForwardCount >= userTunnel.getNum()) {
|
||||
return R.err("该隧道转发数量已达上限,当前限制:" + userTunnel.getNum() + "个");
|
||||
}
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户流量限制
|
||||
*/
|
||||
private R checkUserFlowLimits(Integer userId, Tunnel tunnel) {
|
||||
User userInfo = userService.getById(userId);
|
||||
if (userInfo.getExpTime() != null && userInfo.getExpTime() <= System.currentTimeMillis()) {
|
||||
return R.err("当前账号已到期");
|
||||
}
|
||||
|
||||
UserTunnel userTunnel = getUserTunnel(userId, tunnel.getId().intValue());
|
||||
if (userTunnel == null) {
|
||||
return R.err("你没有该隧道权限");
|
||||
}
|
||||
|
||||
// 检查隧道权限到期时间
|
||||
if (userTunnel.getExpTime() != null && userTunnel.getExpTime() <= System.currentTimeMillis()) {
|
||||
return R.err("该隧道权限已到期,无法恢复服务");
|
||||
}
|
||||
|
||||
// 检查用户总流量限制
|
||||
if (userInfo.getFlow() * BYTES_TO_GB <= userInfo.getInFlow() + userInfo.getOutFlow()) {
|
||||
return R.err("用户总流量已用完,无法恢复服务");
|
||||
}
|
||||
|
||||
// 检查隧道流量限制
|
||||
long tunnelFlow = (tunnel.getFlow() == FLOW_TYPE_UPLOAD_ONLY) ?
|
||||
userTunnel.getOutFlow() :
|
||||
userTunnel.getInFlow() + userTunnel.getOutFlow();
|
||||
|
||||
if (userTunnel.getFlow() * BYTES_TO_GB <= tunnelFlow) {
|
||||
return R.err("该隧道流量已用完,无法恢复服务");
|
||||
}
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 分配端口
|
||||
*/
|
||||
private PortAllocation allocatePorts(Tunnel tunnel) {
|
||||
Integer inPort = allocateInPort(tunnel);
|
||||
if (inPort == null) {
|
||||
return PortAllocation.error("隧道入口端口已满,无法分配新端口");
|
||||
}
|
||||
|
||||
Integer outPort = null;
|
||||
if (tunnel.getType() == TUNNEL_TYPE_TUNNEL_FORWARD) {
|
||||
outPort = allocateOutPort(tunnel);
|
||||
if (outPort == null) {
|
||||
return PortAllocation.error("隧道出口端口已满,无法分配新端口");
|
||||
}
|
||||
}
|
||||
|
||||
return PortAllocation.success(inPort, outPort);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Forward实体对象
|
||||
*/
|
||||
private Forward createForwardEntity(ForwardDto forwardDto, UserInfo currentUser, PortAllocation portAllocation) {
|
||||
Forward forward = new Forward();
|
||||
forward.setStatus(FORWARD_STATUS_ACTIVE);
|
||||
forward.setInPort(portAllocation.getInPort());
|
||||
forward.setOutPort(portAllocation.getOutPort());
|
||||
forward.setUserId(currentUser.getUserId());
|
||||
forward.setUserName(currentUser.getUserName());
|
||||
forward.setCreatedTime(System.currentTimeMillis());
|
||||
forward.setUpdatedTime(System.currentTimeMillis());
|
||||
BeanUtils.copyProperties(forwardDto, forward);
|
||||
return forward;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新Forward实体对象
|
||||
*/
|
||||
private Forward updateForwardEntity(ForwardUpdateDto forwardUpdateDto, Forward existForward, Tunnel tunnel) {
|
||||
Forward forward = new Forward();
|
||||
BeanUtils.copyProperties(forwardUpdateDto, forward);
|
||||
|
||||
// 如果隧道ID发生变化,需要重新分配端口
|
||||
if (!existForward.getTunnelId().equals(forwardUpdateDto.getTunnelId())) {
|
||||
PortAllocation portAllocation = allocatePorts(tunnel);
|
||||
forward.setInPort(portAllocation.getInPort());
|
||||
forward.setOutPort(portAllocation.getOutPort());
|
||||
} else {
|
||||
// 隧道未变化,保持原端口
|
||||
forward.setInPort(existForward.getInPort());
|
||||
forward.setOutPort(existForward.getOutPort());
|
||||
}
|
||||
|
||||
forward.setUpdatedTime(System.currentTimeMillis());
|
||||
return forward;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Gost服务
|
||||
*/
|
||||
private R createGostServices(Forward forward, Tunnel tunnel, Integer limiter) {
|
||||
String serviceName = buildServiceName(forward.getId(), forward.getUserId(), forward.getTunnelId());
|
||||
Node inNode = nodeService.getNodeById(tunnel.getInNodeId());
|
||||
|
||||
// 隧道转发需要创建链和远程服务
|
||||
if (tunnel.getType() == TUNNEL_TYPE_TUNNEL_FORWARD) {
|
||||
R chainResult = createChainService(inNode, serviceName, tunnel.getOutIp(), forward.getOutPort());
|
||||
if (chainResult.getCode() != 0) {
|
||||
updateForwardStatusToError(forward);
|
||||
return chainResult;
|
||||
}
|
||||
|
||||
R remoteResult = createRemoteService(tunnel.getOutNodeId().intValue(), serviceName, forward);
|
||||
if (remoteResult.getCode() != 0) {
|
||||
updateForwardStatusToError(forward);
|
||||
return remoteResult;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 创建主服务
|
||||
R serviceResult = createMainService(inNode, serviceName, forward, limiter, tunnel.getType());
|
||||
if (serviceResult.getCode() != 0) {
|
||||
updateForwardStatusToError(forward);
|
||||
return serviceResult;
|
||||
}
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新Gost服务
|
||||
*/
|
||||
private R updateGostServices(Forward forward, Tunnel tunnel, Integer limiter) {
|
||||
String serviceName = buildServiceName(forward.getId(), forward.getUserId(), forward.getTunnelId());
|
||||
Node inNode = nodeService.getNodeById(tunnel.getInNodeId());
|
||||
|
||||
// 隧道转发需要更新链和远程服务
|
||||
if (tunnel.getType() == TUNNEL_TYPE_TUNNEL_FORWARD) {
|
||||
R chainResult = updateChainService(inNode, serviceName, tunnel.getOutIp(), forward.getOutPort());
|
||||
if (chainResult.getCode() != 0) {
|
||||
updateForwardStatusToError(forward);
|
||||
return chainResult;
|
||||
}
|
||||
|
||||
R remoteResult = updateRemoteService(tunnel.getOutNodeId().intValue(), serviceName, forward);
|
||||
if (remoteResult.getCode() != 0) {
|
||||
updateForwardStatusToError(forward);
|
||||
return remoteResult;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新主服务
|
||||
R serviceResult = updateMainService(inNode, serviceName, forward, limiter, tunnel.getType());
|
||||
if (serviceResult.getCode() != 0) {
|
||||
updateForwardStatusToError(forward);
|
||||
return serviceResult;
|
||||
}
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除Gost服务
|
||||
*/
|
||||
private R deleteGostServices(Forward forward, Tunnel tunnel) {
|
||||
String serviceName = buildServiceName(forward.getId(), forward.getUserId(), forward.getTunnelId());
|
||||
Node inNode = nodeService.getNodeById(tunnel.getInNodeId());
|
||||
|
||||
// 删除主服务
|
||||
GostDto serviceResult = GostUtil.DeleteService(inNode.getIp() + ":" + inNode.getPort(), serviceName, inNode.getSecret());
|
||||
if (!isGostOperationSuccess(serviceResult)) {
|
||||
return R.err(serviceResult.getMsg());
|
||||
}
|
||||
|
||||
// 隧道转发需要删除链和远程服务
|
||||
if (tunnel.getType() == TUNNEL_TYPE_TUNNEL_FORWARD) {
|
||||
GostDto chainResult = GostUtil.DeleteChains(inNode.getIp() + ":" + inNode.getPort(), serviceName, inNode.getSecret());
|
||||
if (!isGostOperationSuccess(chainResult)) {
|
||||
return R.err(chainResult.getMsg());
|
||||
}
|
||||
|
||||
Node outNode = nodeService.getNodeById(tunnel.getOutNodeId());
|
||||
GostDto remoteResult = GostUtil.DeleteRemoteService(outNode.getIp() + ":" + outNode.getPort(), serviceName, outNode.getSecret());
|
||||
if (!isGostOperationSuccess(remoteResult)) {
|
||||
return R.err(remoteResult.getMsg());
|
||||
}
|
||||
}
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建链服务
|
||||
*/
|
||||
private R createChainService(Node inNode, String serviceName, String outIp, Integer outPort) {
|
||||
String remoteAddr = outIp + ":" + outPort;
|
||||
GostDto result = GostUtil.AddChains(inNode.getIp() + ":" + inNode.getPort(), serviceName, remoteAddr, inNode.getSecret());
|
||||
return isGostOperationSuccess(result) ? R.ok() : R.err(result.getMsg());
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建远程服务
|
||||
*/
|
||||
private R createRemoteService(Integer outNodeId, String serviceName, Forward forward) {
|
||||
Node outNode = nodeService.getNodeById(outNodeId.longValue());
|
||||
GostDto result = GostUtil.AddRemoteService(outNode.getIp() + ":" + outNode.getPort(),
|
||||
serviceName, forward.getOutPort(),
|
||||
forward.getRemoteAddr(), outNode.getSecret());
|
||||
return isGostOperationSuccess(result) ? R.ok() : R.err(result.getMsg());
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建主服务
|
||||
*/
|
||||
private R createMainService(Node inNode, String serviceName, Forward forward, Integer limiter, Integer tunnelType) {
|
||||
GostDto result = GostUtil.AddService(inNode.getIp() + ":" + inNode.getPort(), serviceName,
|
||||
forward.getInPort(), limiter, forward.getRemoteAddr(),
|
||||
inNode.getSecret(), tunnelType);
|
||||
return isGostOperationSuccess(result) ? R.ok() : R.err(result.getMsg());
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新链服务
|
||||
*/
|
||||
private R updateChainService(Node inNode, String serviceName, String outIp, Integer outPort) {
|
||||
|
||||
// 创建新链
|
||||
String remoteAddr = outIp + ":" + outPort;
|
||||
GostDto createResult = GostUtil.UpdateChains(inNode.getIp() + ":" + inNode.getPort(), serviceName, remoteAddr, inNode.getSecret());
|
||||
if (createResult.getMsg().contains(GOST_NOT_FOUND_MSG)) {
|
||||
createResult = GostUtil.AddChains(inNode.getIp() + ":" + inNode.getPort(), serviceName, remoteAddr, inNode.getSecret());
|
||||
}
|
||||
return isGostOperationSuccess(createResult) ? R.ok() : R.err(createResult.getMsg());
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新远程服务
|
||||
*/
|
||||
private R updateRemoteService(Integer outNodeId, String serviceName, Forward forward) {
|
||||
Node outNode = nodeService.getNodeById(outNodeId.longValue());
|
||||
// 创建新远程服务
|
||||
GostDto createResult = GostUtil.UpdateRemoteService(outNode.getIp() + ":" + outNode.getPort(),
|
||||
serviceName, forward.getOutPort(),
|
||||
forward.getRemoteAddr(), outNode.getSecret());
|
||||
if (createResult.getMsg().contains(GOST_NOT_FOUND_MSG)) {
|
||||
createResult = GostUtil.AddRemoteService(outNode.getIp() + ":" + outNode.getPort(),
|
||||
serviceName, forward.getOutPort(),
|
||||
forward.getRemoteAddr(), outNode.getSecret());
|
||||
}
|
||||
return isGostOperationSuccess(createResult) ? R.ok() : R.err(createResult.getMsg());
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新主服务
|
||||
*/
|
||||
private R updateMainService(Node inNode, String serviceName, Forward forward, Integer limiter, Integer tunnelType) {
|
||||
GostDto result = GostUtil.UpdateService(inNode.getIp() + ":" + inNode.getPort(), serviceName,
|
||||
forward.getInPort(), limiter, forward.getRemoteAddr(),
|
||||
inNode.getSecret(), tunnelType);
|
||||
|
||||
if (result.getMsg().contains(GOST_NOT_FOUND_MSG)) {
|
||||
result = GostUtil.AddService(inNode.getIp() + ":" + inNode.getPort(), serviceName,
|
||||
forward.getInPort(), limiter, forward.getRemoteAddr(),
|
||||
inNode.getSecret(), tunnelType);
|
||||
}
|
||||
|
||||
return isGostOperationSuccess(result) ? R.ok() : R.err(result.getMsg());
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新转发状态为错误
|
||||
*/
|
||||
private void updateForwardStatusToError(Forward forward) {
|
||||
forward.setStatus(FORWARD_STATUS_ERROR);
|
||||
this.updateById(forward);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否有用户隧道权限
|
||||
*/
|
||||
private boolean hasUserTunnelPermission(Integer userId, Integer tunnelId) {
|
||||
return getUserTunnel(userId, tunnelId) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户隧道关系
|
||||
*/
|
||||
private UserTunnel getUserTunnel(Integer userId, Integer tunnelId) {
|
||||
return userTunnelService.getOne(new QueryWrapper<UserTunnel>()
|
||||
.eq("user_id", userId)
|
||||
.eq("tunnel_id", tunnelId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查隧道是否发生变化
|
||||
*/
|
||||
private boolean isTunnelChanged(Forward existForward, ForwardUpdateDto updateDto) {
|
||||
return !existForward.getTunnelId().equals(updateDto.getTunnelId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 归还用户转发配额
|
||||
*/
|
||||
private void returnUserForwardQuota(UserInfo currentUser) {
|
||||
if (currentUser.getRoleId() != ADMIN_ROLE_ID) {
|
||||
User user = userService.getById(currentUser.getUserId());
|
||||
if (user != null) {
|
||||
user.setNum(user.getNum() + 1);
|
||||
user.setUpdatedTime(System.currentTimeMillis());
|
||||
userService.updateById(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查Gost操作是否成功
|
||||
*/
|
||||
private boolean isGostOperationSuccess(GostDto gostResult) {
|
||||
return Objects.equals(gostResult.getMsg(), GOST_SUCCESS_MSG);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为隧道分配一个可用的入口端口
|
||||
*/
|
||||
private Integer allocateInPort(Tunnel tunnel) {
|
||||
// 获取所有使用相同入口节点的隧道
|
||||
List<Tunnel> tunnelsWithSameInNode = tunnelService.list(new QueryWrapper<Tunnel>().eq("in_node_id", tunnel.getInNodeId()));
|
||||
Set<Long> tunnelIds = tunnelsWithSameInNode.stream()
|
||||
.map(Tunnel::getId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 获取这些隧道的所有转发已使用的入口端口
|
||||
List<Forward> usedForwards = this.list(new QueryWrapper<Forward>().in("tunnel_id", tunnelIds));
|
||||
Set<Integer> usedInPorts = usedForwards.stream()
|
||||
.map(Forward::getInPort)
|
||||
.filter(port -> port != null)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 在隧道端口范围内寻找未使用的端口
|
||||
for (int port = tunnel.getInPortSta(); port <= tunnel.getInPortEnd(); port++) {
|
||||
if (!usedInPorts.contains(port)) {
|
||||
return port;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 为隧道分配一个可用的出口端口
|
||||
*/
|
||||
private Integer allocateOutPort(Tunnel tunnel) {
|
||||
// 获取所有使用相同出口节点的隧道
|
||||
List<Tunnel> tunnelsWithSameOutNode = tunnelService.list(new QueryWrapper<Tunnel>().eq("out_node_id", tunnel.getOutNodeId()));
|
||||
Set<Long> tunnelIds = tunnelsWithSameOutNode.stream()
|
||||
.map(Tunnel::getId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 获取这些隧道的所有转发已使用的出口端口
|
||||
List<Forward> usedForwards = this.list(new QueryWrapper<Forward>().in("tunnel_id", tunnelIds));
|
||||
Set<Integer> usedOutPorts = usedForwards.stream()
|
||||
.map(Forward::getOutPort)
|
||||
.filter(port -> port != null)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 在隧道出口端口范围内寻找未使用的端口
|
||||
for (int port = tunnel.getOutIpSta(); port <= tunnel.getOutIpEnd(); port++) {
|
||||
if (!usedOutPorts.contains(port)) {
|
||||
return port;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建服务名称,确保管理员和用户操作的一致性
|
||||
*/
|
||||
private String buildServiceName(Long forwardId, Integer userId, Integer tunnelId) {
|
||||
// 根据userId和tunnelId查询UserTunnel获取正确的user_tunnel_id
|
||||
UserTunnel userTunnel = userTunnelService.getOne(new QueryWrapper<UserTunnel>()
|
||||
.eq("user_id", userId)
|
||||
.eq("tunnel_id", tunnelId));
|
||||
|
||||
int userTunnelId = (userTunnel != null) ? userTunnel.getId() : 0;
|
||||
|
||||
return forwardId + "_" + userId + "_" + userTunnelId;
|
||||
}
|
||||
|
||||
// ========== 内部数据类 ==========
|
||||
|
||||
/**
|
||||
* 用户信息封装类
|
||||
*/
|
||||
@Data
|
||||
private static class UserInfo {
|
||||
private final Integer userId;
|
||||
private final Integer roleId;
|
||||
private final String userName;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户权限检查结果
|
||||
*/
|
||||
@Data
|
||||
private static class UserPermissionResult {
|
||||
private final boolean hasError;
|
||||
private final String errorMessage;
|
||||
private final Integer limiter;
|
||||
|
||||
private UserPermissionResult(boolean hasError, String errorMessage, Integer limiter) {
|
||||
this.hasError = hasError;
|
||||
this.errorMessage = errorMessage;
|
||||
this.limiter = limiter;
|
||||
}
|
||||
|
||||
public static UserPermissionResult success(Integer limiter) {
|
||||
return new UserPermissionResult(false, null, limiter);
|
||||
}
|
||||
|
||||
public static UserPermissionResult error(String errorMessage) {
|
||||
return new UserPermissionResult(true, errorMessage, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 端口分配结果
|
||||
*/
|
||||
@Data
|
||||
private static class PortAllocation {
|
||||
private final boolean hasError;
|
||||
private final String errorMessage;
|
||||
private final Integer inPort;
|
||||
private final Integer outPort;
|
||||
|
||||
private PortAllocation(boolean hasError, String errorMessage, Integer inPort, Integer outPort) {
|
||||
this.hasError = hasError;
|
||||
this.errorMessage = errorMessage;
|
||||
this.inPort = inPort;
|
||||
this.outPort = outPort;
|
||||
}
|
||||
|
||||
public static PortAllocation success(Integer inPort, Integer outPort) {
|
||||
return new PortAllocation(false, null, inPort, outPort);
|
||||
}
|
||||
|
||||
public static PortAllocation error(String errorMessage) {
|
||||
return new PortAllocation(true, errorMessage, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
package com.admin.service.impl;
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.admin.common.dto.NodeDto;
|
||||
import com.admin.common.dto.NodeUpdateDto;
|
||||
import com.admin.common.dto.PageDto;
|
||||
import com.admin.common.lang.R;
|
||||
import com.admin.entity.Node;
|
||||
import com.admin.entity.Tunnel;
|
||||
import com.admin.mapper.NodeMapper;
|
||||
import com.admin.mapper.TunnelMapper;
|
||||
import com.admin.service.NodeService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 节点服务实现类
|
||||
* 提供节点的增删改查功能,包括节点创建、更新、删除和查询操作
|
||||
* </p>
|
||||
*
|
||||
* @author QAQ
|
||||
* @since 2025-06-03
|
||||
*/
|
||||
@Service
|
||||
public class NodeServiceImpl extends ServiceImpl<NodeMapper, Node> implements NodeService {
|
||||
|
||||
// ========== 常量定义 ==========
|
||||
|
||||
/** 节点默认状态:启用 */
|
||||
private static final int NODE_STATUS_ACTIVE = 0;
|
||||
|
||||
/** 成功响应消息 */
|
||||
private static final String SUCCESS_CREATE_MSG = "节点创建成功";
|
||||
private static final String SUCCESS_UPDATE_MSG = "节点更新成功";
|
||||
private static final String SUCCESS_DELETE_MSG = "节点删除成功";
|
||||
|
||||
/** 错误响应消息 */
|
||||
private static final String ERROR_CREATE_MSG = "节点创建失败";
|
||||
private static final String ERROR_UPDATE_MSG = "节点更新失败";
|
||||
private static final String ERROR_DELETE_MSG = "节点删除失败";
|
||||
private static final String ERROR_NODE_NOT_FOUND = "节点不存在";
|
||||
|
||||
/** 隧道使用检查相关消息 */
|
||||
private static final String ERROR_IN_NODE_IN_USE = "该节点还有 %d 个隧道作为入口节点在使用,请先删除相关隧道";
|
||||
private static final String ERROR_OUT_NODE_IN_USE = "该节点还有 %d 个隧道作为出口节点在使用,请先删除相关隧道";
|
||||
|
||||
// ========== 依赖注入 ==========
|
||||
|
||||
@Resource
|
||||
private TunnelMapper tunnelMapper;
|
||||
|
||||
@Value("${server-addr}")
|
||||
private String serverAddr;
|
||||
|
||||
// ========== 公共接口实现 ==========
|
||||
|
||||
/**
|
||||
* 创建新节点
|
||||
*
|
||||
* @param nodeDto 节点创建数据传输对象
|
||||
* @return 创建结果响应
|
||||
*/
|
||||
@Override
|
||||
public R createNode(NodeDto nodeDto) {
|
||||
Node node = buildNewNode(nodeDto);
|
||||
boolean result = this.save(node);
|
||||
return result ? R.ok(SUCCESS_CREATE_MSG) : R.err(ERROR_CREATE_MSG);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取所有节点列表
|
||||
* 注意:返回结果中会隐藏节点密钥信息
|
||||
*
|
||||
* @return 包含所有节点的响应对象
|
||||
*/
|
||||
@Override
|
||||
public R getAllNodes() {
|
||||
List<Node> nodeList = this.list();
|
||||
hideNodeSecrets(nodeList);
|
||||
return R.ok(nodeList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新节点信息
|
||||
*
|
||||
* @param nodeUpdateDto 节点更新数据传输对象
|
||||
* @return 更新结果响应
|
||||
*/
|
||||
@Override
|
||||
public R updateNode(NodeUpdateDto nodeUpdateDto) {
|
||||
// 1. 验证节点是否存在
|
||||
if (!isNodeExists(nodeUpdateDto.getId())) {
|
||||
return R.err(ERROR_NODE_NOT_FOUND);
|
||||
}
|
||||
|
||||
// 2. 构建更新对象并执行更新
|
||||
Node updateNode = buildUpdateNode(nodeUpdateDto);
|
||||
boolean result = this.updateById(updateNode);
|
||||
|
||||
return result ? R.ok(SUCCESS_UPDATE_MSG) : R.err(ERROR_UPDATE_MSG);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除节点
|
||||
* 删除前会检查是否有隧道正在使用该节点
|
||||
*
|
||||
* @param id 节点ID
|
||||
* @return 删除结果响应
|
||||
*/
|
||||
@Override
|
||||
public R deleteNode(Long id) {
|
||||
// 1. 验证节点是否存在
|
||||
if (!isNodeExists(id)) {
|
||||
return R.err(ERROR_NODE_NOT_FOUND);
|
||||
}
|
||||
|
||||
// 2. 检查节点使用情况
|
||||
R usageCheckResult = checkNodeUsage(id);
|
||||
if (usageCheckResult.getCode() != 0) {
|
||||
return usageCheckResult;
|
||||
}
|
||||
|
||||
// 3. 执行删除操作
|
||||
boolean result = this.removeById(id);
|
||||
return result ? R.ok(SUCCESS_DELETE_MSG) : R.err(ERROR_DELETE_MSG);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取节点信息
|
||||
*
|
||||
* @param id 节点ID
|
||||
* @return 节点对象
|
||||
* @throws RuntimeException 当节点不存在时抛出异常
|
||||
*/
|
||||
@Override
|
||||
public Node getNodeById(Long id) {
|
||||
Node node = this.getById(id);
|
||||
if (node == null) {
|
||||
throw new RuntimeException(ERROR_NODE_NOT_FOUND);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
// ========== 私有辅助方法 ==========
|
||||
|
||||
/**
|
||||
* 构建新节点对象
|
||||
*
|
||||
* @param nodeDto 节点创建DTO
|
||||
* @return 构建完成的节点对象
|
||||
*/
|
||||
private Node buildNewNode(NodeDto nodeDto) {
|
||||
Node node = new Node();
|
||||
BeanUtils.copyProperties(nodeDto, node);
|
||||
|
||||
// 设置默认属性
|
||||
node.setSecret(IdUtil.simpleUUID());
|
||||
node.setStatus(NODE_STATUS_ACTIVE);
|
||||
|
||||
// 设置时间戳
|
||||
long currentTime = System.currentTimeMillis();
|
||||
node.setCreatedTime(currentTime);
|
||||
node.setUpdatedTime(currentTime);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建节点更新对象
|
||||
*
|
||||
* @param nodeUpdateDto 节点更新DTO
|
||||
* @return 构建完成的更新对象
|
||||
*/
|
||||
private Node buildUpdateNode(NodeUpdateDto nodeUpdateDto) {
|
||||
Node node = new Node();
|
||||
node.setId(nodeUpdateDto.getId());
|
||||
node.setName(nodeUpdateDto.getName());
|
||||
node.setIp(nodeUpdateDto.getIp());
|
||||
node.setUpdatedTime(System.currentTimeMillis());
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏节点列表中的密钥信息
|
||||
*
|
||||
* @param nodeList 节点列表
|
||||
*/
|
||||
private void hideNodeSecrets(List<Node> nodeList) {
|
||||
nodeList.forEach(node -> node.setSecret(null));
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查节点是否存在
|
||||
*
|
||||
* @param nodeId 节点ID
|
||||
* @return 节点是否存在
|
||||
*/
|
||||
private boolean isNodeExists(Long nodeId) {
|
||||
return this.getById(nodeId) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查节点使用情况
|
||||
* 验证是否有隧道正在使用该节点作为入口或出口节点
|
||||
*
|
||||
* @param nodeId 节点ID
|
||||
* @return 检查结果响应
|
||||
*/
|
||||
private R checkNodeUsage(Long nodeId) {
|
||||
// 检查入口节点使用情况
|
||||
R inNodeCheckResult = checkInNodeUsage(nodeId);
|
||||
if (inNodeCheckResult.getCode() != 0) {
|
||||
return inNodeCheckResult;
|
||||
}
|
||||
|
||||
// 检查出口节点使用情况
|
||||
return checkOutNodeUsage(nodeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查节点作为入口节点的使用情况
|
||||
*
|
||||
* @param nodeId 节点ID
|
||||
* @return 检查结果响应
|
||||
*/
|
||||
private R checkInNodeUsage(Long nodeId) {
|
||||
QueryWrapper<Tunnel> query = new QueryWrapper<>();
|
||||
query.eq("in_node_id", nodeId);
|
||||
|
||||
long tunnelCount = tunnelMapper.selectCount(query);
|
||||
if (tunnelCount > 0) {
|
||||
String errorMsg = String.format(ERROR_IN_NODE_IN_USE, tunnelCount);
|
||||
return R.err(errorMsg);
|
||||
}
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查节点作为出口节点的使用情况
|
||||
*
|
||||
* @param nodeId 节点ID
|
||||
* @return 检查结果响应
|
||||
*/
|
||||
private R checkOutNodeUsage(Long nodeId) {
|
||||
QueryWrapper<Tunnel> query = new QueryWrapper<>();
|
||||
query.eq("out_node_id", nodeId);
|
||||
|
||||
long tunnelCount = tunnelMapper.selectCount(query);
|
||||
if (tunnelCount > 0) {
|
||||
String errorMsg = String.format(ERROR_OUT_NODE_IN_USE, tunnelCount);
|
||||
return R.err(errorMsg);
|
||||
}
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取节点安装命令
|
||||
* 根据节点信息生成对应的安装命令
|
||||
*
|
||||
* @param id 节点ID
|
||||
* @return 包含安装命令的响应对象
|
||||
*/
|
||||
@Override
|
||||
public R getInstallCommand(Long id) {
|
||||
// 1. 验证节点是否存在
|
||||
Node node = this.getById(id);
|
||||
if (node == null) {
|
||||
return R.err(ERROR_NODE_NOT_FOUND);
|
||||
}
|
||||
|
||||
// 2. 构建安装命令
|
||||
String installCommand = buildInstallCommand(node);
|
||||
|
||||
return R.ok(installCommand);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建节点安装命令
|
||||
*
|
||||
* @param node 节点对象
|
||||
* @return 格式化的安装命令
|
||||
*/
|
||||
private String buildInstallCommand(Node node) {
|
||||
StringBuilder command = new StringBuilder();
|
||||
|
||||
// 第一部分:下载安装脚本
|
||||
command.append("curl -L https://raw.githubusercontent.com/bqlpfy/forward-panel/refs/heads/main/install.sh")
|
||||
.append(" -o ./install.sh && chmod +x ./install.sh && ");
|
||||
|
||||
// 第二部分:执行安装脚本(去掉-u参数)
|
||||
command.append("./install.sh")
|
||||
.append(" -a ").append(serverAddr) // 服务器地址
|
||||
.append(" -p ").append(node.getPort()) // 节点端口
|
||||
.append(" -s ").append(node.getSecret()); // 节点密钥
|
||||
|
||||
return command.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,407 @@
|
||||
package com.admin.service.impl;
|
||||
|
||||
import com.admin.common.dto.GostDto;
|
||||
import com.admin.common.dto.SpeedLimitDto;
|
||||
import com.admin.common.dto.SpeedLimitUpdateDto;
|
||||
import com.admin.common.lang.R;
|
||||
import com.admin.common.utils.GostUtil;
|
||||
import com.admin.entity.Node;
|
||||
import com.admin.entity.SpeedLimit;
|
||||
import com.admin.entity.Tunnel;
|
||||
import com.admin.entity.UserTunnel;
|
||||
import com.admin.mapper.SpeedLimitMapper;
|
||||
import com.admin.service.NodeService;
|
||||
import com.admin.service.SpeedLimitService;
|
||||
import com.admin.service.TunnelService;
|
||||
import com.admin.service.UserTunnelService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.Data;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 限速规则服务实现类
|
||||
* 提供限速规则的增删改查功能,包括与Gost服务的集成
|
||||
* 支持限速器的创建、更新、删除和查询操作
|
||||
* </p>
|
||||
*
|
||||
* @author QAQ
|
||||
* @since 2025-06-04
|
||||
*/
|
||||
@Service
|
||||
public class SpeedLimitServiceImpl extends ServiceImpl<SpeedLimitMapper, SpeedLimit> implements SpeedLimitService {
|
||||
|
||||
// ========== 常量定义 ==========
|
||||
|
||||
/** Gost操作成功响应消息 */
|
||||
private static final String GOST_SUCCESS_MSG = "OK";
|
||||
|
||||
/** Gost未找到资源响应消息 */
|
||||
private static final String GOST_NOT_FOUND_MSG = "not found";
|
||||
|
||||
/** 限速规则状态 */
|
||||
private static final int SPEED_LIMIT_ACTIVE_STATUS = 1;
|
||||
private static final int SPEED_LIMIT_INACTIVE_STATUS = 0;
|
||||
|
||||
/** 速度转换比率:比特到字节 */
|
||||
private static final double BITS_TO_BYTES_RATIO = 8.0;
|
||||
|
||||
/** 成功响应消息 */
|
||||
private static final String SUCCESS_UPDATE_MSG = "限速规则更新成功";
|
||||
private static final String SUCCESS_DELETE_MSG = "限速规则删除成功";
|
||||
|
||||
/** 错误响应消息 */
|
||||
private static final String ERROR_CREATE_MSG = "限速规则创建失败";
|
||||
private static final String ERROR_UPDATE_MSG = "限速规则更新失败";
|
||||
private static final String ERROR_DELETE_MSG = "限速规则删除失败";
|
||||
private static final String ERROR_SPEED_LIMIT_NOT_FOUND = "限速规则不存在";
|
||||
private static final String ERROR_TUNNEL_NOT_FOUND = "指定的隧道不存在";
|
||||
private static final String ERROR_TUNNEL_NOT_EXISTS = "隧道不存在";
|
||||
private static final String ERROR_TUNNEL_NAME_MISMATCH = "隧道名称与隧道ID不匹配";
|
||||
private static final String ERROR_SPEED_LIMIT_IN_USE = "该限速规则还有用户在使用 请先取消分配";
|
||||
|
||||
// ========== 依赖注入 ==========
|
||||
|
||||
@Autowired
|
||||
private TunnelService tunnelService;
|
||||
|
||||
@Autowired
|
||||
private NodeService nodeService;
|
||||
|
||||
@Autowired
|
||||
private UserTunnelService userTunnelService;
|
||||
|
||||
@Autowired
|
||||
@Lazy
|
||||
private SpeedLimitService speedLimitService;
|
||||
|
||||
// ========== 公共接口实现 ==========
|
||||
|
||||
/**
|
||||
* 创建限速规则
|
||||
*
|
||||
* @param speedLimitDto 限速规则创建数据传输对象
|
||||
* @return 创建结果响应
|
||||
*/
|
||||
@Override
|
||||
public R createSpeedLimit(SpeedLimitDto speedLimitDto) {
|
||||
// 1. 验证隧道
|
||||
TunnelValidationResult tunnelValidation = validateTunnelWithResult(speedLimitDto.getTunnelId(), speedLimitDto.getTunnelName());
|
||||
if (tunnelValidation.isHasError()) {
|
||||
return R.err(tunnelValidation.getErrorMessage());
|
||||
}
|
||||
|
||||
// 2. 创建限速规则实体
|
||||
SpeedLimit speedLimit = createSpeedLimitEntity(speedLimitDto);
|
||||
if (!this.save(speedLimit)) {
|
||||
return R.err(ERROR_CREATE_MSG);
|
||||
}
|
||||
|
||||
// 3. 调用Gost API添加限速器
|
||||
R gostResult = addGostLimiter(speedLimit, tunnelValidation.getTunnel());
|
||||
if (gostResult.getCode() != 0) {
|
||||
handleGostOperationFailure(speedLimit);
|
||||
this.removeById(speedLimit.getId());
|
||||
return gostResult;
|
||||
}
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有限速规则
|
||||
*
|
||||
* @return 包含所有限速规则的响应对象
|
||||
*/
|
||||
@Override
|
||||
public R getAllSpeedLimits() {
|
||||
List<SpeedLimit> speedLimits = this.list();
|
||||
return R.ok(speedLimits);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新限速规则
|
||||
*
|
||||
* @param speedLimitUpdateDto 限速规则更新数据传输对象
|
||||
* @return 更新结果响应
|
||||
*/
|
||||
@Override
|
||||
public R updateSpeedLimit(SpeedLimitUpdateDto speedLimitUpdateDto) {
|
||||
// 1. 验证限速规则是否存在
|
||||
SpeedLimit speedLimit = this.getById(speedLimitUpdateDto.getId());
|
||||
if (speedLimit == null) {
|
||||
return R.err(ERROR_SPEED_LIMIT_NOT_FOUND);
|
||||
}
|
||||
|
||||
// 2. 验证隧道
|
||||
TunnelValidationResult tunnelValidation = validateTunnelWithResult(speedLimitUpdateDto.getTunnelId(), speedLimitUpdateDto.getTunnelName());
|
||||
if (tunnelValidation.isHasError()) {
|
||||
return R.err(tunnelValidation.getErrorMessage());
|
||||
}
|
||||
|
||||
// 3. 更新限速规则数据
|
||||
updateSpeedLimitEntity(speedLimitUpdateDto, speedLimit);
|
||||
|
||||
// 4. 调用Gost API更新限速器
|
||||
R gostResult = updateGostLimiter(speedLimit, tunnelValidation.getTunnel());
|
||||
if (gostResult.getCode() != 0) {
|
||||
return gostResult;
|
||||
}
|
||||
|
||||
// 5. 保存更新
|
||||
boolean result = this.updateById(speedLimit);
|
||||
return result ? R.ok(SUCCESS_UPDATE_MSG) : R.err(ERROR_UPDATE_MSG);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除限速规则
|
||||
* 删除前会检查是否有用户正在使用该限速规则
|
||||
*
|
||||
* @param id 限速规则ID
|
||||
* @return 删除结果响应
|
||||
*/
|
||||
@Override
|
||||
public R deleteSpeedLimit(Long id) {
|
||||
// 1. 验证限速规则是否存在
|
||||
SpeedLimit speedLimit = this.getById(id);
|
||||
if (speedLimit == null) {
|
||||
return R.err(ERROR_SPEED_LIMIT_NOT_FOUND);
|
||||
}
|
||||
|
||||
// 2. 检查使用情况
|
||||
R usageCheckResult = checkSpeedLimitUsage(id);
|
||||
if (usageCheckResult.getCode() != 0) {
|
||||
return usageCheckResult;
|
||||
}
|
||||
|
||||
// 3. 获取隧道信息
|
||||
Tunnel tunnel = tunnelService.getById(speedLimit.getTunnelId());
|
||||
if (tunnel == null) {
|
||||
return R.err(ERROR_TUNNEL_NOT_EXISTS);
|
||||
}
|
||||
|
||||
// 4. 调用Gost API删除限速器
|
||||
R gostResult = deleteGostLimiter(id, tunnel);
|
||||
if (gostResult.getCode() != 0) {
|
||||
return gostResult;
|
||||
}
|
||||
|
||||
// 5. 删除限速规则
|
||||
boolean result = this.removeById(id);
|
||||
return result ? R.ok(SUCCESS_DELETE_MSG) : R.err(ERROR_DELETE_MSG);
|
||||
}
|
||||
|
||||
// ========== 私有辅助方法 ==========
|
||||
|
||||
/**
|
||||
* 验证隧道是否存在且名称匹配(返回详细结果)
|
||||
*
|
||||
* @param tunnelId 隧道ID
|
||||
* @param tunnelName 隧道名称
|
||||
* @return 隧道验证结果
|
||||
*/
|
||||
private TunnelValidationResult validateTunnelWithResult(Long tunnelId, String tunnelName) {
|
||||
Tunnel tunnel = tunnelService.getById(tunnelId);
|
||||
if (tunnel == null) {
|
||||
return TunnelValidationResult.error(ERROR_TUNNEL_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (!tunnel.getName().equals(tunnelName)) {
|
||||
return TunnelValidationResult.error(ERROR_TUNNEL_NAME_MISMATCH);
|
||||
}
|
||||
|
||||
return TunnelValidationResult.success(tunnel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证隧道是否存在且名称匹配(兼容原有方法)
|
||||
*
|
||||
* @param tunnelId 隧道ID
|
||||
* @param tunnelName 隧道名称
|
||||
* @return 验证结果响应
|
||||
*/
|
||||
private R validateTunnel(Long tunnelId, String tunnelName) {
|
||||
TunnelValidationResult result = validateTunnelWithResult(tunnelId, tunnelName);
|
||||
return result.isHasError() ? R.err(result.getErrorMessage()) : R.ok(result.getTunnel());
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建限速规则实体对象
|
||||
*
|
||||
* @param speedLimitDto 限速规则创建DTO
|
||||
* @return 构建完成的限速规则对象
|
||||
*/
|
||||
private SpeedLimit createSpeedLimitEntity(SpeedLimitDto speedLimitDto) {
|
||||
SpeedLimit speedLimit = new SpeedLimit();
|
||||
BeanUtils.copyProperties(speedLimitDto, speedLimit);
|
||||
|
||||
// 设置默认属性
|
||||
long currentTime = System.currentTimeMillis();
|
||||
speedLimit.setCreatedTime(currentTime);
|
||||
speedLimit.setUpdatedTime(currentTime);
|
||||
speedLimit.setStatus(SPEED_LIMIT_ACTIVE_STATUS);
|
||||
|
||||
return speedLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新限速规则实体对象
|
||||
*
|
||||
* @param speedLimitUpdateDto 限速规则更新DTO
|
||||
* @param speedLimit 待更新的限速规则对象
|
||||
*/
|
||||
private void updateSpeedLimitEntity(SpeedLimitUpdateDto speedLimitUpdateDto, SpeedLimit speedLimit) {
|
||||
BeanUtils.copyProperties(speedLimitUpdateDto, speedLimit);
|
||||
speedLimit.setUpdatedTime(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查限速规则使用情况
|
||||
*
|
||||
* @param speedLimitId 限速规则ID
|
||||
* @return 检查结果响应
|
||||
*/
|
||||
private R checkSpeedLimitUsage(Long speedLimitId) {
|
||||
int userCount = userTunnelService.count(new QueryWrapper<UserTunnel>().eq("speed_id", speedLimitId));
|
||||
if (userCount != 0) {
|
||||
return R.err(ERROR_SPEED_LIMIT_IN_USE);
|
||||
}
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加Gost限速器
|
||||
*
|
||||
* @param speedLimit 限速规则对象
|
||||
* @param tunnel 隧道对象
|
||||
* @return 操作结果响应
|
||||
*/
|
||||
private R addGostLimiter(SpeedLimit speedLimit, Tunnel tunnel) {
|
||||
String speedInMBps = convertBitsToMBps(speedLimit.getSpeed());
|
||||
Node node = nodeService.getNodeById(tunnel.getInNodeId());
|
||||
|
||||
GostDto gostResult = GostUtil.AddLimiters(
|
||||
buildNodeAddress(node),
|
||||
speedLimit.getId(),
|
||||
speedInMBps,
|
||||
node.getSecret()
|
||||
);
|
||||
|
||||
return isGostOperationSuccess(gostResult) ? R.ok() : R.err(gostResult.getMsg());
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新Gost限速器
|
||||
*
|
||||
* @param speedLimit 限速规则对象
|
||||
* @param tunnel 隧道对象
|
||||
* @return 操作结果响应
|
||||
*/
|
||||
private R updateGostLimiter(SpeedLimit speedLimit, Tunnel tunnel) {
|
||||
String speedInMBps = convertBitsToMBps(speedLimit.getSpeed());
|
||||
Node node = nodeService.getNodeById(tunnel.getInNodeId());
|
||||
String nodeAddress = buildNodeAddress(node);
|
||||
|
||||
// 尝试更新限速器
|
||||
GostDto gostResult = GostUtil.UpdateLimiters(nodeAddress, speedLimit.getId(), speedInMBps, node.getSecret());
|
||||
|
||||
// 如果限速器不存在,则创建新的
|
||||
if (gostResult.getMsg().contains(GOST_NOT_FOUND_MSG)) {
|
||||
gostResult = GostUtil.AddLimiters(nodeAddress, speedLimit.getId(), speedInMBps, node.getSecret());
|
||||
}
|
||||
|
||||
return isGostOperationSuccess(gostResult) ? R.ok() : R.err(gostResult.getMsg());
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除Gost限速器
|
||||
*
|
||||
* @param speedLimitId 限速规则ID
|
||||
* @param tunnel 隧道对象
|
||||
* @return 操作结果响应
|
||||
*/
|
||||
private R deleteGostLimiter(Long speedLimitId, Tunnel tunnel) {
|
||||
Node node = nodeService.getNodeById(tunnel.getInNodeId());
|
||||
GostDto gostResult = GostUtil.DeleteLimiters(buildNodeAddress(node), speedLimitId, node.getSecret());
|
||||
|
||||
return isGostOperationSuccess(gostResult) ? R.ok() : R.err(gostResult.getMsg());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理Gost操作失败的情况
|
||||
*
|
||||
* @param speedLimit 限速规则对象
|
||||
*/
|
||||
private void handleGostOperationFailure(SpeedLimit speedLimit) {
|
||||
speedLimit.setStatus(SPEED_LIMIT_INACTIVE_STATUS);
|
||||
speedLimitService.updateById(speedLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建节点地址
|
||||
*
|
||||
* @param node 节点对象
|
||||
* @return 节点地址字符串
|
||||
*/
|
||||
private String buildNodeAddress(Node node) {
|
||||
return node.getIp() + ":" + node.getPort();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将比特率转换为兆字节每秒
|
||||
*
|
||||
* @param speedInBits 比特率速度
|
||||
* @return 兆字节每秒字符串
|
||||
*/
|
||||
private String convertBitsToMBps(Integer speedInBits) {
|
||||
double mbs = speedInBits / BITS_TO_BYTES_RATIO;
|
||||
BigDecimal bd = new BigDecimal(mbs).setScale(1, RoundingMode.HALF_UP);
|
||||
return bd.doubleValue() + "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查Gost操作是否成功
|
||||
*
|
||||
* @param gostResult Gost操作结果
|
||||
* @return 是否成功
|
||||
*/
|
||||
private boolean isGostOperationSuccess(GostDto gostResult) {
|
||||
return Objects.equals(gostResult.getMsg(), GOST_SUCCESS_MSG);
|
||||
}
|
||||
|
||||
// ========== 内部数据类 ==========
|
||||
|
||||
/**
|
||||
* 隧道验证结果封装类
|
||||
*/
|
||||
@Data
|
||||
private static class TunnelValidationResult {
|
||||
private final boolean hasError;
|
||||
private final String errorMessage;
|
||||
private final Tunnel tunnel;
|
||||
|
||||
private TunnelValidationResult(boolean hasError, String errorMessage, Tunnel tunnel) {
|
||||
this.hasError = hasError;
|
||||
this.errorMessage = errorMessage;
|
||||
this.tunnel = tunnel;
|
||||
}
|
||||
|
||||
public static TunnelValidationResult success(Tunnel tunnel) {
|
||||
return new TunnelValidationResult(false, null, tunnel);
|
||||
}
|
||||
|
||||
public static TunnelValidationResult error(String errorMessage) {
|
||||
return new TunnelValidationResult(true, errorMessage, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,526 @@
|
||||
package com.admin.service.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.admin.common.dto.TunnelDto;
|
||||
import com.admin.common.dto.TunnelListDto;
|
||||
|
||||
import com.admin.common.lang.R;
|
||||
import com.admin.common.utils.JwtUtil;
|
||||
import com.admin.entity.Forward;
|
||||
import com.admin.entity.Node;
|
||||
import com.admin.entity.Tunnel;
|
||||
import com.admin.entity.User;
|
||||
import com.admin.entity.UserTunnel;
|
||||
import com.admin.mapper.TunnelMapper;
|
||||
import com.admin.mapper.UserTunnelMapper;
|
||||
import com.admin.service.ForwardService;
|
||||
import com.admin.service.NodeService;
|
||||
import com.admin.service.TunnelService;
|
||||
import com.admin.service.UserTunnelService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.Data;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 隧道服务实现类
|
||||
* 提供隧道的增删改查功能,包括隧道创建、删除和用户权限管理
|
||||
* 支持端口转发和隧道转发两种模式
|
||||
* </p>
|
||||
*
|
||||
* @author QAQ
|
||||
* @since 2025-06-03
|
||||
*/
|
||||
@Service
|
||||
public class TunnelServiceImpl extends ServiceImpl<TunnelMapper, Tunnel> implements TunnelService {
|
||||
|
||||
// ========== 常量定义 ==========
|
||||
|
||||
/** 隧道类型常量 */
|
||||
private static final int TUNNEL_TYPE_PORT_FORWARD = 1; // 端口转发
|
||||
private static final int TUNNEL_TYPE_TUNNEL_FORWARD = 2; // 隧道转发
|
||||
|
||||
/** 隧道状态常量 */
|
||||
private static final int TUNNEL_STATUS_ACTIVE = 1; // 启用状态
|
||||
|
||||
/** 节点状态常量 */
|
||||
private static final int NODE_STATUS_ONLINE = 1; // 节点在线状态
|
||||
|
||||
/** 用户角色常量 */
|
||||
private static final int ADMIN_ROLE_ID = 0; // 管理员角色ID
|
||||
|
||||
/** 成功响应消息 */
|
||||
private static final String SUCCESS_CREATE_MSG = "隧道创建成功";
|
||||
private static final String SUCCESS_DELETE_MSG = "隧道删除成功";
|
||||
|
||||
/** 错误响应消息 */
|
||||
private static final String ERROR_CREATE_MSG = "隧道创建失败";
|
||||
private static final String ERROR_DELETE_MSG = "隧道删除失败";
|
||||
private static final String ERROR_TUNNEL_NOT_FOUND = "隧道不存在";
|
||||
private static final String ERROR_TUNNEL_NAME_EXISTS = "隧道名称已存在";
|
||||
private static final String ERROR_IN_NODE_NOT_FOUND = "入口节点不存在";
|
||||
private static final String ERROR_OUT_NODE_NOT_FOUND = "出口节点不存在";
|
||||
private static final String ERROR_OUT_NODE_REQUIRED = "出口节点不能为空";
|
||||
private static final String ERROR_OUT_PORT_REQUIRED = "出口端口不能为空";
|
||||
private static final String ERROR_SAME_NODE_NOT_ALLOWED = "隧道转发模式下,入口和出口不能是同一个节点";
|
||||
private static final String ERROR_IN_PORT_RANGE_INVALID = "入口端口开始不能大于结束端口";
|
||||
private static final String ERROR_OUT_PORT_RANGE_INVALID = "出口端口开始不能大于结束端口";
|
||||
private static final String ERROR_NO_AVAILABLE_TUNNELS = "暂无可用隧道";
|
||||
private static final String ERROR_IN_NODE_OFFLINE = "入口节点当前离线,请确保节点正常运行";
|
||||
private static final String ERROR_OUT_NODE_OFFLINE = "出口节点当前离线,请确保节点正常运行";
|
||||
|
||||
/** 使用检查相关消息 */
|
||||
private static final String ERROR_FORWARDS_IN_USE = "该隧道还有 %d 个转发在使用,请先删除相关转发";
|
||||
private static final String ERROR_USER_PERMISSIONS_IN_USE = "该隧道还有 %d 个用户权限关联,请先取消用户权限分配";
|
||||
|
||||
// ========== 依赖注入 ==========
|
||||
|
||||
@Resource
|
||||
UserTunnelMapper userTunnelMapper;
|
||||
|
||||
@Resource
|
||||
NodeService nodeService;
|
||||
|
||||
@Resource
|
||||
ForwardService forwardService;
|
||||
|
||||
@Resource
|
||||
UserTunnelService userTunnelService;
|
||||
|
||||
// ========== 公共接口实现 ==========
|
||||
|
||||
/**
|
||||
* 创建隧道
|
||||
* 支持端口转发和隧道转发两种模式
|
||||
*
|
||||
* @param tunnelDto 隧道创建数据传输对象
|
||||
* @return 创建结果响应
|
||||
*/
|
||||
@Override
|
||||
public R createTunnel(TunnelDto tunnelDto) {
|
||||
// 1. 验证隧道名称唯一性
|
||||
R nameValidationResult = validateTunnelNameUniqueness(tunnelDto.getName());
|
||||
if (nameValidationResult.getCode() != 0) {
|
||||
return nameValidationResult;
|
||||
}
|
||||
|
||||
// 2. 验证入口节点和端口
|
||||
NodeValidationResult inNodeValidation = validateInNode(tunnelDto);
|
||||
if (inNodeValidation.isHasError()) {
|
||||
return R.err(inNodeValidation.getErrorMessage());
|
||||
}
|
||||
|
||||
// 3. 构建隧道实体
|
||||
Tunnel tunnel = buildTunnelEntity(tunnelDto, inNodeValidation.getNode());
|
||||
|
||||
// 4. 根据隧道类型设置出口参数
|
||||
R outNodeSetupResult = setupOutNodeParameters(tunnel, tunnelDto);
|
||||
if (outNodeSetupResult.getCode() != 0) {
|
||||
return outNodeSetupResult;
|
||||
}
|
||||
|
||||
// 5. 设置默认属性并保存
|
||||
setDefaultTunnelProperties(tunnel);
|
||||
boolean result = this.save(tunnel);
|
||||
|
||||
return result ? R.ok(SUCCESS_CREATE_MSG) : R.err(ERROR_CREATE_MSG);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有隧道列表
|
||||
*
|
||||
* @return 包含所有隧道的响应对象
|
||||
*/
|
||||
@Override
|
||||
public R getAllTunnels() {
|
||||
List<Tunnel> tunnelList = this.list();
|
||||
return R.ok(tunnelList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除隧道
|
||||
* 删除前会检查是否有转发或用户权限在使用该隧道
|
||||
*
|
||||
* @param id 隧道ID
|
||||
* @return 删除结果响应
|
||||
*/
|
||||
@Override
|
||||
public R deleteTunnel(Long id) {
|
||||
// 1. 验证隧道是否存在
|
||||
if (!isTunnelExists(id)) {
|
||||
return R.err(ERROR_TUNNEL_NOT_FOUND);
|
||||
}
|
||||
|
||||
// 2. 检查隧道使用情况
|
||||
R usageCheckResult = checkTunnelUsage(id);
|
||||
if (usageCheckResult.getCode() != 0) {
|
||||
return usageCheckResult;
|
||||
}
|
||||
|
||||
// 3. 执行删除操作
|
||||
boolean result = this.removeById(id);
|
||||
return result ? R.ok(SUCCESS_DELETE_MSG) : R.err(ERROR_DELETE_MSG);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户可用的隧道列表
|
||||
* 管理员可以看到所有启用的隧道,普通用户只能看到有权限的启用隧道
|
||||
*
|
||||
* @return 用户可用隧道列表响应
|
||||
*/
|
||||
@Override
|
||||
public R userTunnel() {
|
||||
UserInfo currentUser = getCurrentUserInfo();
|
||||
|
||||
// 根据用户角色获取隧道列表
|
||||
List<Tunnel> tunnelEntities = getUserAccessibleTunnels(currentUser);
|
||||
|
||||
// 转换为DTO并返回
|
||||
List<TunnelListDto> tunnelDtos = convertToTunnelListDtos(tunnelEntities);
|
||||
return R.ok(tunnelDtos);
|
||||
}
|
||||
|
||||
// ========== 私有辅助方法 ==========
|
||||
|
||||
/**
|
||||
* 获取当前用户信息
|
||||
*
|
||||
* @return 用户信息对象
|
||||
*/
|
||||
private UserInfo getCurrentUserInfo() {
|
||||
Integer roleId = JwtUtil.getRoleIdFromToken();
|
||||
Integer userId = JwtUtil.getUserIdFromToken();
|
||||
return new UserInfo(userId, roleId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证隧道名称唯一性
|
||||
*
|
||||
* @param tunnelName 隧道名称
|
||||
* @return 验证结果响应
|
||||
*/
|
||||
private R validateTunnelNameUniqueness(String tunnelName) {
|
||||
Tunnel existTunnel = this.getOne(new QueryWrapper<Tunnel>().eq("name", tunnelName));
|
||||
if (existTunnel != null) {
|
||||
return R.err(ERROR_TUNNEL_NAME_EXISTS);
|
||||
}
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证入口节点和端口
|
||||
*
|
||||
* @param tunnelDto 隧道创建DTO
|
||||
* @return 节点验证结果
|
||||
*/
|
||||
private NodeValidationResult validateInNode(TunnelDto tunnelDto) {
|
||||
// 验证入口节点是否存在
|
||||
Node inNode = nodeService.getById(tunnelDto.getInNodeId());
|
||||
if (inNode == null) {
|
||||
return NodeValidationResult.error(ERROR_IN_NODE_NOT_FOUND);
|
||||
}
|
||||
|
||||
// 验证入口节点是否在线
|
||||
if (inNode.getStatus() != NODE_STATUS_ONLINE) {
|
||||
return NodeValidationResult.error(ERROR_IN_NODE_OFFLINE);
|
||||
}
|
||||
|
||||
// 验证入口端口范围
|
||||
if (tunnelDto.getInPortSta() > tunnelDto.getInPortEnd()) {
|
||||
return NodeValidationResult.error(ERROR_IN_PORT_RANGE_INVALID);
|
||||
}
|
||||
|
||||
return NodeValidationResult.success(inNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建隧道实体对象
|
||||
*
|
||||
* @param tunnelDto 隧道创建DTO
|
||||
* @param inNode 入口节点
|
||||
* @return 构建完成的隧道对象
|
||||
*/
|
||||
private Tunnel buildTunnelEntity(TunnelDto tunnelDto, Node inNode) {
|
||||
Tunnel tunnel = new Tunnel();
|
||||
BeanUtils.copyProperties(tunnelDto, tunnel);
|
||||
|
||||
// 设置入口节点信息
|
||||
tunnel.setInNodeId(tunnelDto.getInNodeId());
|
||||
tunnel.setInIp(inNode.getIp());
|
||||
|
||||
// 设置流量计算类型
|
||||
tunnel.setFlow(tunnelDto.getFlow());
|
||||
|
||||
return tunnel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置出口节点参数
|
||||
*
|
||||
* @param tunnel 隧道对象
|
||||
* @param tunnelDto 隧道创建DTO
|
||||
* @return 设置结果响应
|
||||
*/
|
||||
private R setupOutNodeParameters(Tunnel tunnel, TunnelDto tunnelDto) {
|
||||
if (tunnelDto.getType() == TUNNEL_TYPE_PORT_FORWARD) {
|
||||
// 端口转发:出口参数使用入口参数
|
||||
return setupPortForwardOutParameters(tunnel, tunnelDto);
|
||||
} else {
|
||||
// 隧道转发:需要验证出口参数
|
||||
return setupTunnelForwardOutParameters(tunnel, tunnelDto);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置端口转发的出口参数
|
||||
*
|
||||
* @param tunnel 隧道对象
|
||||
* @param tunnelDto 隧道创建DTO
|
||||
* @return 设置结果响应
|
||||
*/
|
||||
private R setupPortForwardOutParameters(Tunnel tunnel, TunnelDto tunnelDto) {
|
||||
tunnel.setOutNodeId(tunnelDto.getInNodeId());
|
||||
tunnel.setOutIp(tunnel.getInIp());
|
||||
tunnel.setOutIpSta(tunnelDto.getInPortSta());
|
||||
tunnel.setOutIpEnd(tunnelDto.getInPortEnd());
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置隧道转发的出口参数
|
||||
*
|
||||
* @param tunnel 隧道对象
|
||||
* @param tunnelDto 隧道创建DTO
|
||||
* @return 设置结果响应
|
||||
*/
|
||||
private R setupTunnelForwardOutParameters(Tunnel tunnel, TunnelDto tunnelDto) {
|
||||
// 验证出口节点不能为空
|
||||
if (tunnelDto.getOutNodeId() == null) {
|
||||
return R.err(ERROR_OUT_NODE_REQUIRED);
|
||||
}
|
||||
|
||||
// 验证入口和出口不能是同一个节点
|
||||
if (tunnelDto.getInNodeId().equals(tunnelDto.getOutNodeId())) {
|
||||
return R.err(ERROR_SAME_NODE_NOT_ALLOWED);
|
||||
}
|
||||
|
||||
// 验证出口节点是否存在
|
||||
Node outNode = nodeService.getById(tunnelDto.getOutNodeId());
|
||||
if (outNode == null) {
|
||||
return R.err(ERROR_OUT_NODE_NOT_FOUND);
|
||||
}
|
||||
|
||||
// 验证出口节点是否在线
|
||||
if (outNode.getStatus() != NODE_STATUS_ONLINE) {
|
||||
return R.err(ERROR_OUT_NODE_OFFLINE);
|
||||
}
|
||||
|
||||
// 验证出口端口参数
|
||||
if (tunnelDto.getOutIpSta() == null || tunnelDto.getOutIpEnd() == null) {
|
||||
return R.err(ERROR_OUT_PORT_REQUIRED);
|
||||
}
|
||||
if (tunnelDto.getOutIpSta() > tunnelDto.getOutIpEnd()) {
|
||||
return R.err(ERROR_OUT_PORT_RANGE_INVALID);
|
||||
}
|
||||
|
||||
// 设置出口参数
|
||||
tunnel.setOutNodeId(tunnelDto.getOutNodeId());
|
||||
tunnel.setOutIp(outNode.getIp());
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置隧道默认属性
|
||||
*
|
||||
* @param tunnel 隧道对象
|
||||
*/
|
||||
private void setDefaultTunnelProperties(Tunnel tunnel) {
|
||||
tunnel.setStatus(TUNNEL_STATUS_ACTIVE);
|
||||
long currentTime = System.currentTimeMillis();
|
||||
tunnel.setCreatedTime(currentTime);
|
||||
tunnel.setUpdatedTime(currentTime);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 检查隧道是否存在
|
||||
*
|
||||
* @param tunnelId 隧道ID
|
||||
* @return 隧道是否存在
|
||||
*/
|
||||
private boolean isTunnelExists(Long tunnelId) {
|
||||
return this.getById(tunnelId) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查隧道使用情况
|
||||
*
|
||||
* @param tunnelId 隧道ID
|
||||
* @return 检查结果响应
|
||||
*/
|
||||
private R checkTunnelUsage(Long tunnelId) {
|
||||
// 检查转发使用情况
|
||||
R forwardCheckResult = checkForwardUsage(tunnelId);
|
||||
if (forwardCheckResult.getCode() != 0) {
|
||||
return forwardCheckResult;
|
||||
}
|
||||
|
||||
// 检查用户权限使用情况
|
||||
return checkUserPermissionUsage(tunnelId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查转发使用情况
|
||||
*
|
||||
* @param tunnelId 隧道ID
|
||||
* @return 检查结果响应
|
||||
*/
|
||||
private R checkForwardUsage(Long tunnelId) {
|
||||
QueryWrapper<Forward> forwardQuery = new QueryWrapper<>();
|
||||
forwardQuery.eq("tunnel_id", tunnelId);
|
||||
long forwardCount = forwardService.count(forwardQuery);
|
||||
|
||||
if (forwardCount > 0) {
|
||||
String errorMsg = String.format(ERROR_FORWARDS_IN_USE, forwardCount);
|
||||
return R.err(errorMsg);
|
||||
}
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户权限使用情况
|
||||
*
|
||||
* @param tunnelId 隧道ID
|
||||
* @return 检查结果响应
|
||||
*/
|
||||
private R checkUserPermissionUsage(Long tunnelId) {
|
||||
QueryWrapper<UserTunnel> userTunnelQuery = new QueryWrapper<>();
|
||||
userTunnelQuery.eq("tunnel_id", tunnelId);
|
||||
long userTunnelCount = userTunnelService.count(userTunnelQuery);
|
||||
|
||||
if (userTunnelCount > 0) {
|
||||
String errorMsg = String.format(ERROR_USER_PERMISSIONS_IN_USE, userTunnelCount);
|
||||
return R.err(errorMsg);
|
||||
}
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户可访问的隧道列表
|
||||
*
|
||||
* @param userInfo 用户信息
|
||||
* @return 隧道列表
|
||||
*/
|
||||
private List<Tunnel> getUserAccessibleTunnels(UserInfo userInfo) {
|
||||
if (userInfo.getRoleId() == ADMIN_ROLE_ID) {
|
||||
// 管理员:获取所有启用状态的隧道
|
||||
return getActiveTunnels();
|
||||
} else {
|
||||
// 普通用户:根据权限获取启用状态的隧道
|
||||
return getUserAuthorizedTunnels(userInfo.getUserId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有启用状态的隧道
|
||||
*
|
||||
* @return 启用状态的隧道列表
|
||||
*/
|
||||
private List<Tunnel> getActiveTunnels() {
|
||||
return this.list(new QueryWrapper<Tunnel>().eq("status", TUNNEL_STATUS_ACTIVE));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户有权限的启用隧道
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 用户有权限的隧道列表
|
||||
*/
|
||||
private List<Tunnel> getUserAuthorizedTunnels(Integer userId) {
|
||||
List<UserTunnel> userTunnels = userTunnelMapper.selectList(
|
||||
new QueryWrapper<UserTunnel>().eq("user_id", userId)
|
||||
);
|
||||
|
||||
if (userTunnels.isEmpty()) {
|
||||
return java.util.Collections.emptyList(); // 返回空列表
|
||||
}
|
||||
|
||||
List<Integer> tunnelIds = userTunnels.stream()
|
||||
.map(UserTunnel::getTunnelId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return this.list(new QueryWrapper<Tunnel>()
|
||||
.in("id", tunnelIds)
|
||||
.eq("status", TUNNEL_STATUS_ACTIVE));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将隧道实体列表转换为DTO列表
|
||||
*
|
||||
* @param tunnelEntities 隧道实体列表
|
||||
* @return 隧道DTO列表
|
||||
*/
|
||||
private List<TunnelListDto> convertToTunnelListDtos(List<Tunnel> tunnelEntities) {
|
||||
return tunnelEntities.stream()
|
||||
.map(this::convertToTunnelListDto)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 将Tunnel实体转换为TunnelListDto
|
||||
*
|
||||
* @param tunnel 隧道实体
|
||||
* @return 隧道列表DTO
|
||||
*/
|
||||
private TunnelListDto convertToTunnelListDto(Tunnel tunnel) {
|
||||
TunnelListDto dto = new TunnelListDto();
|
||||
dto.setId(tunnel.getId().intValue());
|
||||
dto.setName(tunnel.getName());
|
||||
return dto;
|
||||
}
|
||||
|
||||
// ========== 内部数据类 ==========
|
||||
|
||||
/**
|
||||
* 用户信息封装类
|
||||
*/
|
||||
@Data
|
||||
private static class UserInfo {
|
||||
private final Integer userId;
|
||||
private final Integer roleId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 节点验证结果封装类
|
||||
*/
|
||||
@Data
|
||||
private static class NodeValidationResult {
|
||||
private final boolean hasError;
|
||||
private final String errorMessage;
|
||||
private final Node node;
|
||||
|
||||
private NodeValidationResult(boolean hasError, String errorMessage, Node node) {
|
||||
this.hasError = hasError;
|
||||
this.errorMessage = errorMessage;
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
public static NodeValidationResult success(Node node) {
|
||||
return new NodeValidationResult(false, null, node);
|
||||
}
|
||||
|
||||
public static NodeValidationResult error(String errorMessage) {
|
||||
return new NodeValidationResult(true, errorMessage, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,793 @@
|
||||
package com.admin.service.impl;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.admin.common.dto.ChangePasswordDto;
|
||||
import com.admin.common.dto.LoginDto;
|
||||
import com.admin.common.dto.PageDto;
|
||||
import com.admin.common.dto.UserDto;
|
||||
import com.admin.common.dto.UserUpdateDto;
|
||||
import com.admin.common.dto.UserPackageDto;
|
||||
import com.admin.common.dto.GostDto;
|
||||
import com.admin.common.lang.R;
|
||||
import com.admin.common.task.DelayQueueManager;
|
||||
import com.admin.common.task.DelayTask;
|
||||
import com.admin.common.task.TaskBase;
|
||||
import com.admin.common.utils.GostUtil;
|
||||
import com.admin.common.utils.JwtUtil;
|
||||
import com.admin.common.utils.Md5Util;
|
||||
import com.admin.entity.Forward;
|
||||
import com.admin.entity.Node;
|
||||
import com.admin.entity.Tunnel;
|
||||
import com.admin.entity.User;
|
||||
import com.admin.entity.UserTunnel;
|
||||
import com.admin.mapper.ForwardMapper;
|
||||
import com.admin.mapper.UserMapper;
|
||||
import com.admin.mapper.UserTunnelMapper;
|
||||
import com.admin.service.NodeService;
|
||||
import com.admin.service.TunnelService;
|
||||
import com.admin.service.UserService;
|
||||
import com.admin.service.UserTunnelService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 用户服务实现类
|
||||
* 提供用户的增删改查功能,包括用户登录、创建、更新、删除和套餐信息查询
|
||||
* 支持用户关联数据的级联删除,包括转发和Gost服务的清理
|
||||
* </p>
|
||||
*
|
||||
* @author QAQ
|
||||
* @since 2025-06-03
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
|
||||
|
||||
// ========== 常量定义 ==========
|
||||
|
||||
/** 用户角色常量 */
|
||||
private static final int ADMIN_ROLE_ID = 0; // 管理员角色ID
|
||||
private static final int USER_ROLE_ID = 1; // 普通用户角色ID
|
||||
private static final long ADMIN_USER_ID = 1L; // 管理员用户ID
|
||||
|
||||
/** 用户状态常量 */
|
||||
private static final int USER_STATUS_ACTIVE = 1; // 用户启用状态
|
||||
private static final int USER_STATUS_DISABLED = 0; // 用户停用状态
|
||||
|
||||
/** 隧道类型常量 */
|
||||
private static final int TUNNEL_TYPE_TUNNEL_FORWARD = 2; // 隧道转发类型
|
||||
|
||||
/** 成功响应消息 */
|
||||
private static final String SUCCESS_CREATE_MSG = "用户创建成功";
|
||||
private static final String SUCCESS_UPDATE_MSG = "用户更新成功";
|
||||
private static final String SUCCESS_DELETE_MSG = "用户及关联数据删除成功";
|
||||
|
||||
/** 错误响应消息 */
|
||||
private static final String ERROR_LOGIN_CREDENTIALS = "账号或密码错误";
|
||||
private static final String ERROR_ACCOUNT_DISABLED = "账户停用";
|
||||
private static final String ERROR_CREATE_FAILED = "用户创建失败";
|
||||
private static final String ERROR_UPDATE_FAILED = "用户更新失败";
|
||||
private static final String ERROR_DELETE_FAILED = "用户删除失败";
|
||||
private static final String ERROR_USER_NOT_FOUND = "用户不存在";
|
||||
private static final String ERROR_USERNAME_EXISTS = "用户名已存在";
|
||||
private static final String ERROR_USERNAME_TAKEN = "用户名已被其他用户使用";
|
||||
private static final String ERROR_CANNOT_DELETE_ADMIN = "不能删除管理员用户";
|
||||
private static final String ERROR_USER_NOT_LOGGED_IN = "用户未登录或token无效";
|
||||
private static final String ERROR_GET_PACKAGE_INFO_FAILED = "获取套餐信息失败";
|
||||
private static final String ERROR_CURRENT_PASSWORD_WRONG = "当前密码错误";
|
||||
private static final String ERROR_PASSWORD_NOT_MATCH = "新密码和确认密码不匹配";
|
||||
private static final String SUCCESS_PASSWORD_UPDATE = "密码修改成功";
|
||||
|
||||
/** 登录响应字段名 */
|
||||
private static final String LOGIN_TOKEN_FIELD = "token";
|
||||
private static final String LOGIN_NAME_FIELD = "name";
|
||||
private static final String LOGIN_ROLE_ID_FIELD = "role_id";
|
||||
|
||||
// ========== 依赖注入 ==========
|
||||
|
||||
@Resource
|
||||
private UserMapper userMapper;
|
||||
|
||||
@Resource
|
||||
@Lazy
|
||||
private ForwardMapper forwardMapper;
|
||||
|
||||
@Resource
|
||||
private UserTunnelMapper userTunnelMapper;
|
||||
|
||||
@Resource
|
||||
@Lazy
|
||||
private TunnelService tunnelService;
|
||||
|
||||
@Resource
|
||||
@Lazy
|
||||
private NodeService nodeService;
|
||||
|
||||
@Resource
|
||||
UserTunnelService userTunnelService;
|
||||
|
||||
@Resource
|
||||
private DelayQueueManager delayQueueManager;
|
||||
|
||||
// ========== 公共接口实现 ==========
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
* 验证用户名密码,检查账户状态,生成JWT令牌
|
||||
*
|
||||
* @param loginDto 登录数据传输对象
|
||||
* @return 登录结果响应,包含令牌和用户信息
|
||||
*/
|
||||
@Override
|
||||
public R login(LoginDto loginDto) {
|
||||
// 1. 验证用户凭据
|
||||
LoginValidationResult validationResult = validateUserCredentials(loginDto);
|
||||
if (validationResult.isHasError()) {
|
||||
return R.err(validationResult.getErrorMessage());
|
||||
}
|
||||
|
||||
// 2. 生成令牌并返回用户信息
|
||||
User user = validationResult.getUser();
|
||||
String token = JwtUtil.generateToken(user);
|
||||
|
||||
return R.ok(MapUtil.builder()
|
||||
.put(LOGIN_TOKEN_FIELD, token)
|
||||
.put(LOGIN_NAME_FIELD, user.getName())
|
||||
.put(LOGIN_ROLE_ID_FIELD, user.getRoleId())
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建用户
|
||||
* 检查用户名唯一性,设置默认属性,加密密码
|
||||
*
|
||||
* @param userDto 用户创建数据传输对象
|
||||
* @return 创建结果响应
|
||||
*/
|
||||
@Override
|
||||
public R createUser(UserDto userDto) {
|
||||
// 1. 验证用户名唯一性
|
||||
R usernameValidationResult = validateUsernameUniqueness(userDto.getUser(), null);
|
||||
if (usernameValidationResult.getCode() != 0) {
|
||||
return usernameValidationResult;
|
||||
}
|
||||
|
||||
// 2. 构建用户实体并保存
|
||||
User user = buildNewUserEntity(userDto);
|
||||
boolean result = this.save(user);
|
||||
|
||||
if (result) {
|
||||
// 3. 添加到期时间延时任务
|
||||
scheduleUserExpirationTask(user);
|
||||
return R.ok(SUCCESS_CREATE_MSG);
|
||||
} else {
|
||||
return R.err(ERROR_CREATE_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有用户(分页)
|
||||
* 支持关键字搜索,排除管理员用户,清除密码信息
|
||||
*
|
||||
* @param pageDto 分页查询数据传输对象
|
||||
* @return 分页用户列表响应
|
||||
*/
|
||||
@Override
|
||||
public R getAllUsers(PageDto pageDto) {
|
||||
// 1. 构建分页查询
|
||||
Page<User> page = new Page<>(pageDto.getCurrent(), pageDto.getSize());
|
||||
QueryWrapper<User> queryWrapper = buildUserQueryWrapper(pageDto);
|
||||
|
||||
// 2. 执行查询并处理结果
|
||||
Page<User> userPage = this.page(page, queryWrapper);
|
||||
clearUserPasswords(userPage.getRecords());
|
||||
|
||||
return R.ok(userPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户信息
|
||||
* 验证用户存在性和用户名唯一性,处理密码加密
|
||||
*
|
||||
* @param userUpdateDto 用户更新数据传输对象
|
||||
* @return 更新结果响应
|
||||
*/
|
||||
@Override
|
||||
public R updateUser(UserUpdateDto userUpdateDto) {
|
||||
// 1. 验证用户是否存在
|
||||
if (!isUserExists(userUpdateDto.getId())) {
|
||||
return R.err(ERROR_USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
// 2. 验证用户名唯一性
|
||||
R usernameValidationResult = validateUsernameUniqueness(userUpdateDto.getUser(), userUpdateDto.getId());
|
||||
if (usernameValidationResult.getCode() != 0) {
|
||||
return usernameValidationResult;
|
||||
}
|
||||
|
||||
|
||||
// 4. 构建更新实体并保存
|
||||
User user = buildUpdateUserEntity(userUpdateDto);
|
||||
boolean result = this.updateById(user);
|
||||
|
||||
if (result) {
|
||||
// 5. 处理到期时间延时任务
|
||||
handleUserExpirationTaskUpdate(user);
|
||||
return R.ok(SUCCESS_UPDATE_MSG);
|
||||
} else {
|
||||
return R.err(ERROR_UPDATE_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户
|
||||
* 级联删除用户相关的所有数据,包括转发、Gost服务和隧道权限
|
||||
*
|
||||
* @param id 用户ID
|
||||
* @return 删除结果响应
|
||||
*/
|
||||
@Override
|
||||
public R deleteUser(Long id) {
|
||||
// 1. 验证删除条件
|
||||
R deleteValidationResult = validateUserDeletion(id);
|
||||
if (deleteValidationResult.getCode() != 0) {
|
||||
return deleteValidationResult;
|
||||
}
|
||||
|
||||
try {
|
||||
// 2. 级联删除用户相关数据
|
||||
deleteUserRelatedData(id);
|
||||
delayQueueManager.remove("user_exp_" + id);
|
||||
// 3. 删除用户
|
||||
boolean result = this.removeById(id);
|
||||
return result ? R.ok(SUCCESS_DELETE_MSG) : R.err(ERROR_DELETE_FAILED);
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return R.err("删除用户时发生错误:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户套餐信息
|
||||
* 包括用户基本信息、隧道权限详情和转发详情
|
||||
*
|
||||
* @return 用户套餐信息响应
|
||||
*/
|
||||
@Override
|
||||
public R getUserPackageInfo() {
|
||||
try {
|
||||
// 1. 获取当前用户信息
|
||||
CurrentUserInfo currentUser = getCurrentUserInfo();
|
||||
if (currentUser.isHasError()) {
|
||||
return R.err(currentUser.getErrorMessage());
|
||||
}
|
||||
|
||||
// 2. 构建套餐信息
|
||||
UserPackageDto packageDto = buildUserPackageDto(currentUser);
|
||||
|
||||
return R.ok(packageDto);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return R.err(ERROR_GET_PACKAGE_INFO_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
* 验证当前密码、新密码确认、更新用户密码
|
||||
*
|
||||
* @param changePasswordDto 修改密码数据传输对象
|
||||
* @return 修改结果响应
|
||||
*/
|
||||
@Override
|
||||
public R updatePassword(ChangePasswordDto changePasswordDto) {
|
||||
try {
|
||||
// 1. 获取当前用户信息
|
||||
CurrentUserInfo currentUser = getCurrentUserInfo();
|
||||
if (currentUser.isHasError()) {
|
||||
return R.err(currentUser.getErrorMessage());
|
||||
}
|
||||
|
||||
// 2. 验证新密码和确认密码是否匹配
|
||||
if (!changePasswordDto.getNewPassword().equals(changePasswordDto.getConfirmPassword())) {
|
||||
return R.err(ERROR_PASSWORD_NOT_MATCH);
|
||||
}
|
||||
|
||||
// 3. 验证当前密码是否正确
|
||||
User user = currentUser.getUser();
|
||||
String currentPasswordMd5 = Md5Util.md5(changePasswordDto.getCurrentPassword());
|
||||
if (!user.getPwd().equals(currentPasswordMd5)) {
|
||||
return R.err(ERROR_CURRENT_PASSWORD_WRONG);
|
||||
}
|
||||
|
||||
// 4. 更新密码
|
||||
User updateUser = new User();
|
||||
updateUser.setId(user.getId());
|
||||
updateUser.setPwd(Md5Util.md5(changePasswordDto.getNewPassword()));
|
||||
updateUser.setUpdatedTime(System.currentTimeMillis());
|
||||
|
||||
boolean result = this.updateById(updateUser);
|
||||
return result ? R.ok(SUCCESS_PASSWORD_UPDATE) : R.err(ERROR_UPDATE_FAILED);
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return R.err("修改密码时发生错误:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 私有辅助方法 ==========
|
||||
|
||||
/**
|
||||
* 验证用户登录凭据
|
||||
*
|
||||
* @param loginDto 登录数据传输对象
|
||||
* @return 登录验证结果
|
||||
*/
|
||||
private LoginValidationResult validateUserCredentials(LoginDto loginDto) {
|
||||
User user = this.getOne(new QueryWrapper<User>().eq("user", loginDto.getUsername()));
|
||||
if (user == null) {
|
||||
return LoginValidationResult.error(ERROR_LOGIN_CREDENTIALS);
|
||||
}
|
||||
|
||||
if (!user.getPwd().equals(Md5Util.md5(loginDto.getPassword()))) {
|
||||
return LoginValidationResult.error(ERROR_LOGIN_CREDENTIALS);
|
||||
}
|
||||
|
||||
if (user.getStatus() == USER_STATUS_DISABLED) {
|
||||
return LoginValidationResult.error(ERROR_ACCOUNT_DISABLED);
|
||||
}
|
||||
|
||||
return LoginValidationResult.success(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户名唯一性
|
||||
*
|
||||
* @param username 用户名
|
||||
* @param excludeUserId 排除的用户ID(用于更新时排除自己)
|
||||
* @return 验证结果响应
|
||||
*/
|
||||
private R validateUsernameUniqueness(String username, Long excludeUserId) {
|
||||
QueryWrapper<User> queryWrapper = new QueryWrapper<User>().eq("user", username);
|
||||
if (excludeUserId != null) {
|
||||
queryWrapper.ne("id", excludeUserId);
|
||||
}
|
||||
|
||||
User existUser = this.getOne(queryWrapper);
|
||||
if (existUser != null) {
|
||||
String errorMsg = excludeUserId != null ? ERROR_USERNAME_TAKEN : ERROR_USERNAME_EXISTS;
|
||||
return R.err(errorMsg);
|
||||
}
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建新用户实体对象
|
||||
*
|
||||
* @param userDto 用户创建DTO
|
||||
* @return 构建完成的用户对象
|
||||
*/
|
||||
private User buildNewUserEntity(UserDto userDto) {
|
||||
User user = new User();
|
||||
BeanUtils.copyProperties(userDto, user);
|
||||
|
||||
// 设置加密密码
|
||||
user.setPwd(Md5Util.md5(userDto.getPwd()));
|
||||
|
||||
// 设置默认属性
|
||||
user.setStatus(userDto.getStatus() != null ? userDto.getStatus() : USER_STATUS_ACTIVE);
|
||||
user.setRoleId(USER_ROLE_ID);
|
||||
|
||||
// 设置时间戳
|
||||
long currentTime = System.currentTimeMillis();
|
||||
user.setCreatedTime(currentTime);
|
||||
user.setUpdatedTime(currentTime);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建用户查询条件
|
||||
*
|
||||
* @param pageDto 分页查询DTO
|
||||
* @return 查询条件包装器
|
||||
*/
|
||||
private QueryWrapper<User> buildUserQueryWrapper(PageDto pageDto) {
|
||||
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
|
||||
|
||||
// 关键字搜索
|
||||
if (StrUtil.isNotBlank(pageDto.getKeyword())) {
|
||||
queryWrapper.and(wrapper -> wrapper
|
||||
.like("name", pageDto.getKeyword())
|
||||
.or()
|
||||
.like("user", pageDto.getKeyword())
|
||||
);
|
||||
}
|
||||
|
||||
// 排除管理员用户
|
||||
queryWrapper.ne("id", ADMIN_USER_ID);
|
||||
|
||||
// 按更新时间降序排列
|
||||
queryWrapper.orderByDesc("updated_time");
|
||||
|
||||
return queryWrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除用户列表中的密码信息
|
||||
*
|
||||
* @param users 用户列表
|
||||
*/
|
||||
private void clearUserPasswords(List<User> users) {
|
||||
users.forEach(user -> user.setPwd(null));
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户是否存在
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 用户是否存在
|
||||
*/
|
||||
private boolean isUserExists(Long userId) {
|
||||
return this.getById(userId) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建用户更新实体对象
|
||||
*
|
||||
* @param userUpdateDto 用户更新DTO
|
||||
* @return 构建完成的更新对象
|
||||
*/
|
||||
private User buildUpdateUserEntity(UserUpdateDto userUpdateDto) {
|
||||
User user = new User();
|
||||
BeanUtils.copyProperties(userUpdateDto, user);
|
||||
|
||||
// 处理密码更新
|
||||
if (StrUtil.isNotBlank(userUpdateDto.getPwd())) {
|
||||
user.setPwd(Md5Util.md5(userUpdateDto.getPwd()));
|
||||
} else {
|
||||
user.setPwd(null); // 不更新密码字段
|
||||
}
|
||||
|
||||
// 设置更新时间
|
||||
user.setUpdatedTime(System.currentTimeMillis());
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户删除条件
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 验证结果响应
|
||||
*/
|
||||
private R validateUserDeletion(Long userId) {
|
||||
User user = this.getById(userId);
|
||||
if (user == null) {
|
||||
return R.err(ERROR_USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (user.getRoleId() == ADMIN_ROLE_ID) {
|
||||
return R.err(ERROR_CANNOT_DELETE_ADMIN);
|
||||
}
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户相关的所有数据
|
||||
*
|
||||
* @param userId 用户ID
|
||||
*/
|
||||
private void deleteUserRelatedData(Long userId) {
|
||||
// 1. 删除用户的所有转发和对应的Gost服务
|
||||
deleteUserForwardsAndGostServices(userId);
|
||||
|
||||
// 2. 删除用户隧道权限
|
||||
deleteUserTunnelPermissions(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户转发和对应的Gost服务
|
||||
*
|
||||
* @param userId 用户ID
|
||||
*/
|
||||
private void deleteUserForwardsAndGostServices(Long userId) {
|
||||
QueryWrapper<Forward> forwardQuery = new QueryWrapper<>();
|
||||
forwardQuery.eq("user_id", userId);
|
||||
List<Forward> userForwards = forwardMapper.selectList(forwardQuery);
|
||||
|
||||
for (Forward forward : userForwards) {
|
||||
try {
|
||||
// 删除Gost服务
|
||||
deleteGostServicesForForward(forward, userId);
|
||||
} catch (Exception e) {
|
||||
// 记录错误但继续删除,避免因为Gost服务删除失败而阻断用户删除
|
||||
System.err.println("删除用户转发对应的Gost服务失败,转发ID: " + forward.getId() + ", 错误: " + e.getMessage());
|
||||
}
|
||||
|
||||
// 删除数据库中的转发记录
|
||||
forwardMapper.deleteById(forward.getId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除转发对应的Gost服务
|
||||
*
|
||||
* @param forward 转发对象
|
||||
* @param userId 用户ID
|
||||
*/
|
||||
private void deleteGostServicesForForward(Forward forward, Long userId) {
|
||||
Tunnel tunnel = tunnelService.getById(forward.getTunnelId());
|
||||
if (tunnel == null) return;
|
||||
|
||||
Node inNode = nodeService.getNodeById(tunnel.getInNodeId());
|
||||
if (inNode == null) return;
|
||||
|
||||
// 获取用户隧道关系
|
||||
UserTunnel userTunnel = getUserTunnelRelation(userId, tunnel.getId());
|
||||
if (userTunnel == null) return;
|
||||
|
||||
String serviceName = buildServiceName(forward.getId(), userId, userTunnel.getId());
|
||||
|
||||
// 删除主服务
|
||||
GostUtil.DeleteService(buildNodeAddress(inNode), serviceName, inNode.getSecret());
|
||||
|
||||
// 如果是隧道转发,还需要删除链和远程服务
|
||||
if (tunnel.getType() == TUNNEL_TYPE_TUNNEL_FORWARD) {
|
||||
deleteGostTunnelForwardServices(tunnel, serviceName, inNode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除隧道转发相关的Gost服务
|
||||
*
|
||||
* @param tunnel 隧道对象
|
||||
* @param serviceName 服务名称
|
||||
* @param inNode 入口节点
|
||||
*/
|
||||
private void deleteGostTunnelForwardServices(Tunnel tunnel, String serviceName, Node inNode) {
|
||||
Node outNode = nodeService.getNodeById(tunnel.getOutNodeId());
|
||||
if (outNode != null) {
|
||||
GostUtil.DeleteChains(buildNodeAddress(inNode), serviceName, inNode.getSecret());
|
||||
GostUtil.DeleteRemoteService(buildNodeAddress(outNode), serviceName, outNode.getSecret());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户隧道关系
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param tunnelId 隧道ID
|
||||
* @return 用户隧道关系对象
|
||||
*/
|
||||
private UserTunnel getUserTunnelRelation(Long userId, Long tunnelId) {
|
||||
return userTunnelService.getOne(new QueryWrapper<UserTunnel>()
|
||||
.eq("user_id", userId)
|
||||
.eq("tunnel_id", tunnelId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建服务名称
|
||||
*
|
||||
* @param forwardId 转发ID
|
||||
* @param userId 用户ID
|
||||
* @param userTunnelId 用户隧道ID
|
||||
* @return 服务名称
|
||||
*/
|
||||
private String buildServiceName(Long forwardId, Long userId, Integer userTunnelId) {
|
||||
return forwardId + "_" + userId + "_" + userTunnelId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建节点地址
|
||||
*
|
||||
* @param node 节点对象
|
||||
* @return 节点地址字符串
|
||||
*/
|
||||
private String buildNodeAddress(Node node) {
|
||||
return node.getIp() + ":" + node.getPort();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户隧道权限
|
||||
*
|
||||
* @param userId 用户ID
|
||||
*/
|
||||
private void deleteUserTunnelPermissions(Long userId) {
|
||||
QueryWrapper<UserTunnel> userTunnelQuery = new QueryWrapper<>();
|
||||
userTunnelQuery.eq("user_id", userId);
|
||||
userTunnelMapper.delete(userTunnelQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户信息
|
||||
*
|
||||
* @return 当前用户信息结果
|
||||
*/
|
||||
private CurrentUserInfo getCurrentUserInfo() {
|
||||
Integer userId = JwtUtil.getUserIdFromToken();
|
||||
Integer roleId = JwtUtil.getRoleIdFromToken();
|
||||
|
||||
if (userId == null) {
|
||||
return CurrentUserInfo.error(ERROR_USER_NOT_LOGGED_IN);
|
||||
}
|
||||
|
||||
User user = this.getById(userId);
|
||||
if (user == null) {
|
||||
return CurrentUserInfo.error(ERROR_USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
return CurrentUserInfo.success(user, roleId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建用户套餐信息DTO
|
||||
*
|
||||
* @param currentUser 当前用户信息
|
||||
* @return 用户套餐信息DTO
|
||||
*/
|
||||
private UserPackageDto buildUserPackageDto(CurrentUserInfo currentUser) {
|
||||
User user = currentUser.getUser();
|
||||
Integer roleId = currentUser.getRoleId();
|
||||
|
||||
// 1. 构造用户基本信息
|
||||
UserPackageDto.UserInfoDto userInfo = buildUserInfoDto(user);
|
||||
|
||||
// 2. 获取隧道权限详情
|
||||
List<UserPackageDto.UserTunnelDetailDto> tunnelPermissions = getTunnelPermissions(user.getId(), roleId);
|
||||
|
||||
// 3. 获取转发详情
|
||||
List<UserPackageDto.UserForwardDetailDto> forwards = userMapper.getUserForwardDetails(user.getId().intValue());
|
||||
|
||||
// 4. 构造返回结果
|
||||
UserPackageDto packageDto = new UserPackageDto();
|
||||
packageDto.setUserInfo(userInfo);
|
||||
packageDto.setTunnelPermissions(tunnelPermissions);
|
||||
packageDto.setForwards(forwards);
|
||||
|
||||
return packageDto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建用户基本信息DTO
|
||||
*
|
||||
* @param user 用户对象
|
||||
* @return 用户基本信息DTO
|
||||
*/
|
||||
private UserPackageDto.UserInfoDto buildUserInfoDto(User user) {
|
||||
UserPackageDto.UserInfoDto userInfo = new UserPackageDto.UserInfoDto();
|
||||
userInfo.setId(user.getId());
|
||||
userInfo.setName(user.getName());
|
||||
userInfo.setUser(user.getUser());
|
||||
userInfo.setStatus(user.getStatus());
|
||||
userInfo.setFlow(user.getFlow());
|
||||
userInfo.setInFlow(user.getInFlow());
|
||||
userInfo.setOutFlow(user.getOutFlow());
|
||||
userInfo.setNum(user.getNum());
|
||||
userInfo.setExpTime(user.getExpTime());
|
||||
userInfo.setFlowResetTime(user.getFlowResetTime());
|
||||
userInfo.setCreatedTime(user.getCreatedTime());
|
||||
userInfo.setUpdatedTime(user.getUpdatedTime());
|
||||
return userInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取隧道权限详情
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param roleId 角色ID
|
||||
* @return 隧道权限详情列表
|
||||
*/
|
||||
private List<UserPackageDto.UserTunnelDetailDto> getTunnelPermissions(Long userId, Integer roleId) {
|
||||
if (roleId != null && roleId == ADMIN_ROLE_ID) {
|
||||
return userMapper.getAllTunnelsForAdmin();
|
||||
} else {
|
||||
return userMapper.getUserTunnelDetails(userId.intValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 安排用户到期延时任务
|
||||
*
|
||||
* @param user 用户对象
|
||||
*/
|
||||
private void scheduleUserExpirationTask(User user) {
|
||||
// 取消已存在的延时任务(如果有)
|
||||
delayQueueManager.remove("user_exp_" + user.getId());
|
||||
|
||||
// 创建新的延时任务
|
||||
TaskBase taskBase = new TaskBase(user.getId().toString());
|
||||
taskBase.setType("1"); // 账号到期延迟任务
|
||||
|
||||
long delayTime = user.getExpTime() - System.currentTimeMillis();
|
||||
DelayTask delayTask = new DelayTask(taskBase, delayTime);
|
||||
|
||||
delayQueueManager.put(delayTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理用户到期时间更新的延时任务
|
||||
*
|
||||
* @param newUser 新用户信息
|
||||
*/
|
||||
private void handleUserExpirationTaskUpdate(User newUser) {
|
||||
String taskId = "user_exp_" + newUser.getId();
|
||||
|
||||
// 先取消原有的延时任务
|
||||
delayQueueManager.remove(taskId);
|
||||
|
||||
TaskBase taskBase = new TaskBase(newUser.getId().toString());
|
||||
taskBase.setType("1"); // 账号到期延迟任务
|
||||
|
||||
long delayTime = newUser.getExpTime() - System.currentTimeMillis();
|
||||
DelayTask delayTask = new DelayTask(taskBase, delayTime);
|
||||
|
||||
delayQueueManager.put(delayTask);
|
||||
}
|
||||
|
||||
// ========== 内部数据类 ==========
|
||||
|
||||
/**
|
||||
* 登录验证结果封装类
|
||||
*/
|
||||
@Data
|
||||
private static class LoginValidationResult {
|
||||
private final boolean hasError;
|
||||
private final String errorMessage;
|
||||
private final User user;
|
||||
|
||||
private LoginValidationResult(boolean hasError, String errorMessage, User user) {
|
||||
this.hasError = hasError;
|
||||
this.errorMessage = errorMessage;
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public static LoginValidationResult success(User user) {
|
||||
return new LoginValidationResult(false, null, user);
|
||||
}
|
||||
|
||||
public static LoginValidationResult error(String errorMessage) {
|
||||
return new LoginValidationResult(true, errorMessage, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前用户信息封装类
|
||||
*/
|
||||
@Data
|
||||
private static class CurrentUserInfo {
|
||||
private final boolean hasError;
|
||||
private final String errorMessage;
|
||||
private final User user;
|
||||
private final Integer roleId;
|
||||
|
||||
private CurrentUserInfo(boolean hasError, String errorMessage, User user, Integer roleId) {
|
||||
this.hasError = hasError;
|
||||
this.errorMessage = errorMessage;
|
||||
this.user = user;
|
||||
this.roleId = roleId;
|
||||
}
|
||||
|
||||
public static CurrentUserInfo success(User user, Integer roleId) {
|
||||
return new CurrentUserInfo(false, null, user, roleId);
|
||||
}
|
||||
|
||||
public static CurrentUserInfo error(String errorMessage) {
|
||||
return new CurrentUserInfo(true, errorMessage, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,520 @@
|
||||
package com.admin.service.impl;
|
||||
|
||||
import com.admin.common.dto.UserTunnelDto;
|
||||
import com.admin.common.dto.UserTunnelQueryDto;
|
||||
import com.admin.common.dto.UserTunnelUpdateDto;
|
||||
import com.admin.common.dto.UserTunnelWithDetailDto;
|
||||
import com.admin.common.lang.R;
|
||||
import com.admin.entity.UserTunnel;
|
||||
import com.admin.mapper.TunnelMapper;
|
||||
import com.admin.mapper.UserTunnelMapper;
|
||||
import com.admin.service.TunnelService;
|
||||
import com.admin.service.UserTunnelService;
|
||||
import com.admin.service.ForwardService;
|
||||
import com.admin.service.NodeService;
|
||||
import com.admin.common.task.DelayQueueManager;
|
||||
import com.admin.common.utils.GostUtil;
|
||||
import com.admin.entity.Forward;
|
||||
import com.admin.entity.Tunnel;
|
||||
import com.admin.entity.Node;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 用户隧道权限服务实现类
|
||||
* 提供用户隧道权限的分配、查询、更新和删除功能
|
||||
* 支持流量限制、数量限制、过期时间和限速规则的管理
|
||||
* </p>
|
||||
*
|
||||
* @author QAQ
|
||||
* @since 2025-06-03
|
||||
*/
|
||||
@Service
|
||||
public class UserTunnelServiceImpl extends ServiceImpl<UserTunnelMapper, UserTunnel> implements UserTunnelService {
|
||||
|
||||
// ========== 常量定义 ==========
|
||||
|
||||
/** 成功响应消息 */
|
||||
private static final String SUCCESS_ASSIGN_MSG = "用户隧道权限分配成功";
|
||||
private static final String SUCCESS_REMOVE_MSG = "用户隧道权限删除成功";
|
||||
private static final String SUCCESS_UPDATE_FLOW_MSG = "用户隧道流量限制更新成功";
|
||||
private static final String SUCCESS_UPDATE_MSG = "用户隧道权限更新成功";
|
||||
|
||||
/** 错误响应消息 */
|
||||
private static final String ERROR_ASSIGN_FAILED = "用户隧道权限分配失败";
|
||||
private static final String ERROR_PERMISSION_EXISTS = "该用户已拥有此隧道权限";
|
||||
private static final String ERROR_PERMISSION_NOT_FOUND = "未找到对应的用户隧道权限记录";
|
||||
private static final String ERROR_USER_TUNNEL_NOT_EXISTS = "用户隧道权限不存在";
|
||||
private static final String ERROR_NOT_EXISTS = "不存在";
|
||||
private static final String ERROR_UPDATE_FAILED = "用户隧道权限更新失败";
|
||||
|
||||
// ========== 依赖注入 ==========
|
||||
|
||||
@Autowired
|
||||
private DelayQueueManager delayQueueManager;
|
||||
|
||||
@Autowired
|
||||
@Lazy
|
||||
private ForwardService forwardService;
|
||||
|
||||
@Autowired
|
||||
@Lazy
|
||||
private TunnelService tunnelService;
|
||||
|
||||
@Autowired
|
||||
private NodeService nodeService;
|
||||
|
||||
// ========== 公共接口实现 ==========
|
||||
|
||||
/**
|
||||
* 分配用户隧道权限
|
||||
* 检查权限是否已存在,避免重复分配
|
||||
*
|
||||
* @param userTunnelDto 用户隧道权限分配数据传输对象
|
||||
* @return 分配结果响应
|
||||
*/
|
||||
@Override
|
||||
public R assignUserTunnel(UserTunnelDto userTunnelDto) {
|
||||
// 1. 检查权限是否已存在
|
||||
if (isUserTunnelPermissionExists(userTunnelDto.getUserId(), userTunnelDto.getTunnelId())) {
|
||||
return R.err(ERROR_PERMISSION_EXISTS);
|
||||
}
|
||||
|
||||
// 2. 创建用户隧道权限实体并保存
|
||||
UserTunnel userTunnel = buildUserTunnelEntity(userTunnelDto);
|
||||
// 设置默认状态为启用
|
||||
userTunnel.setStatus(1);
|
||||
boolean success = this.save(userTunnel);
|
||||
|
||||
if (success) {
|
||||
// 3. 如果是启用状态且有到期时间,添加延迟任务
|
||||
if (isEnabledAndHasExpTime(userTunnel)) {
|
||||
try {
|
||||
delayQueueManager.addUserTunnelExpirationTask(userTunnel);
|
||||
} catch (Exception e) {
|
||||
// 延迟任务添加失败不影响主业务逻辑
|
||||
// 可以考虑记录日志或其他处理方式
|
||||
}
|
||||
}
|
||||
return R.ok(SUCCESS_ASSIGN_MSG);
|
||||
}
|
||||
|
||||
return R.err(ERROR_ASSIGN_FAILED);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户隧道权限列表
|
||||
* 通过连表查询获取用户隧道权限及隧道详细信息
|
||||
*
|
||||
* @param queryDto 用户隧道权限查询数据传输对象
|
||||
* @return 用户隧道权限详情列表响应
|
||||
*/
|
||||
@Override
|
||||
public R getUserTunnelList(UserTunnelQueryDto queryDto) {
|
||||
List<UserTunnelWithDetailDto> userTunnelDetails = getUserTunnelDetailsFromDatabase(queryDto.getUserId());
|
||||
return R.ok(userTunnelDetails);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户隧道权限
|
||||
*
|
||||
* @param id 用户隧道权限ID
|
||||
* @return 删除结果响应
|
||||
*/
|
||||
@Override
|
||||
public R removeUserTunnel(Integer id) {
|
||||
// 1. 获取用户隧道权限信息
|
||||
UserTunnel userTunnel = this.getById(id);
|
||||
if (userTunnel == null) {
|
||||
return R.err(ERROR_PERMISSION_NOT_FOUND);
|
||||
}
|
||||
|
||||
// 2. 删除该用户在该隧道下的所有转发
|
||||
try {
|
||||
removeUserTunnelForwards(userTunnel.getUserId(), userTunnel.getTunnelId());
|
||||
} catch (Exception e) {
|
||||
// 转发删除失败,记录日志但不阻止权限删除
|
||||
}
|
||||
|
||||
// 3. 移除延迟任务
|
||||
try {
|
||||
delayQueueManager.removeUserTunnelExpirationTask(id);
|
||||
} catch (Exception e) {
|
||||
// 延迟任务移除失败不影响主业务逻辑
|
||||
}
|
||||
|
||||
// 4. 删除用户隧道权限记录
|
||||
boolean success = this.removeById(id);
|
||||
return success ? R.ok(SUCCESS_REMOVE_MSG) : R.err(ERROR_PERMISSION_NOT_FOUND);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户隧道流量限制
|
||||
*
|
||||
* @param id 用户隧道权限ID
|
||||
* @param flow 流量限制值
|
||||
* @return 更新结果响应
|
||||
*/
|
||||
@Override
|
||||
public R updateUserTunnelFlow(Integer id, Long flow) {
|
||||
// 1. 验证用户隧道权限是否存在
|
||||
UserTunnel userTunnel = this.getById(id);
|
||||
if (userTunnel == null) {
|
||||
return R.err(ERROR_NOT_EXISTS);
|
||||
}
|
||||
|
||||
// 2. 更新流量限制并保存
|
||||
userTunnel.setFlow(flow);
|
||||
boolean success = this.updateById(userTunnel);
|
||||
|
||||
return success ? R.ok(SUCCESS_UPDATE_FLOW_MSG) : R.err(ERROR_PERMISSION_NOT_FOUND);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户隧道权限
|
||||
* 支持更新流量限制、数量限制、流量重置时间、过期时间和限速规则
|
||||
*
|
||||
* @param updateDto 用户隧道权限更新数据传输对象
|
||||
* @return 更新结果响应
|
||||
*/
|
||||
@Override
|
||||
public R updateUserTunnel(UserTunnelUpdateDto updateDto) {
|
||||
// 1. 验证用户隧道权限是否存在
|
||||
UserTunnel existingUserTunnel = this.getById(updateDto.getId());
|
||||
if (existingUserTunnel == null) {
|
||||
return R.err(ERROR_USER_TUNNEL_NOT_EXISTS);
|
||||
}
|
||||
|
||||
// 2. 检查是否更新了限速规则
|
||||
boolean speedChanged = hasSpeedChanged(existingUserTunnel.getSpeedId(), updateDto.getSpeedId());
|
||||
|
||||
// 3. 更新用户隧道权限属性
|
||||
updateUserTunnelProperties(existingUserTunnel, updateDto);
|
||||
|
||||
// 4. 保存更新
|
||||
boolean success = this.updateById(existingUserTunnel);
|
||||
|
||||
if (success) {
|
||||
// 5. 处理延迟任务更新
|
||||
handleDelayTaskUpdate(existingUserTunnel, updateDto);
|
||||
|
||||
// 6. 如果限速规则发生变化,更新该用户隧道下的所有转发
|
||||
if (speedChanged) {
|
||||
updateUserTunnelForwardsSpeed(existingUserTunnel.getUserId(), existingUserTunnel.getTunnelId(), updateDto.getSpeedId());
|
||||
}
|
||||
|
||||
return R.ok(SUCCESS_UPDATE_MSG);
|
||||
}
|
||||
|
||||
return R.err(ERROR_UPDATE_FAILED);
|
||||
}
|
||||
|
||||
// ========== 私有辅助方法 ==========
|
||||
|
||||
/**
|
||||
* 检查用户隧道权限是否已存在
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param tunnelId 隧道ID
|
||||
* @return 权限是否已存在
|
||||
*/
|
||||
private boolean isUserTunnelPermissionExists(Integer userId, Integer tunnelId) {
|
||||
QueryWrapper<UserTunnel> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("user_id", userId).eq("tunnel_id", tunnelId);
|
||||
UserTunnel existingUserTunnel = this.getOne(queryWrapper);
|
||||
return existingUserTunnel != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建用户隧道权限实体对象
|
||||
*
|
||||
* @param userTunnelDto 用户隧道权限DTO
|
||||
* @return 构建完成的用户隧道权限对象
|
||||
*/
|
||||
private UserTunnel buildUserTunnelEntity(UserTunnelDto userTunnelDto) {
|
||||
UserTunnel userTunnel = new UserTunnel();
|
||||
BeanUtils.copyProperties(userTunnelDto, userTunnel);
|
||||
return userTunnel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从数据库获取用户隧道权限详情
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 用户隧道权限详情列表
|
||||
*/
|
||||
private List<UserTunnelWithDetailDto> getUserTunnelDetailsFromDatabase(Integer userId) {
|
||||
return this.baseMapper.getUserTunnelWithDetails(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户隧道权限属性
|
||||
*
|
||||
* @param existingUserTunnel 现有的用户隧道权限对象
|
||||
* @param updateDto 更新数据传输对象
|
||||
*/
|
||||
private void updateUserTunnelProperties(UserTunnel existingUserTunnel, UserTunnelUpdateDto updateDto) {
|
||||
// 更新基本属性
|
||||
existingUserTunnel.setFlow(updateDto.getFlow());
|
||||
existingUserTunnel.setNum(updateDto.getNum());
|
||||
|
||||
// 更新可选属性(仅在非空时更新)
|
||||
updateOptionalProperty(existingUserTunnel::setFlowResetTime, updateDto.getFlowResetTime());
|
||||
updateOptionalProperty(existingUserTunnel::setExpTime, updateDto.getExpTime());
|
||||
|
||||
// 更新限速规则ID(允许设置为null,表示不限速)
|
||||
existingUserTunnel.setSpeedId(updateDto.getSpeedId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新可选属性(仅在值非空时更新)
|
||||
*
|
||||
* @param setter 属性设置方法
|
||||
* @param value 属性值
|
||||
* @param <T> 属性类型
|
||||
*/
|
||||
private <T> void updateOptionalProperty(java.util.function.Consumer<T> setter, T value) {
|
||||
if (value != null) {
|
||||
setter.accept(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理延迟任务更新
|
||||
*
|
||||
* @param userTunnel 更新后的用户隧道对象
|
||||
* @param updateDto 更新数据传输对象
|
||||
*/
|
||||
private void handleDelayTaskUpdate(UserTunnel userTunnel, UserTunnelUpdateDto updateDto) {
|
||||
try {
|
||||
// 如果更新了到期时间,需要重新处理延迟任务
|
||||
if (updateDto.getExpTime() != null) {
|
||||
// 先移除旧的延迟任务
|
||||
delayQueueManager.removeUserTunnelExpirationTask(userTunnel.getId());
|
||||
|
||||
// 如果是启用状态且有到期时间,添加新的延迟任务
|
||||
if (isEnabledAndHasExpTime(userTunnel)) {
|
||||
delayQueueManager.addUserTunnelExpirationTask(userTunnel);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 延迟任务处理失败不影响主业务逻辑
|
||||
// 可以考虑记录日志
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户在指定隧道下的所有转发
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param tunnelId 隧道ID
|
||||
*/
|
||||
private void removeUserTunnelForwards(Integer userId, Integer tunnelId) {
|
||||
// 查询该用户在该隧道下的所有转发
|
||||
QueryWrapper<Forward> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("user_id", userId).eq("tunnel_id", tunnelId);
|
||||
|
||||
List<Forward> userTunnelForwards = forwardService.list(queryWrapper);
|
||||
|
||||
if (!userTunnelForwards.isEmpty()) {
|
||||
// 获取用户隧道权限信息,用于构建服务名称
|
||||
UserTunnel userTunnel = getUserTunnelByUserAndTunnel(userId, tunnelId);
|
||||
|
||||
for (Forward forward : userTunnelForwards) {
|
||||
try {
|
||||
// 先调用GostUtil删除/停止服务
|
||||
stopForwardService(forward, userId, userTunnel != null ? userTunnel.getId() : 0);
|
||||
|
||||
// 然后删除数据库记录
|
||||
forwardService.removeById(forward.getId());
|
||||
|
||||
} catch (Exception e) {
|
||||
// 单个转发删除失败,记录错误但继续处理其他转发
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除转发服务(按创建的反向顺序删除:主服务 -> 远端服务 -> 转发链)
|
||||
*
|
||||
* @param forward 转发对象
|
||||
* @param userId 用户ID
|
||||
* @param userTunnelId 用户隧道ID
|
||||
*/
|
||||
private void stopForwardService(Forward forward, Integer userId, Integer userTunnelId) {
|
||||
try {
|
||||
Tunnel tunnel = tunnelService.getById(forward.getTunnelId());
|
||||
if (tunnel == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Node inNode = nodeService.getById(tunnel.getInNodeId());
|
||||
Node outNode = nodeService.getById(tunnel.getOutNodeId());
|
||||
|
||||
String serviceName = buildServiceName(forward.getId(), Long.valueOf(userId), userTunnelId);
|
||||
|
||||
// 1. 先删除主服务
|
||||
if (inNode != null) {
|
||||
String inNodeAddress = buildNodeAddress(inNode);
|
||||
try {
|
||||
GostUtil.DeleteService(inNodeAddress, serviceName, inNode.getSecret());
|
||||
} catch (Exception e) {
|
||||
// 主服务删除失败,记录但继续
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 如果是隧道转发,删除远端服务
|
||||
if (tunnel.getType() == 1 && outNode != null && !outNode.getId().equals(inNode != null ? inNode.getId() : null)) {
|
||||
String outNodeAddress = buildNodeAddress(outNode);
|
||||
try {
|
||||
GostUtil.DeleteRemoteService(outNodeAddress, serviceName, outNode.getSecret());
|
||||
} catch (Exception e) {
|
||||
// 远端服务删除失败,记录但继续
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 如果是隧道转发,最后删除转发链
|
||||
if (tunnel.getType() == 1 && inNode != null) {
|
||||
String inNodeAddress = buildNodeAddress(inNode);
|
||||
try {
|
||||
GostUtil.DeleteChains(inNodeAddress, serviceName, inNode.getSecret());
|
||||
} catch (Exception e) {
|
||||
// 转发链删除失败,记录但继续
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
// 服务删除失败,记录错误
|
||||
throw new RuntimeException("删除转发服务失败,转发ID:" + forward.getId() + ",错误:" + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户ID和隧道ID获取用户隧道权限
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param tunnelId 隧道ID
|
||||
* @return 用户隧道权限对象
|
||||
*/
|
||||
private UserTunnel getUserTunnelByUserAndTunnel(Integer userId, Integer tunnelId) {
|
||||
try {
|
||||
QueryWrapper<UserTunnel> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("user_id", userId).eq("tunnel_id", tunnelId);
|
||||
return this.getOne(queryWrapper);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建服务名称
|
||||
*
|
||||
* @param forwardId 转发ID
|
||||
* @param userId 用户ID
|
||||
* @param userTunnelId 用户隧道ID
|
||||
* @return 服务名称
|
||||
*/
|
||||
private String buildServiceName(Long forwardId, Long userId, Integer userTunnelId) {
|
||||
return forwardId + "_" + userId + "_" + userTunnelId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建节点地址
|
||||
*
|
||||
* @param node 节点对象
|
||||
* @return 节点地址字符串
|
||||
*/
|
||||
private String buildNodeAddress(Node node) {
|
||||
return node.getIp() + ":" + node.getPort();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户隧道是否启用且有到期时间
|
||||
*
|
||||
* @param userTunnel 用户隧道对象
|
||||
* @return 是否启用且有到期时间
|
||||
*/
|
||||
private boolean isEnabledAndHasExpTime(UserTunnel userTunnel) {
|
||||
return userTunnel.getStatus() != null && userTunnel.getStatus() == 1
|
||||
&& userTunnel.getExpTime() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查限速规则是否发生变化
|
||||
*
|
||||
* @param oldSpeedId 原始限速规则ID
|
||||
* @param newSpeedId 新的限速规则ID
|
||||
* @return 限速规则是否发生变化
|
||||
*/
|
||||
private boolean hasSpeedChanged(Integer oldSpeedId, Integer newSpeedId) {
|
||||
if (oldSpeedId == null && newSpeedId == null) {
|
||||
return false;
|
||||
}
|
||||
if (oldSpeedId == null || newSpeedId == null) {
|
||||
return true;
|
||||
}
|
||||
return !oldSpeedId.equals(newSpeedId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户隧道下所有转发的限速规则
|
||||
* 管理员操作,不需要权限检查,直接查出该用户在该隧道下的所有转发并应用新的限速
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param tunnelId 隧道ID
|
||||
* @param speedId 新的限速规则ID
|
||||
*/
|
||||
private void updateUserTunnelForwardsSpeed(Integer userId, Integer tunnelId, Integer speedId) {
|
||||
// 1. 查询该用户在该隧道下的所有转发
|
||||
QueryWrapper<Forward> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("user_id", userId).eq("tunnel_id", tunnelId);
|
||||
List<Forward> userTunnelForwards = forwardService.list(queryWrapper);
|
||||
|
||||
if (userTunnelForwards.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 获取隧道信息
|
||||
Tunnel tunnel = tunnelService.getById(tunnelId);
|
||||
if (tunnel == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 获取用户隧道权限信息
|
||||
UserTunnel userTunnel = getUserTunnelByUserAndTunnel(userId, tunnelId);
|
||||
if (userTunnel == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. 获取入口节点信息
|
||||
Node inNode = nodeService.getById(tunnel.getInNodeId());
|
||||
|
||||
if (inNode == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 5. 批量更新该用户在该隧道下所有转发的限速配置(只更新入口节点)
|
||||
String inNodeAddress = buildNodeAddress(inNode);
|
||||
|
||||
for (Forward forward : userTunnelForwards) {
|
||||
String serviceName = buildServiceName(forward.getId(), Long.valueOf(userId), userTunnel.getId());
|
||||
|
||||
// 6. 更新入口节点的主服务限速配置(使用批量UpdateService接口)
|
||||
GostUtil.UpdateService(inNodeAddress, serviceName, forward.getInPort(), speedId,
|
||||
forward.getRemoteAddr(), inNode.getSecret(), tunnel.getType());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user