Merge remote-tracking branch 'origin/V1.00' into V1.00

This commit is contained in:
waner 2026-04-01 16:49:07 +08:00
commit f8673ec1fb
27 changed files with 1955 additions and 0 deletions

View File

@ -0,0 +1,30 @@
package com.cisd.tms.common.config;
import com.cisd.tms.modules.device.filter.IpWhitelistFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfig {
@Bean
public IpWhitelistFilter ipWhitelistFilter() {
return new IpWhitelistFilter();
}
/**
* 注册 Filter 并配置拦截规则
*/
@Bean
public FilterRegistrationBean<IpWhitelistFilter> ipWhitelistFilterRegistration() {
FilterRegistrationBean<IpWhitelistFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(ipWhitelistFilter());
//设置拦截路径
registration.addUrlPatterns("/api/v1/*");
return registration;
}
}

View File

@ -0,0 +1,50 @@
package com.cisd.tms.modules.device.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.cisd.tms.common.api.ApiResponse;
import com.cisd.tms.modules.device.dto.network.IpWhitelistResponse;
import com.cisd.tms.modules.device.service.IpWhitelistService;
import com.cisd.tms.modules.device.dto.network.IpWhitelistRequest;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/v1/device/whitelist")
@Tag(name = "IP白名单管理", description = "提供IP白名单的增、删、改、查分页接口")
public class IpWhitelistController {
@Autowired
private IpWhitelistService ipWhitelistService;
@PostMapping("/add")
@Operation(summary = "添加IP白名单", description = "新增一条IP白名单记录")
public ApiResponse<String> addWhitelist(@RequestBody IpWhitelistRequest req) {
ipWhitelistService.addWhitelist(req);
return ApiResponse.success("添加成功");
}
@PostMapping("/update")
@Operation(summary = "更新IP白名单", description = "根据ID更新IP白名单信息")
public ApiResponse<String> updateWhitelist(@RequestBody IpWhitelistRequest req) {
ipWhitelistService.updateWhitelist(req);
return ApiResponse.success("更新成功");
}
@PostMapping("/delete/{id}")
@Operation(summary = "删除IP白名单", description = "根据ID删除指定IP白名单")
public ApiResponse<String> deleteWhitelist(@PathVariable Long id) {
ipWhitelistService.deleteWhitelist(id);
return ApiResponse.success("删除成功");
}
@GetMapping("/page")
@Operation(summary = "分页查询IP白名单", description = "支持分页查询IP白名单列表")
public ApiResponse<IPage<IpWhitelistResponse>> getWhitelistPage(@RequestBody IpWhitelistRequest req) {
IPage<IpWhitelistResponse> page = ipWhitelistService.getWhitelistPage(req);
return ApiResponse.success(page);
}
}

View File

