Explorar el Código

New: 新增IP、域名校验器

lwh hace 1 semana
padre
commit
1b3c108f58
Se han modificado 22 ficheros con 524 adiciones y 167 borrados
  1. 37 2
      pig-common/pig-common-feign/src/main/java/com/pig4cloud/pig/common/feign/sentinel/handle/GlobalBizExceptionHandler.java
  2. 2 1
      pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/dto/app/BatchUpdateMarketingAppsDTO.java
  3. 6 31
      pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/dto/app/MarketingAppsDomainDTO.java
  4. 6 49
      pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/dto/app/MarketingAppsIpDTO.java
  5. 37 0
      pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/dto/common/BaseDomainDTO.java
  6. 45 0
      pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/dto/common/BaseIpDTO.java
  7. 6 30
      pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/dto/config/AddMarketingConfigDomainListDTO.java
  8. 7 47
      pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/dto/config/AddMarketingConfigIpListDTO.java
  9. 3 0
      pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/dto/config/AddMarketingGroupDTO.java
  10. 2 0
      pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/dto/config/MarketingGroupDomainDTO.java
  11. 2 0
      pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/dto/config/MarketingGroupIPDTO.java
  12. 3 0
      pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/dto/config/ModMarketingGroupDomainDTO.java
  13. 4 1
      pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/dto/config/ModMarketingGroupIPDTO.java
  14. 32 0
      pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/util/DomainValidationUtil.java
  15. 50 0
      pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/util/IpValidationUtil.java
  16. 26 0
      pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/valid/DomainValidation.java
  17. 74 0
      pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/valid/DomainValidator.java
  18. 26 0
      pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/valid/IPValidation.java
  19. 116 0
      pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/valid/IPValidator.java
  20. 4 0
      pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/vo/config/GetMarketingGlobalConfigVO.java
  21. 7 1
      pig-marketing/pig-marketing-biz/src/main/java/com/pig4cloud/pig/marketing/controller/MarketingConfigController.java
  22. 29 5
      pig-marketing/pig-marketing-biz/src/main/java/com/pig4cloud/pig/marketing/service/impl/MarketingConfigServiceImpl.java

+ 37 - 2
pig-common/pig-common-feign/src/main/java/com/pig4cloud/pig/common/feign/sentinel/handle/GlobalBizExceptionHandler.java

@@ -18,6 +18,8 @@ package com.pig4cloud.pig.common.feign.sentinel.handle;
 
 import com.alibaba.csp.sentinel.Tracer;
 import com.pig4cloud.pig.common.core.util.R;
+import jakarta.validation.ConstraintViolation;
+import jakarta.validation.ConstraintViolationException;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
 import org.springframework.core.annotation.Order;
@@ -27,6 +29,7 @@ import org.springframework.security.core.SpringSecurityMessageSource;
 import org.springframework.util.Assert;
 import org.springframework.validation.BindException;
 import org.springframework.validation.FieldError;
+import org.springframework.validation.ObjectError;
 import org.springframework.web.bind.MethodArgumentNotValidException;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.ResponseStatus;
@@ -34,6 +37,7 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
 import org.springframework.web.servlet.resource.NoResourceFoundException;
 
 import java.util.List;
