浏览代码

New: 新增全局异常处理,修改IP、域名验证器校验方式

lwh 1 周之前
父节点
当前提交
56dc2726fe
共有 15 个文件被更改,包括 671 次插入208 次删除
  1. 6 0
      pig-common/pig-common-bom/pom.xml
  2. 6 1
      pig-common/pig-common-core/pom.xml
  3. 248 0
      pig-common/pig-common-core/src/main/java/com/pig4cloud/pig/common/core/util/ip/IPUtils.java
  4. 二进制
      pig-common/pig-common-core/src/main/resources/ip/ip2region.xdb
  5. 108 2
      pig-common/pig-common-feign/src/main/java/com/pig4cloud/pig/common/feign/sentinel/handle/GlobalBizExceptionHandler.java
  6. 17 0
      pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/MarketingConfigConstants.java
  7. 1 1
      pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/util/DomainValidationUtil.java
  8. 0 50
      pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/util/IpValidationUtil.java
  9. 9 9
      pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/valid/DomainValidator.java
  10. 19 19
      pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/valid/IPValidator.java
  11. 3 2
      pig-marketing/pig-marketing-biz/src/main/java/com/pig4cloud/pig/marketing/controller/MarketingAppsController.java
  12. 12 0
      pig-marketing/pig-marketing-biz/src/main/java/com/pig4cloud/pig/marketing/service/MarketingAppsService.java
  13. 91 23
      pig-marketing/pig-marketing-biz/src/main/java/com/pig4cloud/pig/marketing/service/impl/MarketingAppsServiceImpl.java
  14. 87 20
      pig-marketing/pig-marketing-biz/src/main/java/com/pig4cloud/pig/marketing/service/impl/MarketingConfigServiceImpl.java
  15. 64 81
      pig-marketing/pig-marketing-biz/src/main/java/com/pig4cloud/pig/marketing/service/impl/MarketingDataServiceImpl.java

+ 6 - 0
pig-common/pig-common-bom/pom.xml

@@ -38,6 +38,7 @@
         <common.io.version>2.18.0</common.io.version>
         <spring.checkstyle.plugin>0.0.43</spring.checkstyle.plugin>
         <flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version>
+		<ip2region.version>2.6.6</ip2region.version>
     </properties>
 
     <!-- 定义全局jar版本,模块使用需要再次引入但不用写版本号-->
@@ -188,6 +189,11 @@
                     </exclusion>
                 </exclusions>
             </dependency>
+			<dependency>
+				<groupId>org.lionsoul</groupId>
+				<artifactId>ip2region</artifactId>
+				<version>${ip2region.version}</version>
+			</dependency>
         </dependencies>
     </dependencyManagement>
 

+ 6 - 1
pig-common/pig-common-core/pom.xml

@@ -65,5 +65,10 @@
             <groupId>org.springframework.cloud</groupId>
             <artifactId>spring-cloud-commons</artifactId>
         </dependency>
-    </dependencies>
+		<!-- IP地区 -->
+		<dependency>
+			<groupId>org.lionsoul</groupId>
+			<artifactId>ip2region</artifactId>
+		</dependency>
+	</dependencies>
 </project>

+ 248 - 0
pig-common/pig-common-core/src/main/java/com/pig4cloud/pig/common/core/util/ip/IPUtils.java