@ -0,0 +1,155 @@
package com.cisd.tms.modules.device.controller;
import com.cisd.tms.common.api.ApiResponse;
import com.cisd.tms.modules.device.dto.network.*;
import com.cisd.tms.modules.device.service.NetworkConfigService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Tag(name = "网络配置管理", description = "提供设备网络信息查询、IPv4/IPv6配置、Bond配置及路由管理等接口")
@RestController
@RequestMapping("/api/v1/device/network-config")
public class NetworkConfigController {
private final NetworkConfigService networkConfigService;
public NetworkConfigController(NetworkConfigService networkConfigService){
this.networkConfigService = networkConfigService;
}
@Operation(summary = "获取网络信息", description = "获取当前设备的所有网络连接信息")
@GetMapping("/network-info")
public ApiResponse<List<NetworkInfoResponse>> getNetworkInfo(){
return ApiResponse.success(networkConfigService.getNetworkInfo());
}
@Operation(summary = "获取IPv4配置信息", description = "根据设备名称获取指定网络接口的IPv4配置详情")
@GetMapping("/ipv4-info/{deviceName}")
public ApiResponse<Ipv4InfoResponse> getIpv4Info(@PathVariable String deviceName){
return ApiResponse.success(networkConfigService.getIpv4Info(deviceName));
}
@Operation(summary = "设置IPv4配置", description = "为指定网络接口配置IPv4地址、掩码、网关等信息")
@PostMapping("/ipv4-config/set")
public ApiResponse<String> setIpv4Config(@RequestBody Ipv4ConfigRequest req) {
String resultMessage = networkConfigService.setIpv4Config(req);
return ApiResponse.success(resultMessage);
}
@Operation(summary = "获取IPv6配置信息", description = "根据设备名称获取指定网络接口的IPv6配置详情")
@GetMapping("/ipv6-info/{deviceName}")
public ApiResponse<Ipv6InfoResponse> getIpv6Info(@PathVariable String deviceName){
return ApiResponse.success(networkConfigService.getIpv6Info(deviceName));
}
@Operation(summary = "设置IPv6配置", description = "为指定网络接口配置IPv6地址、前缀长度、网关等信息")
@PostMapping("/ipv6-config/set")
public ApiResponse<String> setIpv6Config(@RequestBody Ipv6ConfigRequest req) {
String resultMessage = networkConfigService.setIpv6Config(req);
return ApiResponse.success(resultMessage);
}
@Operation(summary = "获取所有Bond名称", description = "返回当前系统中所有已创建的Bond接口名称")
@GetMapping("/all-bond-name")
public ApiResponse<List<String>> getAllBondName(){
return ApiResponse.success(networkConfigService.getAllBondName());
}
@Operation(summary = "创建Bond", description = "创建一个新的Bond聚合接口需指定名称、模式和从属接口")
@PostMapping("/bond/create")
public ApiResponse<String> createBond(@RequestBody BondCreateRequest req) {
String resultMessage = networkConfigService.createBond(req);
return ApiResponse.success(resultMessage);
}
@Operation(summary = "删除Bond", description = "根据Bond名称删除指定的聚合接口")
@PostMapping("/bond/delete/{bondName}")
public ApiResponse<String> deleteBond(@PathVariable("bondName") String bondName){
String resultMessage = networkConfigService.deleteBond(bondName);
return ApiResponse.success(resultMessage);
}
@Operation(summary = "添加从属接口到Bond", description = "向指定Bond中添加一个或多个从属网络接口")
@PostMapping("/bond-slave/add")
public ApiResponse<String> addSlavesTOBond(@RequestBody BondAddSlavesRequest req) {
String resultMessage = networkConfigService.addSlavesTOBond(req);
return ApiResponse.success(resultMessage);
}
@Operation(summary = "从Bond中移除从属接口", description = "从指定Bond中移除一个或多个从属网络接口")
@PostMapping("/bond-slave/remove")
public ApiResponse<String> removeSlaveFromBond(@RequestBody BondRemoveSlaveRequest req) {
String resultMessage = networkConfigService.removeSlaveFromBond(req);
return ApiResponse.success(resultMessage);
}
@Operation(summary = "获取Bond的从属接口列表", description = "根据Bond名称返回其所有从属接口名称")
@GetMapping("/bond-slave/{bondName}")
public ApiResponse<List<String>> getBondSlaves(@PathVariable String bondName){
return ApiResponse.success(networkConfigService.getBondSlaves(bondName));
}
@Operation(summary = "获取Bond模式", description = "根据Bond名称查询当前的绑定模式")
@GetMapping("/bond/mode/{bondName}")
public ApiResponse<String> getBondMode(@PathVariable("bondName") String bondName) {
String mode = networkConfigService.getBondMode(bondName);
if (mode == null) {
return ApiResponse.fail(400, "未找到该连接的 Bond 模式", null);
}
return ApiResponse.success(mode);
}
@Operation(summary = "设置Bond模式", description = "修改指定Bond的绑定模式")
@PostMapping("/bond/mode/set")
public ApiResponse<String> setBondMode(@RequestBody BondModifyModeRequest req) {
String resultMessage = networkConfigService.setBondMode(req);
return ApiResponse.success(resultMessage);
}
@Operation(summary = "获取路由表", description = "返回当前系统的IPv4/IPv6路由表信息")
@GetMapping("/routes/routingTable")
public ApiResponse<List<RouteInfoResponse>> getRoutingTable() {
List<RouteInfoResponse> routes = networkConfigService.getRoutingTable();
return ApiResponse.success(routes);
}
@Operation(summary = "设置默认路由", description = "配置或修改系统的默认网关路由")
@PostMapping("/routes/default/set")
public ApiResponse<String> setDefaultRoute(@RequestBody SetDefaultRouteRequest req) {
String resultMessage = networkConfigService.setDefaultRoute(req);
return ApiResponse.success(resultMessage);
}
@Operation(summary = "添加静态路由", description = "新增一条静态路由规则")
@PostMapping("/routes/static/add")
public ApiResponse<String> addStaticRoute(@RequestBody AddStaticRouteRequest req) {
String resultMessage = networkConfigService.addStaticRoute(req);
return ApiResponse.success(resultMessage);
}
@Operation(summary = "删除默认路由", description = "删除指定的默认路由")
@PostMapping("/routes/default/delete")
public ApiResponse<String> deleteDefaultRoute(@RequestBody DeleteDefaultRouteRequest req) {
String resultMessage = networkConfigService.deleteDefaultRoute(req);
return ApiResponse.success(resultMessage);
}
@Operation(summary = "删除静态路由", description = "删除指定的静态路由")
@PostMapping("/routes/static/delete")
public ApiResponse<String> deleteStaticRoute(@RequestBody DeleteStaticRouteRequest req) {
String resultMessage = networkConfigService.deleteStaticRoute(req);
return ApiResponse.success(resultMessage);
}
}