+import java.util.Set;
 
 /**
  * 全局业务异常处理器,结合Sentinel处理系统异常
@@ -104,9 +108,25 @@ public class GlobalBizExceptionHandler {
 	@ExceptionHandler({ MethodArgumentNotValidException.class })
 	@ResponseStatus(HttpStatus.BAD_REQUEST)
 	public R handleBodyValidException(MethodArgumentNotValidException exception) {
+		// 优先处理类级别的校验错误(如@IPValidation)
+		List<ObjectError> globalErrors = exception.getBindingResult().getGlobalErrors();
+		if (!globalErrors.isEmpty()) {
+			String errorMsg = globalErrors.get(0).getDefaultMessage();
+			log.warn("类级参数校验异常, ex = {}", errorMsg);
+			return R.failed(errorMsg);
+		}
+
+		// 处理字段级别的校验错误
 		List<FieldError> fieldErrors = exception.getBindingResult().getFieldErrors();
-		log.warn("参数绑定异常,ex = {}", fieldErrors.get(0).getDefaultMessage());
-		return R.failed(String.format("%s", fieldErrors.get(0).getDefaultMessage()));
+		if (!fieldErrors.isEmpty()) {
+			String errorMsg = fieldErrors.get(0).getDefaultMessage();
+			log.warn("字段参数绑定异常, ex = {}", errorMsg);
+			return R.failed(errorMsg);
+		}
+
+		// 兜底错误信息
+		log.warn("参数校验异常,但未获取到具体错误信息");
+		return R.failed("参数校验失败");
 	}
 
 	/**
@@ -137,4 +157,19 @@ public class GlobalBizExceptionHandler {
 		return R.failed(exception.getMessage());
 	}
 
+	// 新增:处理自定义校验注解异常
+	@ExceptionHandler(ConstraintViolationException.class)
+	@ResponseStatus(HttpStatus.BAD_REQUEST)
+	public R handleConstraintViolationException(ConstraintViolationException exception) {
+		Set<ConstraintViolation<?>> violations = exception.getConstraintViolations();
+		// 避免空集合导致的类似错误
+		if (violations.isEmpty()) {
+			return R.failed("参数校验失败,但未获取到具体错误信息");
+		}
+		// 获取第一个错误信息(自定义校验器中设置的message)
+		String errorMsg = violations.iterator().next().getMessage();
+		log.warn("自定义校验异常: {}", errorMsg);
+		return R.failed(errorMsg);
+	}
+
 }

+ 2 - 1
pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/dto/app/BatchUpdateMarketingAppsDTO.java

@@ -1,10 +1,10 @@
 package com.pig4cloud.pig.marketing.api.dto.app;
 
 
-import com.pig4cloud.pig.marketing.api.vo.app.MarketingAppsDomainVO;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
 import lombok.Data;
 
 import java.io.Serial;
@@ -55,6 +55,7 @@ public class BatchUpdateMarketingAppsDTO implements Serializable {
 	 * 备注
 	 */
 	@Schema(description = "备注")
+	@Size(max = 255, message = "备注长度不能超过255")
 	private String remark;
 
 	/**

+ 6 - 31
pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/dto/app/MarketingAppsDomainDTO.java

@@ -1,11 +1,11 @@
 package com.pig4cloud.pig.marketing.api.dto.app;
 
 
+import com.pig4cloud.pig.marketing.api.dto.common.BaseDomainDTO;
+import com.pig4cloud.pig.marketing.api.valid.DomainValidation;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
-
-import java.io.Serial;
-import java.io.Serializable;
+import lombok.EqualsAndHashCode;
 
 /**
  * @author: lwh
@@ -13,11 +13,10 @@ import java.io.Serializable;
  * @description: 应用域名列表
  */
 @Data
+@DomainValidation
+@EqualsAndHashCode(callSuper = true)
 @Schema(description = "应用域名列表")