@@ -0,0 +1,248 @@
+package com.pig4cloud.pig.common.core.util.ip;
+
+import cn.hutool.core.util.StrUtil;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.extern.log4j.Log4j2;
+import org.lionsoul.ip2region.xdb.Searcher;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.InetAddress;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+@Log4j2
+public class IPUtils {
+
+	// 内网IP段正则
+	private static final String INTRANET_IP_REGEX = "(10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})|" +
+			"(172\\.((1[6-9])|(2\\d)|(3[01]))\\.\\d{1,3}\\.\\d{1,3})|" +
+			"(192\\.168\\.\\d{1,3}\\.\\d{1,3})|" +
+			"(127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})";
+	private static final Pattern INTRANET_PATTERN = Pattern.compile(INTRANET_IP_REGEX);
+
+	// IP地址合法性校验正则
+	private static final String IP_REGEX = "^(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[1-9])\\." +
+			"(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\." +
+			"(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\." +
+			"(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)$";
+	private static final Pattern IP_PATTERN = Pattern.compile(IP_REGEX);
+
+	private static Searcher searcher;
+
+	static {
+		initIpSearcher();
+	}
+
+	/**
+	 * 初始化IP地址索引
+	 */
+	private static void initIpSearcher() {
+		try {
+			// 基于内存查询-从资源文件加载xdb数据库
+			String dbPath = IPUtils.class.getClassLoader().getResource("ip/ip2region.xdb").getPath();
+			byte[] cBuff = Searcher.loadContentFromFile(dbPath);
+			searcher = Searcher.newWithBuffer(cBuff);
+			log.info("ip2region初始化成功");
+		} catch (Exception e) {
+			log.error("ip2region初始化失败", e);
+			searcher = null;
+		}
+	}
+
+	/**
+	 * 获取IP地址
+	 *
+	 * @param request request
+	 * @return ipAddress
+	 */
+	public static String getIpAddress(HttpServletRequest request) {
+		String ip = request.getHeader("x-forwarded-for");
+		if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
+			ip = request.getHeader("Proxy-Client-IP");
+		}
+		if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
+			ip = request.getHeader("WL-Proxy-Client-IP");
+		}
+		if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
+			ip = request.getHeader("HTTP_CLIENT_IP");
+		}
+		if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
+			ip = request.getHeader("HTTP_X_FORWARDED_FOR");
+		}
+		if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
+			ip = request.getRemoteAddr();
+		}
+		// 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
+		if (ip != null && ip.length() > 15) {
+			if (ip.indexOf(",") > 0) {
+				ip = ip.substring(0, ip.indexOf(","));
+			}
+		}
+		if ("127.0.0.1".equals(ip)) {
+			// 获取本机真正的ip地址
+			try {
+				ip = InetAddress.getLocalHost().getHostAddress();
+			} catch (UnknownHostException e) {
+				log.error(e.getMessage(), e);
+			}
+		}
+		return ip;
+	}
+
+	/**
+	 * 判断是否内网IP
+	 * @param ip ip
+	 * @return boolean
+	 */
+	public static boolean isIntranetIp(String ip) {
+		if (StrUtil.isBlank(ip)) {
+			return false;
+		}
+		Matcher matcher = INTRANET_PATTERN.matcher(ip);
+		return matcher.matches();
+	}
+
+	/**
+	 * 检查IP是否合法
+	 * @param ip ip
+	 * @return boolean
+	 */
+
+	public static boolean isValidIp(String ip) {
+		if (StrUtil.isBlank(ip)) {
+			return false;
+		}
+		Matcher matcher = IP_PATTERN.matcher(ip);
+		return matcher.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;
+	}
+
+	/**
+	 * 获取IP归属地(国家-省份)
+	 * @param ip ip
+	 * @return 国家-省份
+	 */
+	public static String getIpRegion(String ip) {
+		// 处理多IP情况,取第一个
+		if (StrUtil.isNotBlank(ip) && ip.contains(",")) {
+			String[] split = ip.split(",");
+			ip = split[0].trim();
+		}
+
+		// 检查IP合法性
+		if (!isValidIp(ip)) {
+			return "未知";
+		}
+
+		// 检查是否为内网IP
+		if (isIntranetIp(ip)) {
+			return "内网";
+		}
+
+		// 如果搜索器未初始化成功,返回未知
+		if (searcher == null) {
+			log.warn("ip2region搜索器未初始化,无法查询IP归属地");
+			return "未知";
+		}
+
+		try {
+			String region = searcher.search(ip);
+			if (StrUtil.isNotBlank(region)) {
+				List<String> arr = StrUtil.split(region, '|');
+				// 确保数组有足够的元素
+				if (arr.size() >= 3) {
+					String country = "0".equals(arr.get(0)) ? "未知" : arr.get(0);
+					String province = "0".equals(arr.get(2)) ? "未知" : arr.get(2);
+
+					// 处理国外省份可能为0的情况
+					if ("未知".equals(province) && !"中国".equals(country)) {
+						return country;
+					}
+
+					return country + "-" + province;
+				}
+			}
+		} catch (Exception e) {
+			log.warn("查询IP[{}]归属地出错", ip, e);
+		}
+
+		return "未知";
+	}
+
+	/**
+	 * 获取外网IP
+	 * @return ip
+	 */
+	public static String getPublicIp() {
+		String ip = "";
+		StringBuilder inputLine = new StringBuilder();
+		String read;
+		BufferedReader in = null;
+		try {
+			URL url = new URL("http://ip.chinaz.com");
+			HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
+			in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), StandardCharsets.UTF_8));
+			while ((read = in.readLine()) != null) {
+				inputLine.append(read).append("\r\n");
+			}
+		} catch (IOException e) {
+			log.error("获取公网IP失败:{}",e.getMessage());
+		} finally {
+			if (in != null) {
+				try {
+					in.close();
+				} catch (IOException e) {
+					log.error("获取公网IP失败:{}",e.getMessage());
+				}
+			}
+		}
+		Pattern p = Pattern.compile("<dd class=\"fz24\">(.*?)</dd>");
+		Matcher m = p.matcher(inputLine.toString());
+		if (m.find()) {
+			ip = m.group(1);
+		}
+		log.info("获取的公网ip为[{}]", ip);
+		return ip;
+	}
+
+	public static void main(String[] args) {
+		// 测试案例
+		System.out.println(getIpRegion("39.144.24.215"));   // 中国-北京
+		System.out.println(getIpRegion("103.26.94.206"));  // 中国-香港
+		System.out.println(getIpRegion("153.35.52.159"));  // 日本-未知
+		System.out.println(getIpRegion("8.8.8.8"));        // 美国-加利福尼亚州
+		System.out.println(getIpRegion("192.168.1.1"));    // 全球(内网)
+		System.out.println(getIpRegion("127.0.0.1"));      // 全球(内网)
+		System.out.println(getIpRegion("invalid.ip"));     // 未知(非法IP)
+		System.out.println(getIpRegion(""));               // 未知(空IP)
+	}
+}

二进制
pig-common/pig-common-core/src/main/resources/ip/ip2region.xdb


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

@@ -17,13 +17,17 @@
 package com.pig4cloud.pig.common.feign.sentinel.handle;
 
 import com.alibaba.csp.sentinel.Tracer;
+import com.fasterxml.jackson.databind.JsonMappingException;
 import com.pig4cloud.pig.common.core.util.R;
 import jakarta.validation.ConstraintViolation;
 import jakarta.validation.ConstraintViolationException;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.TypeMismatchException;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.context.MessageSourceResolvable;
 import org.springframework.core.annotation.Order;
 import org.springframework.http.HttpStatus;
+import org.springframework.http.converter.HttpMessageNotReadableException;
 import org.springframework.security.access.AccessDeniedException;
 import org.springframework.security.core.SpringSecurityMessageSource;
 import org.springframework.util.Assert;
@@ -31,9 +35,12 @@ 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.MissingServletRequestParameterException;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.ResponseStatus;
 import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.method.annotation.HandlerMethodValidationException;
+import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
 import org.springframework.web.servlet.resource.NoResourceFoundException;
 
 import java.util.List;