View File

@ -0,0 +1,16 @@
package com.cisd.tms.modules.device.dto.network;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "添加静态路由请求参数")
public class AddStaticRouteRequest {
@Schema(description = "网络设备名称(如 eth0、bond0", example = "eth0")
private String deviceName;
@Schema(description = "目标网络CIDR", example = "192.168.1.0/24")
private String targetCidr;
@Schema(description = "下一跳地址网关IP", example = "192.168.1.1")
private String nextHop;
}

View File

@ -0,0 +1,16 @@
package com.cisd.tms.modules.device.dto.network;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Data
@Schema(description = "向Bond添加从属接口请求参数")
public class BondAddSlavesRequest {
@Schema(description = "Bond接口名称", example = "bond0")
private String bondName;
@Schema(description = "要添加到Bond的从属接口列表", example = "[\"eth0\", \"eth1\"]")
private List<String> slaveList;
}

View File

@ -0,0 +1,32 @@
package com.cisd.tms.modules.device.dto.network;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "创建Bond请求参数")
public class BondCreateRequest {
/**
* Bond 名称例如 "bond0"
*/
@Schema(description = "Bond 名称", example = "bond0")
private String bondName;
/**
* 模式 (例如: 0, 1, 2, 4)
* 0: balance-rr (轮询)
* 1: active-backup (主备)
* 2: balance-xor
* 4: 802.3ad (动态链路聚合)
*/
@Schema(description = "Bond 模式0: balance-rr 轮询, 1: active-backup 主备, 2: balance-xor, 4: 802.3ad 动态链路聚合"
,example = "1")
private Integer mode;
/**
* 可选的 IPv4 配置
* 如果前端传了该对象创建 Bond 后会自动配置 IP
*/
@Schema(description = "可选的 IPv4 配置,如果提供该对象,创建 Bond 后会自动配置 IP")
private Ipv4ConfigRequest ipv4Config;
}

View File

@ -0,0 +1,15 @@
package com.cisd.tms.modules.device.dto.network;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "修改bond模式请求参数")
public class BondModifyModeRequest {
@Schema(description = "Bond 名称", example = "bond0")
private String bondName;
@Schema(description = "Bond 模式0: balance-rr 轮询, 1: active-backup 主备, 2: balance-xor, 4: 802.3ad 动态链路聚合"
,example = "1")
private Integer mode;
}

View File

@ -0,0 +1,17 @@
package com.cisd.tms.modules.device.dto.network;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Data
@Schema(description = "从Bond移除从属接口请求参数")
public class BondRemoveSlaveRequest {
@Schema(description = "Bond接口名称", example = "bond0")
private String bondName;
@Schema(description = "要从Bond移除的从属接口列表", example = "[\"eth0\", \"eth1\"]")
private List<String> slaveList;
}