-public class MarketingAppsDomainDTO implements Serializable {
-
-	@Serial
-	private static final long serialVersionUID = 1L;
+public class MarketingAppsDomainDTO extends BaseDomainDTO {
 
 	/**
 	 * id
@@ -25,30 +24,6 @@ public class MarketingAppsDomainDTO implements Serializable {
 	@Schema(description = "id")
 	private Long id;
 
-	/**
-	 * 来源类型
-	 */
-	@Schema(description = "来源类型,1-来自分组,2-具体域名")
-	private Integer sourceType;
-
-	/**
-	 * 分组ID
-	 */
-	@Schema(description = "分组ID")
-	private Long groupId;
-
-	/**
-	 * 分组名称
-	 */
-	@Schema(description = "分组名称")
-	private String groupName;
-
-	/**
-	 * 域名
-	 */
-	@Schema(description = "域名")
-	private String domain;
-
 	/**
 	 * 是否修改
 	 */

+ 6 - 49
pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/dto/app/MarketingAppsIpDTO.java

@@ -1,11 +1,12 @@
 package com.pig4cloud.pig.marketing.api.dto.app;
 
 
+import com.pig4cloud.pig.marketing.api.dto.common.BaseIpDTO;
+import com.pig4cloud.pig.marketing.api.valid.IPValidation;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
+import lombok.EqualsAndHashCode;
 
-import java.io.Serial;
-import java.io.Serializable;
 
 /**
  * @author: lwh
@@ -13,12 +14,10 @@ import java.io.Serializable;
  * @description: 应用ip列表入参
  */
 @Data
+@IPValidation
+@EqualsAndHashCode(callSuper = true)
 @Schema(description = "应用ip列表入参")
-public class MarketingAppsIpDTO implements Serializable {
-
-
-	@Serial
-	private static final long serialVersionUID = 1L;
+public class MarketingAppsIpDTO extends BaseIpDTO {
 
 	/**
 	 * id
@@ -26,48 +25,6 @@ public class MarketingAppsIpDTO implements Serializable {
 	@Schema(description = "id")
 	private Long id;
 
-	/**
-	 * ip类型
-	 */
-	@Schema(description = "ip类型,1-白名单,2-黑名单")
-	private Integer ipType;
-
-	/**
-	 * 来源类型
-	 */
-	@Schema(description = "来源类型,1-来自分组,2-具体IP")
-	private Integer sourceType;
-
-	/**
-	 * 分组ID
-	 */
-	@Schema(description = "分组ID")
-	private Long groupId;
-
-	/**
-	 * 分组名称
-	 */
-	@Schema(description = "分组名称")
-	private String groupName;
-
-	/**
-	 * ip模式
-	 */
-	@Schema(description = "ip模式,1-单IP,2-IP段")
-	private Integer ipMode;
-
-	/**
-	 * 起始IP
-	 */
-	@Schema(description = "起始IP")
-	private String startIp;
-
-	/**
-	 * 结束IP
-	 */
-	@Schema(description = "结束IP")
-	private String endIp;
-
 	/**
 	 * 是否修改
 	 */

+ 37 - 0
pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/dto/common/BaseDomainDTO.java

@@ -0,0 +1,37 @@
+package com.pig4cloud.pig.marketing.api.dto.common;
+
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * @author: lwh
+ * @date: 2025-07-29
+ * @description: 域名相关DTO的公共父类
+ */
+@Data
+public class BaseDomainDTO implements Serializable {
+	@Serial
+	private static final long serialVersionUID = 1L;
+
+	@NotNull(message = "来源类型不能为空")
+	@Schema(description = "来源类型,1-来自分组,2-具体域名")
+	private Integer sourceType;
+
+	@Schema(description = "分组ID")
+	private Long groupId;
+
+	@Schema(description = "分组名称")
+	@Size(max = 255, message = "分组名称长度不能超过255")
+	private String groupName;
+
+	@Schema(description = "域名")
+	@Size(max = 255, message = "域名长度不能超过255")
+	private String domain;
+
+}

+ 45 - 0
pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/dto/common/BaseIpDTO.java

@@ -0,0 +1,45 @@
+package com.pig4cloud.pig.marketing.api.dto.common;
+
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * @author: lwh
+ * @date: 2025-07-29
+ * @description: IP相关DTO的公共父类
+ */
+@Data
+public class BaseIpDTO implements Serializable {
+	@Serial
+	private static final long serialVersionUID = 1L;
+
+	@NotNull(message = "ipType不能为空")
+	@Schema(description = "ip类型,1-白名单,2-黑名单")
+	private Integer ipType;
+
+	@NotNull(message = "sourceType不能为空")
+	@Schema(description = "来源类型,1-来自分组,2-具体IP")
+	private Integer sourceType;
+
+	@Schema(description = "分组ID")
+	private Long groupId;
+
+	@Schema(description = "分组名称")
+	@Size(max = 255, message = "分组名称长度不能超过255")
+	private String groupName;
+
+	@Schema(description = "ip模式,1-单IP,2-IP段")
+	private Integer ipMode;
+
+	@Schema(description = "起始IP")
+	private String startIp;
+
+	@Schema(description = "结束IP")
+	private String endIp;
+}

+ 6 - 30
pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/dto/config/AddMarketingConfigDomainListDTO.java

@@ -1,11 +1,11 @@
 package com.pig4cloud.pig.marketing.api.dto.config;
 
 
+import com.pig4cloud.pig.marketing.api.dto.common.BaseDomainDTO;
+import com.pig4cloud.pig.marketing.api.valid.DomainValidation;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
-
-import java.io.Serial;
-import java.io.Serializable;
+import lombok.EqualsAndHashCode;
 
 /**
  * @author: lwh
@@ -13,33 +13,9 @@ import java.io.Serializable;
  * @description: 添加域名入参
  */
 @Data
+@DomainValidation
+@EqualsAndHashCode(callSuper = true)
 @Schema(description = "添加域名入参")
-public class AddMarketingConfigDomainListDTO implements Serializable {
-
-	@Serial
-	private static final long serialVersionUID = 1L;
-
-	/**
-	 * 来源类型
-	 */
-	@Schema(description = "来源类型,1-来自分组,2-具体域名")
-	private Integer sourceType;
-
-	/**
-	 * 分组ID
-	 */
-	@Schema(description = "分组ID")
-	private Long groupId;
-
-	/**
-	 * 分组名称
-	 */
-	@Schema(description = "分组名称")
-	private String groupName;
+public class AddMarketingConfigDomainListDTO extends BaseDomainDTO {
 
-	/**
-	 * 域名
-	 */
-	@Schema(description = "域名")
-	private String domain;
 }

+ 7 - 47
pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/dto/config/AddMarketingConfigIpListDTO.java

@@ -1,10 +1,14 @@
 package com.pig4cloud.pig.marketing.api.dto.config;
 
 
+import com.pig4cloud.pig.marketing.api.dto.common.BaseIpDTO;
+import com.pig4cloud.pig.marketing.api.valid.IPValidation;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.NotEmpty;
 import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
 import lombok.Data;
+import lombok.EqualsAndHashCode;
 
 import java.io.Serial;
 import java.io.Serializable;
@@ -15,53 +19,9 @@ import java.io.Serializable;
  * @description: 添加IP集合入参
  */
 @Data
+@IPValidation
+@EqualsAndHashCode(callSuper = true)
 @Schema(description = "添加IP集合入参")
-public class AddMarketingConfigIpListDTO implements Serializable {
+public class AddMarketingConfigIpListDTO extends BaseIpDTO {
 
-	@Serial
-	private static final long serialVersionUID = 1L;
-
-	/**
-	 * ip类型
-	 */
-	@Schema(description = "ip类型,1-白名单,2-黑名单")
-	@NotNull(message = "ip类型不能为空")
-	private Integer ipType;
-
-	/**
-	 * 来源类型
-	 */
-	@Schema(description = "来源类型,1-来自分组,2-具体IP")
-	@NotNull(message = "来源类型不能为空")
-	private Integer sourceType;
-
-	/**
-	 * 分组ID
-	 */
-	@Schema(description = "分组ID")
-	private Long groupId;
-
-	/**
-	 * 分组名称
-	 */
-	@Schema(description = "分组名称")
-	private String groupName;
-
-	/**
-	 * ip模式
-	 */
-	@Schema(description = "ip模式,1-单IP,2-IP段")
-	private Integer ipMode;
-
-	/**
-	 * 起始IP
-	 */
-	@Schema(description = "起始IP")
-	private String startIp;
-
-	/**
-	 * 结束IP
-	 */
-	@Schema(description = "结束IP")
-	private String endIp;
 }

+ 3 - 0
pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/dto/config/AddMarketingGroupDTO.java

@@ -4,6 +4,7 @@ package com.pig4cloud.pig.marketing.api.dto.config;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.NotEmpty;
 import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
 import lombok.Data;
 
 import java.io.Serial;
@@ -22,6 +23,7 @@ public class AddMarketingGroupDTO implements Serializable {
 	private static final long serialVersionUID = 1L;
 
 	@Schema(description = "分组名称", example = "default")
+	@Size(max = 255, message = "分组名称长度不能超过255")
 	@NotEmpty(message = "分组名称不能为空")
 	private String groupName;
 
@@ -30,5 +32,6 @@ public class AddMarketingGroupDTO implements Serializable {
 	private Integer groupType;
 
 	@Schema(description = "备注")
+	@Size(max = 255, message = "备注长度不能超过255")
 	private String remark;
 }

+ 2 - 0
pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/dto/config/MarketingGroupDomainDTO.java

@@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotEmpty;
 import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
 import lombok.Data;
 
 import java.io.Serial;
@@ -33,6 +34,7 @@ public class MarketingGroupDomainDTO implements Serializable {
 	 */
 	@Schema(description = "域名")
 	@NotBlank(message = "域名不能为空")
+	@Size(max = 255, message = "域名长度不能超过255")
 	private String domain;
 
 	/**

+ 2 - 0
pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/dto/config/MarketingGroupIPDTO.java

@@ -2,6 +2,7 @@ package com.pig4cloud.pig.marketing.api.dto.config;
 
 
 import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
 import lombok.Data;
 
 import java.io.Serial;
@@ -29,6 +30,7 @@ public class MarketingGroupIPDTO implements Serializable {
 	 * ip模式,1-单IP,2-IP段
 	 */
 	@Schema(description = "ip模式,1-单IP,2-IP段")
+	@NotNull(message = "ip模式不能为空")
 	private Integer ipMode;
 
 	/**

+ 3 - 0
pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/dto/config/ModMarketingGroupDomainDTO.java

@@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
 import lombok.Data;
 
 import java.io.Serial;
@@ -34,6 +35,7 @@ public class ModMarketingGroupDomainDTO implements Serializable {
 	 * 分组名称
 	 */
 	@Schema(description = "分组名称")
+	@Size(max = 255, message = "分组名称长度不能超过255")
 	@NotBlank(message = "分组名称不能为空")
 	private String groupName;
 
@@ -41,6 +43,7 @@ public class ModMarketingGroupDomainDTO implements Serializable {
 	 * 备注
 	 */
 	@Schema(description = "备注")
+	@Size(max = 255, message = "备注长度不能超过255")
 	private String remark;
 
 	/**

+ 4 - 1
pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/dto/config/ModMarketingGroupIPDTO.java

@@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
 import lombok.Data;
 
 import java.io.Serial;
@@ -34,6 +35,7 @@ public class ModMarketingGroupIPDTO implements Serializable {
 	 * 分组名称
 	 */
 	@Schema(description = "分组名称")
+	@Size(max = 255, message = "分组名称长度不能超过255")
 	@NotBlank(message = "分组名称不能为空")
 	private String groupName;
 
@@ -41,6 +43,7 @@ public class ModMarketingGroupIPDTO implements Serializable {
 	 * 备注
 	 */
 	@Schema(description = "备注")
+	@Size(max = 255, message = "备注长度不能超过255")
 	private String remark;
 
 	/**
@@ -50,7 +53,7 @@ public class ModMarketingGroupIPDTO implements Serializable {
 	List<Long> delList;
 
 	/**
-	 * 分组域名
+	 * 分组IP
 	 */
 	@Valid
 	@Schema(description = "分组IP")

+ 32 - 0
pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/util/DomainValidationUtil.java

@@ -0,0 +1,32 @@
+package com.pig4cloud.pig.marketing.api.util;
+
+
+import java.util.regex.Pattern;
+
+/**
+ * @author: lwh
+ * @date: 2025-07-29
+ * @description: 域名校验工具类
+ */
+
+public class DomainValidationUtil {
+	// 简单域名正则(支持字母、数字、下划线、横线、点,且点不能连续)
+	private static final Pattern DOMAIN_PATTERN = Pattern.compile(
+			"^[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)*$"
+	);
+
+	/**
+	 * 验证域名格式是否合法
+	 */
+	public static boolean isValidDomain(String domain) {
+		if (domain == null || domain.trim().isEmpty()) {
+			return false;
+		}
+		String trimDomain = domain.trim();
+		// 长度限制(1-255字符)
+		if (trimDomain.length() < 1 || trimDomain.length() > 255) {
+			return false;
+		}
+		return DOMAIN_PATTERN.matcher(trimDomain).matches();
+	}
+}

+ 50 - 0
pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/util/IpValidationUtil.java

@@ -0,0 +1,50 @@
+package com.pig4cloud.pig.marketing.api.util;
+
+
+import java.util.regex.Pattern;
+
+/**
+ * @author: lwh
+ * @date: 2025-07-29
+ * @description: IP校验工具类
+ */
+
+public class IpValidationUtil {
+	// IP地址正则表达式(IPv4)
+	private static final Pattern IP_PATTERN = Pattern.compile(
+			"^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$"
+	);
+
+	/**
+	 * 验证IP地址格式是否合法
+	 */
+	public static boolean isValidIp(String ip) {
+		if (ip == null || ip.trim().isEmpty()) {
+			return false;
+		}
+		return IP_PATTERN.matcher(ip.trim()).matches();
+	}
+
+	/**
+	 * 比较两个IP地址大小(将IP转为Long比较)
+	 * @return true:endIp > startIp
+	 */
+	public static boolean isEndIpGreater(String startIp, String endIp) {
+		if (!isValidIp(startIp) || !isValidIp(endIp)) {
+			return false;
+		}
+		return ipToLong(startIp) < ipToLong(endIp);
+	}
+
+	/**
+	 * IP地址转Long(便于比较大小)
+	 */
+	private static long ipToLong(String ip) {
+		String[] parts = ip.split("\\.");
+		long result = 0;
+		for (int i = 0; i < 4; i++) {
+			result |= Long.parseLong(parts[i]) << (24 - i * 8);
+		}
+		return result;
+	}
+}

+ 26 - 0
pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/valid/DomainValidation.java

@@ -0,0 +1,26 @@
+package com.pig4cloud.pig.marketing.api.valid;
+
+
+import jakarta.validation.Constraint;
+import jakarta.validation.Payload;
+
+import java.lang.annotation.*;
+
+/**
+ * @author: lwh
+ * @date: 2025-07-29
+ * @description: 域名相关字段校验注解
+ */
+
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Constraint(validatedBy = {DomainValidator.class})
+@Documented
+public @interface DomainValidation {
+	String message() default "域名校验失败:不符合来源类型与其他字段的关联规则";
+
+	Class<?>[] groups() default {};
+
+	Class<? extends Payload>[] payload() default {};
+}
+

+ 74 - 0
pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/valid/DomainValidator.java

@@ -0,0 +1,74 @@
+package com.pig4cloud.pig.marketing.api.valid;
+
+
+import com.pig4cloud.pig.marketing.api.dto.app.MarketingAppsDomainDTO;
+import com.pig4cloud.pig.marketing.api.dto.common.BaseDomainDTO;
+import com.pig4cloud.pig.marketing.api.util.DomainValidationUtil;
+import jakarta.validation.ConstraintValidator;
+import jakarta.validation.ConstraintValidatorContext;
+
+/**
+ * @author: lwh
+ * @date: 2025-07-29
+ * @description: 域名校验器
+ */
+
+public class DomainValidator implements ConstraintValidator<DomainValidation, Object> {
+
+	@Override
+	public boolean isValid(Object value, ConstraintValidatorContext context) {
+		if (!(value instanceof BaseDomainDTO dto)) {
+			return true;
+		}
+
+		Integer sourceType = dto.getSourceType();
+		Long groupId = dto.getGroupId();
+		String domain = dto.getDomain();
+
+		// 来源类型为分组(1)时的校验
+		if (sourceType == 1) {
+			if (groupId == null) {
+				setMessage(context, "来源为分组时,分组ID不能为空");
+				return false;
+			}
+			// 分组模式下不能有域名相关字段
+			if (notEmpty(domain)) {
+				setMessage(context, "来源为分组时,不能填写域名");
+				return false;
+			}
+			return true;
+		}
+
+		// 来源类型为具体域名(2)时的校验
+		if (sourceType == 2) {
+			if (groupId != null) {
+				setMessage(context, "来源为具体域名时,分组ID必须为空");
+				return false;
+			}
+			if (!notEmpty(domain)) {
+				setMessage(context, "来源为具体域名时,域名不能为空");
+				return false;
+			}
+			// 校验域名格式
+			if (!DomainValidationUtil.isValidDomain(domain)) {
+				setMessage(context, "域名格式不合法(支持字母、数字、下划线、横线、点)");
+				return false;
+			}
+			return true;
+		}
+
+		setMessage(context, "来源类型值不合法(1-来自分组,2-具体域名)");
+		return false;
+	}
+
+	// 工具方法:判断字符串非空
+	private boolean notEmpty(String str) {
+		return str != null && !str.trim().isEmpty();
+	}
+
+	// 自定义错误信息
+	private void setMessage(ConstraintValidatorContext context, String message) {
+		context.disableDefaultConstraintViolation();
+		context.buildConstraintViolationWithTemplate(message).addConstraintViolation();
+	}
+}

+ 26 - 0
pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/valid/IPValidation.java

@@ -0,0 +1,26 @@
+package com.pig4cloud.pig.marketing.api.valid;
+
+
+import jakarta.validation.Constraint;
+import jakarta.validation.Payload;
+
+import java.lang.annotation.*;
+
+/**
+ * @author: lwh
+ * @date: 2025-07-29
+ * @description: IP相关字段校验注解
+ */
+
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Constraint(validatedBy = {IPValidator.class})
+@Documented
+public @interface IPValidation {
+	String message() default "IP校验失败:不符合sourceType与其他字段的关联规则";
+
+	Class<?>[] groups() default {};
+
+	Class<? extends Payload>[] payload() default {};
+}
+

+ 116 - 0
pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/valid/IPValidator.java

@@ -0,0 +1,116 @@
+package com.pig4cloud.pig.marketing.api.valid;
+
+
+import com.pig4cloud.pig.marketing.api.dto.app.MarketingAppsIpDTO;
+import com.pig4cloud.pig.marketing.api.dto.common.BaseIpDTO;
+import com.pig4cloud.pig.marketing.api.util.IpValidationUtil;
+import jakarta.validation.ConstraintValidator;
+import jakarta.validation.ConstraintValidatorContext;
+
+/**
+ * @author: lwh
+ * @date: 2025-07-29
+ * @description: IP校验器
+ */
+
+public class IPValidator implements ConstraintValidator<IPValidation, Object> {
+
+	@Override
+	public boolean isValid(Object value, ConstraintValidatorContext context) {
+		if (!(value instanceof BaseIpDTO dto)) {
+			return true;
+		}
+
+		Integer sourceType = dto.getSourceType();
+		Long groupId = dto.getGroupId();
+		Integer ipMode = dto.getIpMode();
+		String startIp = dto.getStartIp();
+		String endIp = dto.getEndIp();
+
+		// 来源类型为分组(1)时的校验
+		if (sourceType == 1) {
+			if (groupId == null) {
+				setMessage(context, "来源为分组时,分组ID不能为空");
+				return false;
+			}
+			// 分组模式下不能有IP相关字段
+			if (ipMode != null || notEmpty(startIp) || notEmpty(endIp)) {
+				setMessage(context, "来源为分组时,不能填写IP模式、IP地址");
+				return false;
+			}
+			return true;
+		}
+
+		// 来源类型为具体IP(2)时的校验
+		if (sourceType == 2) {
+			// 具体IP模式下不能有groupId
+			if (groupId != null) {
+				setMessage(context, "来源为具体IP时,分组ID必须为空");
+				return false;
+			}
+			// 必须指定IP模式
+			if (ipMode == null) {
+				setMessage(context, "来源为具体IP时,IP模式不能为空");
+				return false;
+			}
+
+			// 单IP模式(ipMode=1)
+			if (ipMode == 1) {
+				if (!notEmpty(startIp)) {
+					setMessage(context, "单IP模式下,startIp不能为空");
+					return false;
+				}
+				if (notEmpty(endIp)) {
+					setMessage(context, "单IP模式下,endIp必须为空");
+					return false;
+				}
+				// 校验IP格式
+				if (!IpValidationUtil.isValidIp(startIp)) {
+					setMessage(context, "IP格式不合法(IPv4)");
+					return false;
+				}
+				return true;
+			}
+
+			// IP段模式(ipMode=2)
+			if (ipMode == 2) {
+				if (!notEmpty(startIp) || !notEmpty(endIp)) {
+					setMessage(context, "IP段模式下,startIp和endIp都不能为空");
+					return false;
+				}
+				// 校验IP格式
+				if (!IpValidationUtil.isValidIp(startIp)) {
+					setMessage(context, "startIp格式不合法(IPv4)");
+					return false;
+				}
+				if (!IpValidationUtil.isValidIp(endIp)) {
+					setMessage(context, "endIp格式不合法(IPv4)");
+					return false;
+				}
+				// 校验endIp > startIp
+				if (!IpValidationUtil.isEndIpGreater(startIp, endIp)) {
+					setMessage(context, "IP段模式下,endIp必须大于startIp");
+					return false;
+				}
+				return true;
+			}
+
+			setMessage(context, "IP模式值不合法(1-单IP,2-IP段)");
+			return false;
+		}
+
+		setMessage(context, "来源类型值不合法(1-来自分组,2-具体IP)");
+		return false;
+	}
+
+	// 工具方法:判断字符串非空
+	private boolean notEmpty(String str) {
+		return str != null && !str.trim().isEmpty();
+	}
+
+	// 自定义错误信息
+	private void setMessage(ConstraintValidatorContext context, String message) {
+		context.disableDefaultConstraintViolation();
+		context.buildConstraintViolationWithTemplate(message).addConstraintViolation();
+	}
+}

+ 4 - 0
pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/vo/config/GetMarketingGlobalConfigVO.java

@@ -4,6 +4,7 @@ package com.pig4cloud.pig.marketing.api.vo.config;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
 import lombok.Data;
 
 import java.io.Serial;
@@ -47,6 +48,8 @@ public class GetMarketingGlobalConfigVO implements Serializable {
 	 * 提示信息
 	 */
 	@Schema(description = "提示信息")
+	@NotBlank(message = "提示信息不能为空")
+	@Size(max = 128, message = "提示信息长度不能超过128")
 	String promptMsg;
 
 	/**
@@ -54,5 +57,6 @@ public class GetMarketingGlobalConfigVO implements Serializable {
 	 */
 	@Schema(description = "跳转链接")
 	@NotBlank(message = "跳转链接不能为空")
+	@Size(max = 128, message = "跳转链接长度不能超过128")
 	String url;
 }

+ 7 - 1
pig-marketing/pig-marketing-biz/src/main/java/com/pig4cloud/pig/marketing/controller/MarketingConfigController.java

@@ -48,7 +48,7 @@ public class MarketingConfigController {
 	 */
 	@PostMapping("/delGroup")
 	@Operation(summary = "删除分组")
-	public R delMarketingConfigGroup(@RequestBody DelMarketingGroupDTO reqDto) {
+	public R delMarketingConfigGroup(@Valid @RequestBody DelMarketingGroupDTO reqDto) {
 		return R.ok(marketingConfigService.delMarketingConfigGroup(reqDto));
 	}
 
@@ -135,6 +135,9 @@ public class MarketingConfigController {
 	@GetMapping("/delIp/{id}")
 	@Operation(summary = "IP集合删除IP")
 	public R delMarketingConfigIpList(@PathVariable Long id) {
+		if (id == null) {
+			throw new BusinessException("id不能为空");
+		}
 		return R.ok(marketingConfigService.delMarketingConfigIpList(id));
 	}
 
@@ -166,6 +169,9 @@ public class MarketingConfigController {
 	@GetMapping("/delDomain/{id}")
 	@Operation(summary = "域名集合删除域名")
 	public R delMarketingConfigDomainList(@PathVariable Long id) {
+		if (id == null){
+			throw new BusinessException("id不能为空");
+		}
 		return R.ok(marketingConfigService.delMarketingConfigDomainList(id));
 	}
 

+ 29 - 5
pig-marketing/pig-marketing-biz/src/main/java/com/pig4cloud/pig/marketing/service/impl/MarketingConfigServiceImpl.java

@@ -7,6 +7,7 @@ import com.pig4cloud.pig.admin.api.dto.SysPublicParamDTO;
 import com.pig4cloud.pig.admin.api.feign.RemoteParamService;
 import com.pig4cloud.pig.common.core.exception.BusinessException;
 import com.pig4cloud.pig.common.core.util.R;
+import com.pig4cloud.pig.marketing.api.dto.app.MarketingAppsIpDTO;
 import com.pig4cloud.pig.marketing.api.dto.config.*;
 import com.pig4cloud.pig.marketing.api.entity.*;
 import com.pig4cloud.pig.marketing.api.vo.config.*;
@@ -18,6 +19,7 @@ import org.springframework.beans.BeanUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.math.BigDecimal;
 import java.util.*;
 
 
@@ -73,23 +75,34 @@ public class MarketingConfigServiceImpl implements MarketingConfigService {
 	@Transactional(rollbackFor = Exception.class)
 	public Boolean delMarketingConfigGroup(DelMarketingGroupDTO reqDto) {
 		List<Long> ids = reqDto.getIds();
+		for (Long id : reqDto.getIds()) {
+			MarketingConfigGroup group = groupMapper.selectOne(Wrappers.<MarketingConfigGroup>lambdaQuery()
+					.eq(MarketingConfigGroup::getId, id)
+					.eq(MarketingConfigGroup::getGroupType, reqDto.getGroupType())
+			);
+			if (group == null){
+				if (reqDto.getGroupType() == 1)
+					throw new BusinessException("分组id:"+id+",IP分组不存在");
+				else
+					throw new BusinessException("分组id:"+id+",域名分组不存在");
+			}
+		}
+
 		// 删除分组
 		int delGroup = groupMapper.deleteByIds(ids);
 		// 删除分组下域名或ip
-		int item = 0;
-		// 删除分组子项
 		if (reqDto.getGroupType() == 1){
 			// 删除IP
-			item = groupIpMapper.delete(Wrappers.<MarketingGroupIp>lambdaQuery().in(MarketingGroupIp::getGroupId, ids));
+			groupIpMapper.delete(Wrappers.<MarketingGroupIp>lambdaQuery().in(MarketingGroupIp::getGroupId, ids));
 			// 删除应用、全局配置关联的IP分组
 			ipMapper.delete(Wrappers.<MarketingAppsIp>lambdaQuery().in(MarketingAppsIp::getGroupId, ids));
 		}else if (reqDto.getGroupType() == 2){
 			// 删除域名
-			item = groupDomainMapper.delete(Wrappers.<MarketingGroupDomain>lambdaQuery().in(MarketingGroupDomain::getGroupId, ids));
+			groupDomainMapper.delete(Wrappers.<MarketingGroupDomain>lambdaQuery().in(MarketingGroupDomain::getGroupId, ids));
 			// 删除应用、全局配置关联的域名分组
 			domainMapper.delete(Wrappers.<MarketingAppsDomain>lambdaQuery().in(MarketingAppsDomain::getGroupId, ids));
 		}
-		return delGroup > 0 && item > 0;
+		return delGroup > 0;
 	}
 
 	/**
@@ -339,6 +352,10 @@ public class MarketingConfigServiceImpl implements MarketingConfigService {
 	public Boolean addMarketingConfigIpList(AddMarketingConfigIpListDTO reqDto) {
 		// 判断IP表中是否重复
 		// 判断分组中是否重复
+		MarketingAppsIpDTO ipDTO = new MarketingAppsIpDTO();
+		BeanUtils.copyProperties(reqDto, ipDTO);
+
+//		checkIp(ipDTO);
 
 		MarketingAppsIp marketingAppsIp = new MarketingAppsIp();
 		BeanUtils.copyProperties(reqDto, marketingAppsIp);
@@ -478,6 +495,13 @@ public class MarketingConfigServiceImpl implements MarketingConfigService {
 			sysParamList.add(sysParam);
 		}
 		if (StringUtils.isNotBlank(reDto.getTriggerNum())){
+			BigDecimal bigDecimal = new BigDecimal(reDto.getTriggerNum());
+			if (bigDecimal.compareTo(BigDecimal.ZERO) > 0){
+				throw new BusinessException("触发频率不能小于0");
+			}
+			if (bigDecimal.compareTo(new BigDecimal("10000")) > 0) {
+				throw new BusinessException("触发频率不能大于10000");
+			}
 			sysParam = new SysPublicParamDTO();
 			sysParam.setPublicKey(TRIGGER_NUM);
 			sysParam.setPublicValue(reDto.getTriggerNum());