@@ -54,6 +61,88 @@ import java.util.Set;
 @ConditionalOnExpression("!'${security.oauth2.client.clientId}'.isEmpty()")
 public class GlobalBizExceptionHandler {
 
+
+	/**
+	 * 处理控制器方法参数校验异常(如@RequestParam/@PathVariable的校验失败)
+	 */
+	@ExceptionHandler(HandlerMethodValidationException.class)
+	@ResponseStatus(HttpStatus.BAD_REQUEST)
+	public R handleHandlerMethodValidationException(HandlerMethodValidationException e) {
+		log.error("控制器方法参数校验失败 ex={}", e.getMessage(), e);
+
+		List<? extends MessageSourceResolvable> errors = e.getAllErrors();
+		if (!errors.isEmpty()) {
+			MessageSourceResolvable firstError = errors.get(0);
+			String errorMsg = firstError.getDefaultMessage();
+			if (errorMsg == null && firstError.getCodes() != null && firstError.getCodes().length > 0) {
+				errorMsg = firstError.getCodes()[0];
+			}
+			return R.failed("参数校验失败:" + (errorMsg != null ? errorMsg : "未知错误,请联系管理员"));
+		}
+
+		// 兜底提示
+		return R.failed("参数校验失败,请检查请求参数");
+	}
+
+
+	/**
+	 * 处理HTTP消息解析异常(优先捕获,包括JSON类型不匹配)
+	 */
+	@ExceptionHandler(HttpMessageNotReadableException.class)
+	@ResponseStatus(HttpStatus.BAD_REQUEST)
+	public R handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
+		log.error("HTTP消息解析异常 ex={}", e.getMessage(), e);
+
+		// 检查根原因是否为JSON映射异常(包含字段信息)
+		Throwable rootCause = e.getRootCause();
+		if (rootCause instanceof JsonMappingException jsonMappingEx) {
+			// 提取出错的字段路径
+			String fieldPath = jsonMappingEx.getPath().stream()
+					.map(JsonMappingException.Reference::getFieldName)
+					.reduce((a, b) -> a + "." + b)
+					.orElse("");
+
+			return R.failed("参数类型错误:字段 [" + fieldPath + "] 类型不匹配,请检查");
+		}
+
+		// 其他解析错误(如JSON格式错误)
+		return R.failed("请求参数格式错误,请检查JSON格式是否正确");
+	}
+
+	/**
+	 * 处理类型转换相关的异常(表单/路径参数)
+	 * 包括:方法参数类型不匹配、绑定参数类型错误等
+	 */
+	@ExceptionHandler({
+			MethodArgumentTypeMismatchException.class,
+			TypeMismatchException.class
+	})
+	@ResponseStatus(HttpStatus.BAD_REQUEST)
+	public R handleTypeMismatchException(Exception e) {
+		log.error("参数类型错误 ex={}", e.getMessage(), e);
+
+		// 提取具体字段名
+		String fieldName = "";
+		if (e instanceof MethodArgumentTypeMismatchException methodEx) {
+			fieldName = methodEx.getName();
+		} else if (e instanceof TypeMismatchException typeEx && typeEx.getPropertyName() != null) {
+			fieldName = typeEx.getPropertyName();
+		}
+
+		return R.failed("参数类型错误:字段 [" + fieldName + "] 类型不匹配,请检查");
+	}
+
+	/**
+	 * 处理缺少参数的异常
+	 */
+	@ExceptionHandler(MissingServletRequestParameterException.class)
+	@ResponseStatus(HttpStatus.BAD_REQUEST)
+	public R handleMissingParamException(MissingServletRequestParameterException e) {
+		log.error("缺少请求参数 ex={}", e.getMessage(), e);
+		return R.failed("缺少必要参数:[" + e.getParameterName() + "],类型:" + e.getParameterType());
+	}
+
+
 	/**
 	 * 全局异常.
 	 * @param e the e
@@ -137,9 +226,26 @@ public class GlobalBizExceptionHandler {
 	@ExceptionHandler({ BindException.class })
 	@ResponseStatus(HttpStatus.BAD_REQUEST)
 	public R bindExceptionHandler(BindException exception) {
+		// 优先获取字段错误信息
 		List<FieldError> fieldErrors = exception.getBindingResult().getFieldErrors();
-		log.warn("参数绑定异常,ex = {}", fieldErrors.get(0).getDefaultMessage());
-		return R.failed(fieldErrors.get(0).getDefaultMessage());
+		if (!fieldErrors.isEmpty()) {
+			String errorMsg = fieldErrors.get(0).getDefaultMessage();
+			log.warn("参数绑定异常,ex = {}", errorMsg);
+			return R.failed("参数类型错误:" + errorMsg); // 明确提示类型错误
+		}
+
+		// 处理全局错误
+		List<ObjectError> globalErrors = exception.getBindingResult().getGlobalErrors();
+		if (!globalErrors.isEmpty()) {
+			String errorMsg = globalErrors.get(0).getDefaultMessage();
+			log.warn("参数绑定全局异常,ex = {}", errorMsg);
+			return R.failed(errorMsg);
+		}
+		// 兜底提示
+		return R.failed("参数绑定错误,请检查参数类型");
+//		List<FieldError> fieldErrors = exception.getBindingResult().getFieldErrors();
+//		log.warn("参数绑定异常,ex = {}", fieldErrors.get(0).getDefaultMessage());
+//		return R.failed(fieldErrors.get(0).getDefaultMessage());
 	}
 
 	/**

+ 17 - 0
pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/MarketingConfigConstants.java

@@ -0,0 +1,17 @@
+package com.pig4cloud.pig.marketing.api;
+
+
+/**
+ * @author: lwh
+ * @date: 2025-07-31
+ * @description: 营销配置常量类
+ */
+
+public interface MarketingConfigConstants {
+
+	String TRIGGER_MODE = "TRIGGER_MODE";
+	String TRIGGER_RULE = "TRIGGER_RULE";
+	String TRIGGER_NUM = "TRIGGER_NUM";
+	String PROMPT_MSG = "PROMPT_MSG";
+	String URL = "URL";
+}

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

@@ -23,7 +23,7 @@ public class DomainValidationUtil {
 			return false;
 		}
 		String trimDomain = domain.trim();
-		// 长度限制(1-255字符)
+		// 长度限制
 		if (trimDomain.length() < 1 || trimDomain.length() > 255) {
 			return false;
 		}

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

@@ -1,50 +0,0 @@
-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;
-	}
-}

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

@@ -31,20 +31,20 @@ public class DomainValidator implements ConstraintValidator<DomainValidation, Ob
 				setMessage(context, "来源为分组时,分组ID不能为空");
 				return false;
 			}