View File

@ -0,0 +1,11 @@
package com.cisd.tms.modules.device.dto.network;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "删除默认路由请求参数")
public class DeleteDefaultRouteRequest {
@Schema(description = "网络设备名称(如 eth0、bond0", example = "eth0")
private String deviceName;
}

View File

@ -0,0 +1,18 @@
package com.cisd.tms.modules.device.dto.network;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "删除静态路由请求参数")
public class DeleteStaticRouteRequest {
@Schema(description = "网络设备名称", example = "eth0")
private String deviceName;
@Schema(description = "目标网络CIDR", example = "192.168.1.0/24")
private String targetCidr;
@Schema(description = "下一跳地址网关IP", example = "192.168.1.1")
private String nextHop;
}

View File

@ -0,0 +1,21 @@
package com.cisd.tms.modules.device.dto.network;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "IP白名单请求参数")
public class IpWhitelistRequest {
@Schema(description = "白名单记录ID更新时必填", example = "1")
private String id;
@Schema(description = "IP地址", example = "192.168.1.1")
private String ip;
@Schema(description = "掩码长度", example = "24")
private String mask;
// 分页参数
@Schema(description = "当前页码", defaultValue = "1", example = "1")
private Integer pageNum = 1;
@Schema(description = "每页条数", defaultValue = "10", example = "10")
private Integer pageSize = 10;
}

View File

@ -0,0 +1,15 @@
package com.cisd.tms.modules.device.dto.network;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "IP白名单响应参数")
public class IpWhitelistResponse {
@Schema(description = "白名单记录ID更新时必填", example = "1")
private String id;
@Schema(description = "IP地址", example = "192.168.1.1")
private String ip;
@Schema(description = "掩码长度", example = "24")
private String mask;
}

View File

@ -0,0 +1,17 @@
package com.cisd.tms.modules.device.dto.network;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "IPv4配置请求参数")
public class Ipv4ConfigRequest {
@Schema(description = "网络设备名称", example = "eth0")
private String deviceName;
@Schema(description = "IPv4地址", example = "192.168.1.100")
private String ipv4;
@Schema(description = "子网掩码长度", example = "24")
private String maskLength;
@Schema(description = "默认网关地址", example = "192.168.1.1")
private String gateway;
}

View File

@ -0,0 +1,18 @@
package com.cisd.tms.modules.device.dto.network;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "IPv4配置信息响应")
public class Ipv4InfoResponse {
@Schema(description = "IPv4地址", example = "192.168.1.100")
private String ipv4;
@Schema(description = "子网掩码长度", example = "24")
private String maskLength;
@Schema(description = "默认网关地址", example = "192.168.1.1")
private String gateway;
@Schema(description = "IP获取方式如 static、dhcp", example = "static")
private String method;
}

View File

@ -0,0 +1,13 @@
package com.cisd.tms.modules.device.dto.network;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "单个IPv6地址及掩码")
public class Ipv6AddressItem {
@Schema(description = "IPv6地址", example = "2001:db8::2")
private String ipv6;
@Schema(description = "子网掩码前缀长度", example = "64")
private String maskLength;
}

View File

@ -0,0 +1,17 @@
package com.cisd.tms.modules.device.dto.network;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "IPv6配置请求参数")
public class Ipv6ConfigRequest {
@Schema(description = "网络设备名称(如 eth0、bond0", example = "eth0")
private String deviceName;
@Schema(description = "IPv6地址", example = "2001:db8::1")
private String ipv6;
@Schema(description = "默认网关地址", example = "2001:db8::1")
private String gateway;
@Schema(description = "子网前缀长度(如 64", example = "64")
private String maskLength;
}

View File

@ -0,0 +1,17 @@
package com.cisd.tms.modules.device.dto.network;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Data
@Schema(description = "IPv6配置信息响应")
public class Ipv6InfoResponse {
@Schema(description = "IPv6地址及掩码列表")
private List<Ipv6AddressItem> ipv6List;
@Schema(description = "默认网关地址", example = "2001:db8::1")
private String gateway;
@Schema(description = "IP获取方式如 static、dhcp、auto", example = "static")
private String method;
}