-			// 分组模式下不能有域名相关字段
-			if (notEmpty(domain)) {
-				setMessage(context, "来源为分组时,不能填写域名");
-				return false;
-			}
+//			// 分组模式下不能有域名相关字段
+//			if (notEmpty(domain)) {
+//				setMessage(context, "来源为分组时,不能填写域名");
+//				return false;
+//			}
 			return true;
 		}
 
 		// 来源类型为具体域名(2)时的校验
 		if (sourceType == 2) {
-			if (groupId != null) {
-				setMessage(context, "来源为具体域名时,分组ID必须为空");
-				return false;
-			}
+//			if (groupId != null) {
+//				setMessage(context, "来源为具体域名时,分组ID必须为空");
+//				return false;
+//			}
 			if (!notEmpty(domain)) {
 				setMessage(context, "来源为具体域名时,域名不能为空");
 				return false;

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

@@ -1,8 +1,8 @@
 package com.pig4cloud.pig.marketing.api.valid;
 
 
+import com.pig4cloud.pig.common.core.util.ip.IPUtils;
 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;
 
@@ -32,21 +32,21 @@ public class IPValidator implements ConstraintValidator<IPValidation, Object> {
 				setMessage(context, "来源为分组时,分组ID不能为空");
 				return false;
 			}
-			// 分组模式下不能有IP相关字段
-			if (ipMode != null || notEmpty(startIp) || notEmpty(endIp)) {
-				setMessage(context, "来源为分组时,不能填写IP模式、IP地址");
-				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模式下不能有groupId
+//			if (groupId != null) {
+//				setMessage(context, "来源为具体IP时,分组ID必须为空");
+//				return false;
+//			}
 			// 必须指定IP模式
 			if (ipMode == null) {
 				setMessage(context, "来源为具体IP时,IP模式不能为空");
@@ -59,12 +59,12 @@ public class IPValidator implements ConstraintValidator<IPValidation, Object> {
 					setMessage(context, "单IP模式下,startIp不能为空");
 					return false;
 				}
-				if (notEmpty(endIp)) {
-					setMessage(context, "单IP模式下,endIp必须为空");
-					return false;
-				}
+//				if (notEmpty(endIp)) {
+//					setMessage(context, "单IP模式下,endIp必须为空");
+//					return false;
+//				}
 				// 校验IP格式
-				if (!IpValidationUtil.isValidIp(startIp)) {
+				if (!IPUtils.isValidIp(startIp)) {
 					setMessage(context, "IP格式不合法(IPv4)");
 					return false;
 				}
@@ -78,16 +78,16 @@ public class IPValidator implements ConstraintValidator<IPValidation, Object> {
 					return false;
 				}
 				// 校验IP格式
-				if (!IpValidationUtil.isValidIp(startIp)) {
+				if (!IPUtils.isValidIp(startIp)) {
 					setMessage(context, "startIp格式不合法(IPv4)");
 					return false;
 				}
-				if (!IpValidationUtil.isValidIp(endIp)) {
+				if (!IPUtils.isValidIp(endIp)) {
 					setMessage(context, "endIp格式不合法(IPv4)");
 					return false;
 				}
 				// 校验endIp > startIp
-				if (!IpValidationUtil.isEndIpGreater(startIp, endIp)) {
+				if (!IPUtils.isEndIpGreater(startIp, endIp)) {
 					setMessage(context, "IP段模式下,endIp必须大于startIp");
 					return false;
 				}

+ 3 - 2
pig-marketing/pig-marketing-biz/src/main/java/com/pig4cloud/pig/marketing/controller/MarketingAppsController.java

@@ -141,7 +141,7 @@ public class MarketingAppsController {
 	 */
 	@GetMapping("/stat/page")
 	@Operation(summary = "分页统计应用一级营销数据")
-	public R pageStatAppFirstMktData(@ParameterObject PageStatAppMktDataDTO reqDto) {
+	public R pageStatAppFirstMktData(@Valid @ParameterObject PageStatAppMktDataDTO reqDto) {
 		return R.ok(marketingAppsService.pageStatAppFirstMktData(reqDto));
 	}
 
@@ -152,7 +152,8 @@ public class MarketingAppsController {
 	 */
 	@GetMapping("/stat/second/page")
 	@Operation(summary = "分页统计应用二级营销数据")
-	public R pageStatAppSecondMktData(@ParameterObject PageStatAppMktDataDTO reqDto,
+	public R pageStatAppSecondMktData(@Valid @ParameterObject PageStatAppMktDataDTO reqDto,
+									  @Valid
 									  @ParameterObject
 									  @NotBlank(message = "ip不能为空")
 									  @Schema(description = "ip") String ip) {

+ 12 - 0
pig-marketing/pig-marketing-biz/src/main/java/com/pig4cloud/pig/marketing/service/MarketingAppsService.java

@@ -87,4 +87,16 @@ public interface MarketingAppsService {
 	 * @return 二级营销数据
 	 */
 	Page pageStatAppSecondMktData(PageStatAppMktDataDTO reqDto, String ip);
+
+	/**
+	 * 校验IP冲突
+	 * @return Boolean
+	 */
+	Boolean checkIp(MarketingAppsIpDTO ipDto, Long appId, Boolean isConfig);
+
+	/**
+	 * 校验域名冲突
+	 * @return Boolean
+	 */
+	Boolean checkDomain(MarketingAppsDomainDTO domainDTO, Long appId, Boolean isConfig);
 }

+ 91 - 23
pig-marketing/pig-marketing-biz/src/main/java/com/pig4cloud/pig/marketing/service/impl/MarketingAppsServiceImpl.java

@@ -8,12 +8,14 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+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.common.core.util.ip.IPUtils;
 import com.pig4cloud.pig.marketing.api.dto.app.*;
 import com.pig4cloud.pig.marketing.api.entity.*;
 import com.pig4cloud.pig.marketing.api.vo.app.*;
-import com.pig4cloud.pig.marketing.api.vo.config.GetMarketingGlobalConfigVO;
 import com.pig4cloud.pig.marketing.mapper.*;
 import com.pig4cloud.pig.marketing.service.MarketingAppsService;
 import com.pig4cloud.pig.marketing.service.MarketingConfigService;
@@ -27,8 +29,11 @@ import org.springframework.transaction.annotation.Transactional;
 import java.io.IOException;
 import java.time.LocalDateTime;
 import java.util.*;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.stream.Collectors;
 
+import static com.pig4cloud.pig.marketing.api.MarketingConfigConstants.*;
+
 /**
  * @author: lwh
  * @date: 2025-07-22
@@ -60,7 +65,8 @@ public class MarketingAppsServiceImpl implements MarketingAppsService {
 
 	private final MarketingGroupIpMapper groupIpMapper;
 
-	private final MarketingConfigService marketingConfigService;
+	private final RemoteParamService remoteParamService;
+
 
 
 
@@ -267,20 +273,30 @@ public class MarketingAppsServiceImpl implements MarketingAppsService {
 		Map<String, MarketingApps> localAppMap = localApps.stream()
 				.collect(Collectors.toMap(MarketingApps::getAppId, app -> app));
 
-		// 4. 处理新增和更新
+		// 4. 获取全局配置
+		AtomicReference<Integer> triggerRule = new AtomicReference<>();
+		AtomicReference<String> triggerNum = new AtomicReference<>("");
+		R<ArrayList<SysPublicParamDTO>> ParamDTO = remoteParamService.getParamList(Arrays.asList(TRIGGER_RULE, TRIGGER_NUM));
+
+		ParamDTO.getData().forEach(param -> {
+			if (TRIGGER_RULE.equals(param.getPublicKey())) {
+				triggerRule.set(Integer.valueOf(param.getPublicValue()));
+			} else if (TRIGGER_NUM.equals(param.getPublicKey())) {
+				triggerNum.set(param.getPublicValue());
+			}
+		});
+
+		// 5. 处理新增和更新
 		for (MarketingApps app : appList) {
 			MarketingApps localApp = localAppMap.get(app.getAppId());
 
 			if (localApp == null) {
-				// 获取全局配置信息
-				GetMarketingGlobalConfigVO globalConfig = marketingConfigService.getMarketingGlobalConfig();
-
 				// 4.1 本地不存在,新增
 				app.setStatus( true);
 				app.setDomainLimit(false);
 				app.setLaunch( true);
-				app.setTriggerRule(globalConfig.getTriggerRule());
-				app.setTriggerNum(globalConfig.getTriggerNum());
+				app.setTriggerRule(triggerRule.get());
+				app.setTriggerNum(triggerNum.get());
 				appsMapper.insert(app);
 				addCount++;
 			} else {
@@ -383,6 +399,12 @@ public class MarketingAppsServiceImpl implements MarketingAppsService {
 			default -> null;
 		};
 		List<PageStatAppFirstMktDataVO> records = appsMapper.statAppFirstMktData(page, reqDto.getAppId(), startTime);
+
+		for (PageStatAppFirstMktDataVO record : records) {
+			String ip = record.getIp();
+			String region = IPUtils.getIpRegion(ip);
+			record.setRegion(region);
+		}
 		page.setRecords(records);
 		return page;
 	}
@@ -493,13 +515,35 @@ public class MarketingAppsServiceImpl implements MarketingAppsService {
 				if (ipDto.getId() != null && (ipDto.getModify()==null || !ipDto.getModify())){
 					continue;
 				}
+				// 去除无用数据
+				if (ipDto.getSourceType() == 1){
+					ipDto.setIpMode(null);
+					ipDto.setStartIp(null);
+					ipDto.setEndIp( null);
+
+					// 查询分组是否存在
+					MarketingConfigGroup group = groupMapper.selectOne(Wrappers.<MarketingConfigGroup>lambdaQuery()
+							.eq(MarketingConfigGroup::getId, ipDto.getGroupId())
+							.eq(MarketingConfigGroup::getGroupType, 1)
+					);
+					if (group == null){
+						throw new BusinessException("分组ID:"+ipDto.getGroupId()+"不存在");
+					}
+				}else {
+					ipDto.setGroupId(null);
+					ipDto.setGroupName(null);
+					if (ipDto.getIpMode() == 1){
+						ipDto.setEndIp(null);
+					}
+				}
 
 				// 查询ip表和ip分组表中是否存在
-				if (checkIp(ipDto, appId)){
+				if (checkIp(ipDto, appId, false)){
 					if (ipDto.getIpMode() == 1){
 						throw new BusinessException("ip:"+ipDto.getStartIp()+"已存在");
 					}
-					throw new BusinessException("ip:"+ipDto.getStartIp()+"/"+ipDto.getEndIp()+"已存在");
+					String[] end = ipDto.getEndIp().split("\\.");
+					throw new BusinessException("ip:"+ipDto.getStartIp()+"/"+end[3]+"已存在");
 				}
 
 				// 执行新增或修改
@@ -533,7 +577,24 @@ public class MarketingAppsServiceImpl implements MarketingAppsService {
 					continue;
 				}
 
-				if (checkDomain(domainDto, appId)){
+				// 去除无用数据
+				if (domainDto.getSourceType() == 1){
+					domainDto.setDomain(null);
+
+					// 查询分组是否存在
+					MarketingConfigGroup group = groupMapper.selectOne(Wrappers.<MarketingConfigGroup>lambdaQuery()
+							.eq(MarketingConfigGroup::getId, domainDto.getGroupId())
+							.eq(MarketingConfigGroup::getGroupType, 2)
+					);
+					if (group == null){
+						throw new BusinessException("分组ID:"+domainDto.getGroupId()+"不存在");
+					}
+				}else {
+					domainDto.setGroupId(null);
+					domainDto.setGroupName(null);
+				}
+
+				if (checkDomain(domainDto, appId, false)){
 					throw new BusinessException("域名:"+domainDto.getDomain()+"已存在");
 				}
 
@@ -558,13 +619,14 @@ public class MarketingAppsServiceImpl implements MarketingAppsService {
 	 * @param ipDto IP
 	 * @return 是否存在
 	 */
-	public boolean checkIp(MarketingAppsIpDTO ipDto, Long appId) {
-		List<Long> groupIds = getAppRelatedGroupIds(appId, true);
+	@Override
+	public Boolean checkIp(MarketingAppsIpDTO ipDto, Long appId, Boolean isConfig) {
+		List<Long> groupIds = getAppRelatedGroupIds(appId, true, isConfig);
 		if (ipDto.getSourceType().equals(1)){
 			return groupIds.contains(ipDto.getGroupId());
 		}
 		// 1. 检查应用IP表
-		if (checkAppsIpExists(ipDto, appId)) {
+		if (checkAppsIpExists(ipDto, appId, isConfig)) {
 			return true;
 		}
 
@@ -578,8 +640,8 @@ public class MarketingAppsServiceImpl implements MarketingAppsService {
 	 * @param appId 应用ID
 	 * @return 是否存在
 	 */
-	public Boolean checkDomain(MarketingAppsDomainDTO domainDTO, Long appId) {
-		List<Long> groupIds = getAppRelatedGroupIds(appId, false);  // 需要实现获取应用关联的分组ID列表
+	public Boolean checkDomain(MarketingAppsDomainDTO domainDTO, Long appId, Boolean isConfig) {
+		List<Long> groupIds = getAppRelatedGroupIds(appId, false, isConfig);  // 需要实现获取应用关联的分组ID列表
 		if (domainDTO.getSourceType().equals(1)){
 			// 来自分组
 			return groupIds.contains(domainDTO.getGroupId());
@@ -587,9 +649,10 @@ public class MarketingAppsServiceImpl implements MarketingAppsService {
 
 		// 1. 检查应用域名表
 		LambdaQueryWrapper<MarketingAppsDomain> appsQuery = new LambdaQueryWrapper<>();
-		appsQuery.eq(MarketingAppsDomain::getAppId, appId)
+		appsQuery.eq(appId != null, MarketingAppsDomain::getAppId, appId)
 				.eq(MarketingAppsDomain::getSourceType, 2)
-				.eq(MarketingAppsDomain::getDomain, domainDTO.getDomain());
+				.eq(MarketingAppsDomain::getDomain, domainDTO.getDomain())
+				.eq(MarketingAppsDomain::getConfig, isConfig);
 		if (appsDomainMapper.selectCount(appsQuery) > 0) {
 			return true;
 		}
@@ -607,12 +670,15 @@ public class MarketingAppsServiceImpl implements MarketingAppsService {
 	/**
 	 * 检查应用IP表中是否存在冲突
 	 */
-	private Boolean checkAppsIpExists(MarketingAppsIpDTO ipDto,Long appId){
+	private Boolean checkAppsIpExists(MarketingAppsIpDTO ipDto,Long appId, Boolean isConfig){
 		// 1、校验该appId应用下ip表,及ip分组表中,是否存在该ip
 		// 1.1、校验marketing_apps_ip表,appId=appId,是否包含
 		// 1.2、校验marketing_group_ip表,是否包含ip,如果包含查出分组id,在查看appId下是否包含这个分组
 		QueryWrapper<MarketingAppsIp> query = new QueryWrapper<>();
-		query.eq("app_id", appId).eq("source_type",2);
+		query.eq("source_type",2).eq("config", isConfig?1:0);
+		if (appId != null){
+			query.eq("app_id", appId);
+		}
 
 		// 单IP模式检查
 		if (ipDto.getIpMode() == 1) {
@@ -701,11 +767,12 @@ public class MarketingAppsServiceImpl implements MarketingAppsService {
 	/**
 	 * 获取应用关联的所有分组ID
 	 */
-	private List<Long> getAppRelatedGroupIds(Long appId, Boolean isIp) {
+	private List<Long> getAppRelatedGroupIds(Long appId, Boolean isIp, Boolean isConfig) {
 		// 示例实现,实际需根据业务调整
 		if (isIp) {
 			LambdaQueryWrapper<MarketingAppsIp> query = new LambdaQueryWrapper<>();
-			query.eq(MarketingAppsIp::getAppId, appId)
+			query.eq(appId!=null ,MarketingAppsIp::getAppId, appId)
+					.eq(MarketingAppsIp::getConfig, isConfig)
 					.eq(MarketingAppsIp::getSourceType, 1)  // 来源类型为分组
 					.select(MarketingAppsIp::getGroupId)
 					.groupBy(MarketingAppsIp::getGroupId);
@@ -714,7 +781,8 @@ public class MarketingAppsServiceImpl implements MarketingAppsService {
 					.toList();
 		}else {
 			LambdaQueryWrapper<MarketingAppsDomain> query = new LambdaQueryWrapper<>();
-			query.eq(MarketingAppsDomain::getAppId, appId)
+			query.eq(appId!=null, MarketingAppsDomain::getAppId, appId)
+					.eq(MarketingAppsDomain::getConfig, isConfig)
 					.eq(MarketingAppsDomain::getSourceType, 1)  // 来源类型为分组
 					.select(MarketingAppsDomain::getGroupId)
 					.groupBy(MarketingAppsDomain::getGroupId);

+ 87 - 20
pig-marketing/pig-marketing-biz/src/main/java/com/pig4cloud/pig/marketing/service/impl/MarketingConfigServiceImpl.java

@@ -7,23 +7,29 @@ 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.common.core.util.ip.IPUtils;
+import com.pig4cloud.pig.marketing.api.dto.app.MarketingAppsDomainDTO;
 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.util.DomainValidationUtil;
-import com.pig4cloud.pig.marketing.api.util.IpValidationUtil;
 import com.pig4cloud.pig.marketing.api.vo.config.*;
 import com.pig4cloud.pig.marketing.mapper.*;
+import com.pig4cloud.pig.marketing.service.MarketingAppsService;
 import com.pig4cloud.pig.marketing.service.MarketingConfigService;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
 import java.util.*;
 
+import static com.pig4cloud.pig.marketing.api.MarketingConfigConstants.*;
+
 
 /**
  * @author: lwh
@@ -46,6 +52,8 @@ public class MarketingConfigServiceImpl implements MarketingConfigService {
 
 	private final MarketingAppsDomainMapper domainMapper;
 
+	private  final MarketingAppsService appsService;
+
 	private final RemoteParamService remoteParamService;
 
 	/**
@@ -164,7 +172,17 @@ public class MarketingConfigServiceImpl implements MarketingConfigService {
 	@Override
 	@Transactional
 	public R modMarketingGroupDomain(ModMarketingGroupDomainDTO reqDto) {
-		// 先查询域名分组是否存在
+		//先查询分组是否存在
+		MarketingConfigGroup group = groupMapper.selectOne(Wrappers.<MarketingConfigGroup>lambdaQuery()
+				.eq(MarketingConfigGroup::getId, reqDto.getId())
+				.eq(MarketingConfigGroup::getGroupType, 2)
+		);
+
+		if (group == null){
+			throw new BusinessException("分组id:"+reqDto.getId()+",分组不存在");
+		}
+
+		// 在查询域名是否重复
 		MarketingConfigGroup groupExist = groupMapper.selectOne(Wrappers.<MarketingConfigGroup>lambdaQuery()
 				.eq(MarketingConfigGroup::getGroupType, "2")
 				.eq(MarketingConfigGroup::getGroupName, reqDto.getGroupName()));
@@ -172,7 +190,6 @@ public class MarketingConfigServiceImpl implements MarketingConfigService {
 			return R.failed("分组名已存在");
 		}
 		// 更新分组信息
-		MarketingConfigGroup group = groupMapper.selectById(reqDto.getId());
 		group.setGroupName(reqDto.getGroupName());
 		group.setRemark(reqDto.getRemark());
 		groupMapper.updateById(group);
@@ -249,6 +266,16 @@ public class MarketingConfigServiceImpl implements MarketingConfigService {
 	 */
 	@Override
 	public R modMarketingGroupIp(ModMarketingGroupIPDTO reqDto) {
+		//先查询分组是否存在
+		MarketingConfigGroup group = groupMapper.selectOne(Wrappers.<MarketingConfigGroup>lambdaQuery()
+				.eq(MarketingConfigGroup::getId, reqDto.getId())
+				.eq(MarketingConfigGroup::getGroupType, 1)
+		);
+
+		if (group == null){
+			throw new BusinessException("分组id:"+reqDto.getId()+",分组不存在");
+		}
+
 		MarketingConfigGroup groupExist = groupMapper.selectOne(Wrappers.<MarketingConfigGroup>lambdaQuery()
 				.eq(MarketingConfigGroup::getGroupType, "1")
 				.eq(MarketingConfigGroup::getGroupName, reqDto.getGroupName())
@@ -258,7 +285,6 @@ public class MarketingConfigServiceImpl implements MarketingConfigService {
 		}
 
 		// 更新分组信息
-		 MarketingConfigGroup group = groupMapper.selectById(reqDto.getId());
 		 group.setGroupName(reqDto.getGroupName());
 		 group.setRemark(reqDto.getRemark());
 		 groupMapper.updateById(group);
@@ -279,14 +305,14 @@ public class MarketingConfigServiceImpl implements MarketingConfigService {
 			}
 
 			// ip格式校验
-			if (!IpValidationUtil.isValidIp(ipDto.getStartIp())){
+			if (!IPUtils.isValidIp(ipDto.getStartIp())){
 				throw new BusinessException("IP格式不合法(IPv4)");
 			}
 			if (ipDto.getIpMode() == 2){
-				if (!IpValidationUtil.isValidIp(ipDto.getEndIp())){
+				if (!IPUtils.isValidIp(ipDto.getEndIp())){
 					throw new BusinessException("结束IP格式不合法(IPv4)");
 				}
-				if (!IpValidationUtil.isEndIpGreater(ipDto.getStartIp(), ipDto.getEndIp())){
+				if (!IPUtils.isEndIpGreater(ipDto.getStartIp(), ipDto.getEndIp())){
 					throw new BusinessException("结束IP不能小于开始IP");
 				}
 			}
@@ -372,13 +398,41 @@ public class MarketingConfigServiceImpl implements MarketingConfigService {
 	 */
 	@Override
 	public Boolean addMarketingConfigIpList(AddMarketingConfigIpListDTO reqDto) {
-		// 判断IP表中是否重复
-		// 判断分组中是否重复
-		MarketingAppsIpDTO ipDTO = new MarketingAppsIpDTO();
-		BeanUtils.copyProperties(reqDto, ipDTO);
 
-//		checkIp(ipDTO);
+		// 去除无用数据
+		if (reqDto.getSourceType() == 1){
+			reqDto.setIpMode(null);
+			reqDto.setStartIp(null);
+			reqDto.setEndIp( null);
 
+			// 查询分组是否存在
+			MarketingConfigGroup group = groupMapper.selectOne(Wrappers.<MarketingConfigGroup>lambdaQuery()
+					.eq(MarketingConfigGroup::getId, reqDto.getGroupId())
+					.eq(MarketingConfigGroup::getGroupType, 1)
+			);
+			if (group == null){
+				throw new BusinessException("分组ID:"+reqDto.getGroupId()+"不存在");
+			}
+		}else {
+			reqDto.setGroupId(null);
+			reqDto.setGroupName(null);
+			if (reqDto.getIpMode() == 1){
+				reqDto.setEndIp(null);
+			}
+		}
+		// 校验IP是否存在
+		MarketingAppsIpDTO ipDTO = new MarketingAppsIpDTO();
+		BeanUtils.copyProperties(reqDto, ipDTO);
+		if (appsService.checkIp(ipDTO,null, true)){
+			if (reqDto.getSourceType() ==1){
+				throw new BusinessException("分组ID:"+reqDto.getGroupId()+"已存在");
+			}
+			if (ipDTO.getIpMode() == 1) {
+				throw new BusinessException("ip:" + ipDTO.getStartIp() + "已存在");
+			}
+			String[] end = ipDTO.getEndIp().split("\\.");
+			throw new BusinessException("ip:" + ipDTO.getStartIp() + "/" + end[3] + "已存在");
+		}
 		MarketingAppsIp marketingAppsIp = new MarketingAppsIp();
 		BeanUtils.copyProperties(reqDto, marketingAppsIp);
 		marketingAppsIp.setConfig(true);
@@ -432,8 +486,27 @@ public class MarketingConfigServiceImpl implements MarketingConfigService {
 	 */
 	@Override
 	public Boolean addMarketingConfigDomainList(AddMarketingConfigDomainListDTO reqDto) {
-		// 判断域名表中是否重复
-		// 判断分组中是否重复
+
+		if (reqDto.getSourceType() == 1){
+			reqDto.setDomain(null);
+			// 查询分组是否存在
+			MarketingConfigGroup group = groupMapper.selectOne(Wrappers.<MarketingConfigGroup>lambdaQuery()
+					.eq(MarketingConfigGroup::getId, reqDto.getGroupId())
+					.eq(MarketingConfigGroup::getGroupType, 2)
+			);
+			if (group == null){
+				throw new BusinessException("分组ID:"+reqDto.getGroupId()+"不存在");
+			}
+		}else {
+			reqDto.setGroupId(null);
+			reqDto.setGroupName(null);
+		}
+		// 校验域名是否存在
+		MarketingAppsDomainDTO domainDTO = new MarketingAppsDomainDTO();
+		BeanUtils.copyProperties(reqDto, domainDTO);
+		if (appsService.checkDomain(domainDTO, null, true)){
+			throw new BusinessException("域名:"+reqDto.getDomain()+"已存在");
+		}
 
 		MarketingAppsDomain domain = new MarketingAppsDomain();
 		BeanUtils.copyProperties(reqDto, domain);
@@ -460,12 +533,6 @@ public class MarketingConfigServiceImpl implements MarketingConfigService {
 		return delete > 0;
 	}
 
-	private static final String TRIGGER_MODE = "TRIGGER_MODE";
-	private static final String TRIGGER_RULE = "TRIGGER_RULE";
-	private static final String TRIGGER_NUM = "TRIGGER_NUM";
-	private static final String PROMPT_MSG = "PROMPT_MSG";
-	private static final String URL = "URL";
-
 	/**
 	 * 查询全局配置信息
 	 * @return 全局配置信息

+ 64 - 81
pig-marketing/pig-marketing-biz/src/main/java/com/pig4cloud/pig/marketing/service/impl/MarketingDataServiceImpl.java

@@ -4,6 +4,7 @@ package com.pig4cloud.pig.marketing.service.impl;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.pig4cloud.pig.common.core.util.ip.IPUtils;
 import com.pig4cloud.pig.marketing.api.dto.data.MarketingDataReportDTO;
 import com.pig4cloud.pig.marketing.api.dto.data.PageFirstLevelDataDTO;
 import com.pig4cloud.pig.marketing.api.dto.data.PageSecondLevelDataDTO;
@@ -76,7 +77,7 @@ public class MarketingDataServiceImpl implements MarketingDataService {
 		result.setType("link");
 
 		// 获取客户端IP地址
-		String ip = getIp(request);
+		String ip = IPUtils.getIpAddress(request);
 		// 获取域名
 		String domain = reqDto.getDomain();
 		// 应用匹配
@@ -102,6 +103,68 @@ public class MarketingDataServiceImpl implements MarketingDataService {
 		return result;
 	}
 
+	/**
+	 * 分页统计一级营销数据
+	 * @param reqDto 分页参数
+	 * @return Page
+	 */
+	@Override
+	public Page pageFirstLevelData(PageFirstLevelDataDTO reqDto) {
+		String ip = reqDto.getIp();
+		// 计算时间条件
+		LocalDateTime now = LocalDateTime.now();
+		LocalDateTime lastHourTime = now.minusHours(1);
+		LocalDateTime todayStartTime = now.withHour(0).withMinute(0).withSecond(0).withNano(0);
+
+		// 格式化时间
+		DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+		String lastHourTimeStr = lastHourTime.format(formatter);
+		String todayStartTimeStr = todayStartTime.format(formatter);
+
+		// 计算偏移量
+		long offset = (reqDto.getCurrent() - 1) * reqDto.getSize();
+
+		// 查询数据
+		Page<PageFirstLevelDataVO> page = new Page<>(reqDto.getCurrent(), reqDto.getSize());
+
+		List<PageFirstLevelDataVO> rs = marketingDataMapper.countFirstLevelMarketingData(ip, reqDto.getAppId(), lastHourTimeStr, todayStartTimeStr, offset, reqDto.getSize());
+		page.setRecords(rs);
+		page.setTotal(marketingDataMapper.countFirstLevelDataTotal(ip, reqDto.getAppId()));
+
+		return page;
+	}
+
+	/**
+	 * 分页统计二级营销数据
+	 * @param reqDto 分页参数
+	 * @return Page
+	 */
+	@Override
+	public Page pageSecondLevelData(PageSecondLevelDataDTO reqDto) {
+		String ip = reqDto.getIp();
+
+		// 计算时间条件
+		LocalDateTime now = LocalDateTime.now();
+		LocalDateTime lastHourTime = now.minusHours(1);
+		LocalDateTime todayStartTime = now.withHour(0).withMinute(0).withSecond(0).withNano(0);
+
+		// 格式化时间
+		DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+		String lastHourTimeStr = lastHourTime.format(formatter);
+		String todayStartTimeStr = todayStartTime.format(formatter);
+
+		// 计算偏移量
+		long offset = (reqDto.getCurrent() - 1) * reqDto.getSize();
+
+		// 查询数据
+		Page<PageSecondLevelDataVO> page = new Page<>(reqDto.getCurrent(), reqDto.getSize());
+		page.setRecords(marketingDataMapper.countSecondLevelMarketingData(ip, lastHourTimeStr, todayStartTimeStr, offset, reqDto.getSize()));
+		page.setTotal(marketingDataMapper.countSecondLevelDataTotal(ip));
+
+		return page;
+	}
+
+
 	/**
 	 * 获取匹配的app
 	 * @param ip ip
@@ -322,84 +385,4 @@ public class MarketingDataServiceImpl implements MarketingDataService {
 		return result & 0xFFFFFFFFL;
 	}
 
-	@Override
-	public Page pageFirstLevelData(PageFirstLevelDataDTO reqDto) {
-		String ip = reqDto.getIp();
-		// 计算时间条件
-		LocalDateTime now = LocalDateTime.now();
-		LocalDateTime lastHourTime = now.minusHours(1);
-		LocalDateTime todayStartTime = now.withHour(0).withMinute(0).withSecond(0).withNano(0);
-
-		// 格式化时间
-		DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
-		String lastHourTimeStr = lastHourTime.format(formatter);
-		String todayStartTimeStr = todayStartTime.format(formatter);
-
-		// 计算偏移量
-		long offset = (reqDto.getCurrent() - 1) * reqDto.getSize();
-
-		// 查询数据
-		Page<PageFirstLevelDataVO> page = new Page<>(reqDto.getCurrent(), reqDto.getSize());
-
-		List<PageFirstLevelDataVO> rs = marketingDataMapper.countFirstLevelMarketingData(ip, reqDto.getAppId(), lastHourTimeStr, todayStartTimeStr, offset, reqDto.getSize());
-		page.setRecords(rs);
-		page.setTotal(marketingDataMapper.countFirstLevelDataTotal(ip, reqDto.getAppId()));
-
-		return page;
-	}
-
-	@Override
-	public Page pageSecondLevelData(PageSecondLevelDataDTO reqDto) {
-		String ip = reqDto.getIp();
-
-		// 计算时间条件
-		LocalDateTime now = LocalDateTime.now();
-		LocalDateTime lastHourTime = now.minusHours(1);
-		LocalDateTime todayStartTime = now.withHour(0).withMinute(0).withSecond(0).withNano(0);
-
-		// 格式化时间
-		DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
-		String lastHourTimeStr = lastHourTime.format(formatter);
-		String todayStartTimeStr = todayStartTime.format(formatter);
-
-		// 计算偏移量
-		long offset = (reqDto.getCurrent() - 1) * reqDto.getSize();
-
-		// 查询数据
-		Page<PageSecondLevelDataVO> page = new Page<>(reqDto.getCurrent(), reqDto.getSize());
-		page.setRecords(marketingDataMapper.countSecondLevelMarketingData(ip, lastHourTimeStr, todayStartTimeStr, offset, reqDto.getSize()));
-		page.setTotal(marketingDataMapper.countSecondLevelDataTotal(ip));
-
-		return page;
-	}
-
-	/**
-	 * 获取ip地址
-	 */
-	public static String getIp(HttpServletRequest request) {
-		String ip = request.getHeader("X-Forwarded-For");
-		if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
-			ip = request.getHeader("Proxy-Client-IP");
-		}
-		if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
-			ip = request.getHeader("WL-Proxy-Client-IP");
-		}
-		if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
-			ip = request.getRemoteAddr();
-		}
-		String comma = ",";
-		String localhost = "127.0.0.1";
-		if (ip.contains(comma)) {
-			ip = ip.split(",")[0];
-		}
-		if (localhost.equals(ip)) {
-			// 获取本机真正的ip地址
-			try {
-				ip = InetAddress.getLocalHost().getHostAddress();
-			} catch (UnknownHostException e) {
-				log.error(e.getMessage(), e);
-			}
-		}
-		return ip;
-	}
 }