View File

@ -0,0 +1,30 @@
package com.cisd.tms.modules.device.dto.network;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Data
@Schema(description = "网络接口信息响应参数")
public class NetworkInfoResponse {
@Schema(description = "设备名称", example = "eth0")
private String deviceName;
@Schema(description = "接口类型", example = "ethernet")
private String type;
@Schema(description = "连接状态", example = "connected")
private String state;
@Schema(description = "NetworkManager中的连接名称通常与deviceName相同", example = "eth0")
private String connectionName;
@Schema(description = "IPv4 地址", example = "192.168.1.100")
private String ipv4;
@Schema(description = "IPv6 地址", example = "2001:db8::1")
private List<String> ipv6;
@Schema(description = "MAC 地址", example = "00:11:22:33:44:55")
private String mac;
@Schema(description = "默认网关地址", example = "192.168.1.1")
private String gateway;
}

View File

@ -0,0 +1,18 @@
package com.cisd.tms.modules.device.dto.network;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "路由信息响应")
public class RouteInfoResponse {
@Schema(description = "目标网络或主机CIDR格式或主机地址", example = "0.0.0.0/0")
private String destination;
@Schema(description = "下一跳地址(网关)", example = "192.168.1.1")
private String nextHop;
@Schema(description = "出口网络接口名称", example = "eth0")
private String interfaceName;
}

View File

@ -0,0 +1,15 @@
package com.cisd.tms.modules.device.dto.network;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "设置默认路由请求参数")
public class SetDefaultRouteRequest {
@Schema(description = "网络设备名称(如 eth0、bond0", example = "eth0")
private String deviceName;
@Schema(description = "默认网关IP地址", example = "192.168.1.1")
private String gatewayIp;
}

View File

@ -0,0 +1,39 @@
package com.cisd.tms.modules.device.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("tms_access_whitelist")
public class IpWhitelistEntity {
/**
* 主键
*/
private Long id;
/**
* IP地址
*/
private String ip;
/**
* 掩码长度
*/
private String mask;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}

View File

@ -0,0 +1,123 @@
package com.cisd.tms.modules.device.filter;
import com.cisd.tms.modules.device.service.IpWhitelistService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
@Slf4j
public class IpWhitelistFilter implements Filter {
@Autowired
private IpWhitelistService ipWhitelistService;
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String clientIp = getClientIp(request);
List<String> whitelist = ipWhitelistService.getAllIp();
// 空名单兜底
if (whitelist == null || whitelist.isEmpty()) {
filterChain.doFilter(request, response);
return;
}
int clientIpInt;
try {
clientIpInt = ipToInt(clientIp);
} catch (IllegalArgumentException e) {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("{\"code\": 403, \"msg\": \"非法的客户端 IP 格式,拒绝访问\"}");
return;
}
// 白名单匹配
boolean isAllowed = false;
for (String cidr : whitelist) {
try{
String[] parts = cidr.split("/");
String networkIp = parts[0];
int maskLength = parts.length > 1 ? Integer.parseInt(parts[1]) : 32;
if (maskLength < 0 || maskLength > 32) {
continue;
}
int networkIpInt = ipToInt(networkIp);
int mask = (maskLength == 0) ? 0 : (0xFFFFFFFF << (32 - maskLength));
isAllowed = (networkIpInt & mask) == (clientIpInt & mask);
if (isAllowed){
break;
}
} catch (IllegalArgumentException e){
log.error("数据库白名单{}该条规则解析异常,", cidr, e);
}
}
// 默认拒绝
if (!isAllowed) {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("{\"code\": 403, \"msg\": \"非法访问您的IP不在允许的白名单内\"}");
return;
}
filterChain.doFilter(request, response);
}
@Override
public void destroy() {
}
private String getClientIp(HttpServletRequest request) {
String[] headers = {
"X-Forwarded-For",
"Proxy-Client-IP",
"WL-Proxy-Client-IP",
"HTTP_CLIENT_IP",
"HTTP_X_FORWARDED_FOR"
};
for (String header : headers) {
String ip = request.getHeader(header);
if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) {
return ip.split(",")[0].trim();
}
}
return request.getRemoteAddr();
}
private int ipToInt(String ipAddress) {
String[] octets = ipAddress.split("\\.");
if (octets.length != 4) {
throw new IllegalArgumentException("非法的 IPv4 地址: " + ipAddress);
}
int result = 0;
for (int i = 0; i < 4; i++) {
int octet = Integer.parseInt(octets[i]);
if (octet < 0 || octet > 255) {
throw new IllegalArgumentException("非法的 IPv4 地址段: " + octet);
}
result |= (octet << (24 - (8 * i)));
}
return result;
}
}

View File

@ -0,0 +1,11 @@
package com.cisd.tms.modules.device.mapper;
import com.cisd.tms.infrastructure.persistence.mapper.BaseMapperX;
import com.cisd.tms.modules.device.entity.IpWhitelistEntity;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface IpWhitelistMapper extends BaseMapperX<IpWhitelistEntity> {
}

View File

@ -0,0 +1,32 @@
package com.cisd.tms.modules.device.repository;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cisd.tms.modules.device.dto.network.IpWhitelistRequest;
import com.cisd.tms.modules.device.entity.IpWhitelistEntity;
import java.util.List;
import java.util.Optional;
public interface IpWhitelistRepository {
void addWhitelist(IpWhitelistEntity entity);
Optional<IpWhitelistEntity> findById(Long id);
void updateWhitelist(IpWhitelistEntity entity);
void deleteById(Long id);
boolean existsByIpAndMask(String ip, String mask, Long excludeId);
/**
* 分页查询白名单
*/
Page<IpWhitelistEntity> selectPage(Page<IpWhitelistEntity> page, IpWhitelistRequest req);
/**
* 查询所有白名单
*/
List<IpWhitelistEntity> selectAll();
}

View File

@ -0,0 +1,75 @@
package com.cisd.tms.modules.device.repository.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cisd.tms.modules.device.dto.network.IpWhitelistRequest;
import com.cisd.tms.modules.device.entity.IpWhitelistEntity;
import com.cisd.tms.modules.device.mapper.IpWhitelistMapper;
import com.cisd.tms.modules.device.repository.IpWhitelistRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public class IpWhiltelistRepositoryImpl implements IpWhitelistRepository {
@Autowired
private IpWhitelistMapper ipWhitelistMapper;
@Override
public void addWhitelist(IpWhitelistEntity entity){
ipWhitelistMapper.insert(entity);
}
@Override
public Optional<IpWhitelistEntity> findById(Long id){
return Optional.ofNullable(ipWhitelistMapper.selectById(id));
}
@Override
public void updateWhitelist(IpWhitelistEntity entity){
ipWhitelistMapper.updateById(entity);
}
@Override
public void deleteById(Long id){
ipWhitelistMapper.deleteById(id);
}
@Override
public boolean existsByIpAndMask(String ip, String mask, Long excludeId) {
LambdaQueryWrapper<IpWhitelistEntity> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(IpWhitelistEntity::getIp, ip)
.eq(IpWhitelistEntity::getMask, mask);
if (excludeId != null) {
wrapper.ne(IpWhitelistEntity::getId, excludeId);
}
return ipWhitelistMapper.exists(wrapper);
}
@Override
public Page<IpWhitelistEntity> selectPage(Page<IpWhitelistEntity> page, IpWhitelistRequest req) {
// LambdaQueryWrapper<IpWhitelistEntity> wrapper = new LambdaQueryWrapper<>();
//
// if (StringUtils.hasText(req.getIp())) {
// wrapper.like(IpWhitelistEntity::getIp, req.getIp());
// }
// if (StringUtils.hasText(req.getMask())) {
// wrapper.eq(IpWhitelistEntity::getMask, req.getMask());
// }
// wrapper.orderByDesc(IpWhitelistEntity::getCreateTime);
return ipWhitelistMapper.selectPage(page, null);
}
@Override
public List<IpWhitelistEntity> selectAll() {
return ipWhitelistMapper.selectList(null);
}
}

View File

@ -0,0 +1,131 @@
package com.cisd.tms.modules.device.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cisd.tms.common.exception.BizException;
import com.cisd.tms.modules.device.dto.network.IpWhitelistRequest;
import com.cisd.tms.modules.device.dto.network.IpWhitelistResponse;
import com.cisd.tms.modules.device.entity.IpWhitelistEntity;
import com.cisd.tms.modules.device.repository.IpWhitelistRepository;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Service
public class IpWhitelistService {
private final IpWhitelistRepository ipWhitelistRepository;
public IpWhitelistService(IpWhitelistRepository ipWhitelistRepository) {
this.ipWhitelistRepository = ipWhitelistRepository;
}
public void addWhitelist(IpWhitelistRequest req) {
// 新增前校验 IP+掩码 是否已存在
checkIpRule(req.getIp(), req.getMask(), null);
IpWhitelistEntity entity = new IpWhitelistEntity();
String ip = req.getIp();
String mask = req.getMask();
entity.setIp(ip);
entity.setMask(mask);
ipWhitelistRepository.addWhitelist(entity);
}
//
public void updateWhitelist(IpWhitelistRequest req) {
if (req.getId() == null || req.getId().trim().isEmpty()) {
throw new IllegalArgumentException("ID should not be null");
}
Long idLong = Long.valueOf(req.getId());
checkIpRule(req.getIp(), req.getMask(), idLong);
Optional<IpWhitelistEntity> entityOpt = ipWhitelistRepository.findById(idLong);
if (entityOpt.isEmpty()) {
throw new BizException(404, "ID is not exist");
}
IpWhitelistEntity entity = entityOpt.get();
entity.setIp(req.getIp());
entity.setMask(req.getMask());
ipWhitelistRepository.updateWhitelist(entity);
}
public void deleteWhitelist(Long id) {
Optional<IpWhitelistEntity> entityOpt = ipWhitelistRepository.findById(id);
if (entityOpt.isEmpty()) {
throw new BizException(404, "ID is not exist");
}
ipWhitelistRepository.deleteById(id);
}
public IPage<IpWhitelistResponse> getWhitelistPage(IpWhitelistRequest req) {
Page<IpWhitelistEntity> page = new Page<>(req.getPageNum(), req.getPageSize());
ipWhitelistRepository.selectPage(page, req);
return page.convert(entity -> {
IpWhitelistResponse response = new IpWhitelistResponse();
response.setId(String.valueOf(entity.getId()));
response.setIp(entity.getIp());
response.setMask(entity.getMask());
return response;
});
}
public List<String> getAllIp() {
// CIDR 格式
return ipWhitelistRepository.selectAll().stream()
.map(entity -> entity.getIp() + "/" + entity.getMask())
.collect(Collectors.toList());
}
/**
* 校验 IP+掩码是否已存在
*/
private void checkIpRule(String ip, String mask, Long excludeId) {
if (ip == null || ip.isEmpty()){
throw new IllegalArgumentException("ip is empty");
}
if (mask == null || mask.isEmpty()){
throw new IllegalArgumentException("mask is empty");
}
String[] octets = ip.split("\\.");
for (int i = 0; i < 4; i++) {
if (octets.length != 4) {
throw new IllegalArgumentException("非法的 IPv4 地址: " + ip);
}
if (octets[i].length() > 1 && octets[i].startsWith("0")) {
throw new IllegalArgumentException("IP 地址段不允许包含前导零: " + octets[i]);
}
int octet = Integer.parseInt(octets[i]);
if (octet < 0 || octet > 255) {
throw new IllegalArgumentException("非法的 IPv4 地址段: " + octet);
}
}
int maskInt= Integer.parseInt(mask);
if (maskInt < 0 || maskInt > 32) {
throw new IllegalArgumentException("掩码范围需要在 0 到 32 之间");
}
if (ipWhitelistRepository.existsByIpAndMask(ip, mask, excludeId)) {
throw new BizException(404, "已存在该 IP 和掩码配置");
}
}
}

File diff suppressed because it is too large Load Diff