Browse Source

Merge remote-tracking branch 'refs/remotes/origin/dev/lwh' into dev/wcl

wangcl 1 week ago
parent
commit
0c42288d37

+ 1 - 1
pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/dto/rule/SaveMktRuleDTO.java

@@ -50,7 +50,7 @@ public class SaveMktRuleDTO implements Serializable {
 
 	@Schema(description = "推送方式,1-弹出提示,2-打开网址,3-内置浏览器打开网址,4-使用webview打开网址",example = "1")
 	@NotNull(message = "推送方式不能为空")
-	private Integer action;
+	private String action;
 
 	@Schema(description = "备注")
 	private String remark;

+ 1 - 1
pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/entity/MktMgmtRule.java

@@ -53,7 +53,7 @@ public class MktMgmtRule extends Model<MktMgmtRule> {
 	 * 推送方式
 	 */
 	@Schema(description = "推送方式,1-弹出提示,2-打开网址,3-内置浏览器打开网址,4-使用webview打开网址",example = "1")
-	private Integer action;
+	private String action;
 
 	/**
 	 * 备注

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

@@ -65,6 +65,68 @@ public class DomainValidationUtil {
 		return true;
 	}
 
+
+	public static boolean isValidDomain2(String domain) {
+		// 空值检查
+		if (domain == null || domain.trim().isEmpty()) {
+			return false;
+		}
+
+		String trimDomain = domain.trim().toLowerCase();
+
+		// 长度限制检查
+		if (trimDomain.length() > 255) {
+			return false;
+		}
+
+		// 检查是否包含至少一个点
+		if (!trimDomain.contains(".")) {
+			return false;
+		}
+
+		// 分割域名部分
+		String[] parts = trimDomain.split("\\.");
+
+		// 检查至少有两个部分(主域名和顶级域名)
+		if (parts.length < 2) {
+			return false;
+		}
+
+		// 检查每个部分的长度和格式
+		for (String part : parts) {
+			// 每个部分不能为空
+			if (part.isEmpty()) {
+				return false;
+			}
+
+			// 每个部分长度不能超过63个字符
+			if (part.length() > 63) {
+				return false;
+			}
+
+			// 检查每个部分的字符是否合法
+			// 允许:字母、数字、连字符
+			// 不允许:以连字符开头或结尾
+			if (part.startsWith("-") || part.endsWith("-")) {
+				return false;
+			}
+
+			// 检查字符是否合法
+			for (char c : part.toCharArray()) {
+				if (!Character.isLetterOrDigit(c) && c != '-') {
+					return false;
+				}
+			}
+		}
+
+		// 检查顶级域名至少有2个字符
+		String tld = parts[parts.length - 1];
+		if (tld.length() < 2) {
+			return false;
+		}
+
+		return true;
+	}
 	/**
 	 * 验证域名并返回具体错误信息
 	 * @param domain 待验证的域名

+ 1 - 1
pig-marketing/pig-marketing-api/src/main/java/com/pig4cloud/pig/marketing/api/vo/rule/PageMktRuleVO.java

@@ -63,7 +63,7 @@ public class PageMktRuleVO implements Serializable {
 	 * 推送方式,1-弹出提示,2-打开网址,3-内置浏览器打开网址,4-使用webview打开网址
 	 */
 	@Schema(description = "推送方式,1-弹出提示,2-打开网址,3-内置浏览器打开网址,4-使用webview打开网址")
-	private Integer action;
+	private String action;
 
 	/**
 	 * 备注

+ 435 - 349
pig-marketing/pig-marketing-biz/src/main/java/com/pig4cloud/pig/marketing/service/impl/MktMgmtRuleServiceImpl.java

@@ -60,6 +60,22 @@ public class MktMgmtRuleServiceImpl implements MktMgmtRuleService {
 		List<String> ipList = reqDto.getIp();
 		// 验证域名列表
 		List<String> domainList = reqDto.getDomain();
+		// 验证关键字列表
+		List<String> keywords = reqDto.getKeyword();
+
+		// 统一验证输入数据
+		if (ipList != null && !ipList.isEmpty()) {
+			validateIpList(ipList);
+		}
+		if (domainList != null && !domainList.isEmpty()) {
+			validateDomainList(domainList);
+		}
+		if (keywords != null && !keywords.isEmpty()) {
+			validateKeywordList(keywords);
+		}
+
+		// 验证规则名
+		validateRuleName(reqDto.getRuleName(), reqDto.getId());
 
 		if (reqDto.getId() == null) {
 			// 新增规则
@@ -71,32 +87,7 @@ public class MktMgmtRuleServiceImpl implements MktMgmtRuleService {
 			if (ipList != null && !ipList.isEmpty()) {
 				for (String ip : ipList) {
 					if (StringUtils.hasText(ip)) {
-						MktMgmtRuleIp ruleIp = new MktMgmtRuleIp();
-						ruleIp.setRuleId(rule.getId());
-
-						// 处理IP格式
-						if (ip.contains("/")) {
-							// IP段格式
-							String[] parts = ip.split("/");
-							String startIp = parts[0];
-							String endIpSuffix = parts[1];
-
-							String[] startParts = startIp.split("\\.");
-							int endSuffix = Integer.parseInt(endIpSuffix);
-							String endIp = String.format("%s.%s.%s.%d",
-									startParts[0], startParts[1], startParts[2], endSuffix);
-
-							ruleIp.setIpMode(2); // IP段模式
-							ruleIp.setStartIp(startIp);
-							ruleIp.setEndIp(endIp);
-						} else {
-							// 单IP格式
-							ruleIp.setIpMode(1);
-							ruleIp.setStartIp(ip);
-							ruleIp.setEndIp("");
-						}
-						// 校验IP合法性
-						validateIp(ruleIp);
+						MktMgmtRuleIp ruleIp = buildRuleIp(ip, rule.getId());
 						mktMgmtRuleIpMapper.insert(ruleIp);
 					}
 				}
@@ -106,27 +97,20 @@ public class MktMgmtRuleServiceImpl implements MktMgmtRuleService {
 			if (domainList != null && !domainList.isEmpty()) {
 				for (String domain : domainList) {
 					if (StringUtils.hasText(domain)) {
-						// 校验域名
-						validateDomain(domain);
 						MktMgmtRuleDomain ruleDomain = new MktMgmtRuleDomain();
 						ruleDomain.setRuleId(rule.getId());
-						ruleDomain.setDomain(domain);
+						ruleDomain.setDomain(domain.trim());
 						mktMgmtRuleDomainMapper.insert(ruleDomain);
 					}
 				}
 			}
 
 			// 保存关键字
-			List<String> keywords = reqDto.getKeyword();
 			if (keywords != null && !keywords.isEmpty()) {
 				for (String keyword : keywords) {
 					if (StringUtils.hasText(keyword)) {
-						if (checkRuleKeywordExists(keyword)) {
-							throw new BusinessException("关键字 '" + keyword + "' 已存在,不能重复");
-						}
-
 						MktMgmtKeyword mktMgmtKeyword = new MktMgmtKeyword();
-						mktMgmtKeyword.setKeyword(keyword);
+						mktMgmtKeyword.setKeyword(keyword.trim());
 						mktMgmtKeyword.setRuleId(rule.getId());
 						mktMgmtKeywordMapper.insert(mktMgmtKeyword);
 					}
@@ -134,6 +118,7 @@ public class MktMgmtRuleServiceImpl implements MktMgmtRuleService {
 			}
 		}
 		else {
+			// 修改规则
 			// 1. 更新主规则信息
 			MktMgmtRule mktMgmtRule = mktMgmtRuleMapper.selectById(reqDto.getId());
 			if (mktMgmtRule == null) {
@@ -143,168 +128,16 @@ public class MktMgmtRuleServiceImpl implements MktMgmtRuleService {
 			BeanUtils.copyProperties(reqDto, rule);
 			mktMgmtRuleMapper.updateById(rule);
 
-			// 2. 处理IP列表
 			Long ruleId = reqDto.getId();
 
-			// 查询现有的IP记录
-			List<MktMgmtRuleIp> oldIpRecords = mktMgmtRuleIpMapper.selectList(Wrappers.<MktMgmtRuleIp>lambdaQuery()
-					.eq(MktMgmtRuleIp::getRuleId, ruleId));
-
-			// 构建现有IP的映射(startIp -> record)
-			Map<String, MktMgmtRuleIp> oldIpMap = oldIpRecords.stream()
-					.collect(Collectors.toMap(MktMgmtRuleIp::getStartIp, ip -> ip));
-
-			// 构建新IP的映射
-			Map<String, MktMgmtRuleIp> newIpMap = new HashMap<>();
-			if (ipList != null && !ipList.isEmpty()) {
-				for (String ip : ipList) {
-					if (StringUtils.hasText(ip)) {
-						MktMgmtRuleIp ruleIp = new MktMgmtRuleIp();
-						ruleIp.setRuleId(ruleId);
-
-						// 处理IP格式
-						if (ip.contains("/")) {
-							// IP段格式
-							String[] parts = ip.split("/");
-							String startIpPart = parts[0];
-							String endIpSuffix = parts[1];
-
-							String[] startParts = startIpPart.split("\\.");
-							int endSuffix = Integer.parseInt(endIpSuffix);
-							String endIp = String.format("%s.%s.%s.%d",
-									startParts[0], startParts[1], startParts[2], endSuffix);
-
-							ruleIp.setIpMode(2); // IP段模式
-							ruleIp.setStartIp(startIpPart);
-							ruleIp.setEndIp(endIp);
-						} else {
-							// 单IP格式
-							ruleIp.setIpMode(1);
-							ruleIp.setStartIp(ip);
-							ruleIp.setEndIp("");
-						}
-
-						newIpMap.put(ruleIp.getStartIp(), ruleIp);
-					}
-				}
-			}
-
-			// 处理需要删除的IP(存在于旧列表但不存在于新列表)
-			List<Long> needDeleteIpIds = oldIpRecords.stream()
-					.filter(oldIp -> !newIpMap.containsKey(oldIp.getStartIp()))
-					.map(MktMgmtRuleIp::getId)
-					.toList();
-			if (!needDeleteIpIds.isEmpty()) {
-				mktMgmtRuleIpMapper.deleteByIds(needDeleteIpIds);
-			}
-
-			// 处理需要新增的IP(存在于新列表但不存在于旧列表)
-			List<MktMgmtRuleIp> needAddIps = newIpMap.values().stream()
-					.filter(newIp -> !oldIpMap.containsKey(newIp.getStartIp()))
-					.toList();
-			if (!needAddIps.isEmpty()) {
-				for (MktMgmtRuleIp ruleIp : needAddIps) {
-					// 校验IP合法性
-					validateIp(ruleIp);
-					// 校验IP是否已存在
-					if (checkRuleIpExists(ruleIp, ruleId)) {
-						if (ruleIp.getIpMode() == 1) {
-							throw new BusinessException("IP '" + ruleIp.getStartIp() + "' 已存在或被其他IP段包含");
-						} else {
-							String[] end = ruleIp.getEndIp().split("\\.");
-							throw new BusinessException("IP段 '" + ruleIp.getStartIp() + "/" + end[3] + "' 已存在或与其他IP段重叠");
-						}
-					}
-					mktMgmtRuleIpMapper.insert(ruleIp);
-				}
-			}
+			// 2. 处理IP列表
+			updateIpList(ipList, ruleId);
 
 			// 3. 处理域名列表
-			// 查询现有的域名记录
-			List<MktMgmtRuleDomain> oldDomainRecords = mktMgmtRuleDomainMapper.selectList(Wrappers.<MktMgmtRuleDomain>lambdaQuery()
-					.eq(MktMgmtRuleDomain::getRuleId, ruleId));
-
-			// 构建现有域名的映射(domain -> record)
-			Map<String, MktMgmtRuleDomain> oldDomainMap = oldDomainRecords.stream()
-					.collect(Collectors.toMap(MktMgmtRuleDomain::getDomain, domain -> domain));
-
-			// 构建新域名的映射
-			Map<String, MktMgmtRuleDomain> newDomainMap = new HashMap<>();
-			if (domainList != null && !domainList.isEmpty()) {
-				for (String domain : domainList) {
-					if (StringUtils.hasText(domain)) {
-						MktMgmtRuleDomain ruleDomain = new MktMgmtRuleDomain();
-						ruleDomain.setRuleId(ruleId);
-						ruleDomain.setDomain(domain);
-						newDomainMap.put(domain, ruleDomain);
-					}
-				}
-			}
+			updateDomainList(domainList, ruleId);
 
-			// 处理需要删除的域名(存在于旧列表但不存在于新列表)
-			List<Long> needDeleteDomainIds = oldDomainRecords.stream()
-					.filter(oldDomain -> !newDomainMap.containsKey(oldDomain.getDomain()))
-					.map(MktMgmtRuleDomain::getId)
-					.toList();
-			if (!needDeleteDomainIds.isEmpty()) {
-				mktMgmtRuleDomainMapper.deleteByIds(needDeleteDomainIds);
-			}
-
-			// 处理需要新增的域名(存在于新列表但不存在于旧列表)
-			List<MktMgmtRuleDomain> needAddDomains = newDomainMap.values().stream()
-					.filter(newDomain -> !oldDomainMap.containsKey(newDomain.getDomain()))
-					.toList();
-			if (!needAddDomains.isEmpty()) {
-				for (MktMgmtRuleDomain ruleDomain : needAddDomains) {
-					// 校验域名合法性
-					validateDomain(ruleDomain.getDomain());
-					// 校验新增的域名是否重复
-					if (checkRuleDomainExists(ruleDomain.getDomain(), ruleId)) {
-						throw new BusinessException("域名 '" + ruleDomain.getDomain() + "' 已存在,不能重复");
-					}
-					mktMgmtRuleDomainMapper.insert(ruleDomain);
-				}
-			}
-
-			// 4. 处理关键字(精确更新)
-			List<String> newKeywords = reqDto.getKeyword();
-			newKeywords = newKeywords == null ? Collections.emptyList() : newKeywords;
-
-			List<MktMgmtKeyword> oldKeywords = mktMgmtKeywordMapper.selectList(Wrappers.<MktMgmtKeyword>lambdaQuery()
-					.eq(MktMgmtKeyword::getRuleId, ruleId));
-			Set<String> oldKeywordSet = oldKeywords.stream()
-					.map(MktMgmtKeyword::getKeyword)
-					.collect(Collectors.toSet());
-
-			// 提取新关键字集合
-			Set<String> newKeywordSet = new HashSet<>(newKeywords);
-
-			// 处理需要删除的关键字(存在于旧列表但不存在于新列表)
-			List<Long> needDeleteIds = oldKeywords.stream()
-					.filter(keyword -> !newKeywordSet.contains(keyword.getKeyword()))
-					.map(MktMgmtKeyword::getId)
-					.toList();
-			if (!needDeleteIds.isEmpty()) {
-				mktMgmtKeywordMapper.deleteByIds(needDeleteIds);
-			}
-
-			// 处理需要新增的关键字(存在于新列表但不存在于旧列表)
-			List<String> needAddKeywords = newKeywordSet.stream()
-					.filter(keyword -> !oldKeywordSet.contains(keyword))
-					.toList();
-			if (!needAddKeywords.isEmpty()) {
-				for (String keyword : needAddKeywords) {
-					// 检查关键字是否重复
-					if (checkRuleKeywordExists(keyword)) {
-						throw new BusinessException("关键字 '" + keyword + "' 已被其他规则使用,不能重复");
-					}
-
-					MktMgmtKeyword mktMgmtKeyword = new MktMgmtKeyword();
-					mktMgmtKeyword.setKeyword(keyword);
-					mktMgmtKeyword.setRuleId(ruleId);
-					mktMgmtKeywordMapper.insert(mktMgmtKeyword);
-				}
-			}
+			// 4. 处理关键字列表
+			updateKeywordList(keywords, ruleId);
 		}
 	}
 
@@ -412,7 +245,7 @@ public class MktMgmtRuleServiceImpl implements MktMgmtRuleService {
 		}
 
 		// 按创建时间倒序排列
-		queryWrapper.orderByDesc("create_time");
+		queryWrapper.orderByDesc("create_time","id");
 
 		// 执行分页查询
 		Page<MktMgmtRule> rulePage = mktMgmtRuleMapper.selectPage(page, queryWrapper);
@@ -480,88 +313,6 @@ public class MktMgmtRuleServiceImpl implements MktMgmtRuleService {
 		return resultPage;
 	}
 
-	public Page<PageMktRuleVO> pageMktRule2(PageMktRuleDTO reqDto) {
-		Page<MktMgmtRule> page = new Page<>(reqDto.getCurrent(), reqDto.getSize());
-
-		// 构建查询条件
-		QueryWrapper<MktMgmtRule> queryWrapper = new QueryWrapper<>();
-
-		// 1.1、关键字模糊查询 - 通过关联查询关键字表
-		if (StringUtils.hasText(reqDto.getKeyword())) {
-			// 先查询包含该关键字的规则ID
-			List<MktMgmtKeyword> keywords = mktMgmtKeywordMapper.selectList(
-					Wrappers.<MktMgmtKeyword>lambdaQuery()
-							.like(MktMgmtKeyword::getKeyword, reqDto.getKeyword())
-			);
-
-			if (!keywords.isEmpty()) {
-				List<Long> ruleIds = keywords.stream()
-						.map(MktMgmtKeyword::getRuleId)
-						.distinct()
-						.collect(Collectors.toList());
-				queryWrapper.in("id", ruleIds);
-			} else {
-				// 如果没有找到匹配的关键字,返回空结果
-				return new Page<>();
-			}
-		}
-
-		// 1.2、IP匹配逻辑 - 支持单IP和IP段匹配
-		if (StringUtils.hasText(reqDto.getIp())) {
-			String inputIp = reqDto.getIp();
-			// 验证输入IP格式
-			if (!IPUtils.isValidIp(inputIp)) {
-				throw new BusinessException("输入的IP格式不正确");
-			}
-
-			queryWrapper.and(wrapper -> wrapper
-					// 匹配单IP模式:ipMode=1 且 startIp等于输入IP
-					.and(w -> w.eq("ip_mode", 1).eq("start_ip", inputIp))
-					// 匹配IP段模式:ipMode=2 且 输入IP在startIp和endIp范围内
-					.or(w -> w.eq("ip_mode", 2)
-							.apply("INET_ATON({0}) BETWEEN INET_ATON(start_ip) AND INET_ATON(end_ip)", inputIp))
-			);
-		}
-
-		// 1.3、域名模糊查询
-		if (StringUtils.hasText(reqDto.getDomain())) {
-			queryWrapper.like("domain", reqDto.getDomain());
-		}
-
-		// 按创建时间倒序排列
-		queryWrapper.orderByDesc("create_time");
-
-		// 执行分页查询
-		Page<MktMgmtRule> rulePage = mktMgmtRuleMapper.selectPage(page, queryWrapper);
-
-		// 转换为VO对象
-		Page<PageMktRuleVO> resultPage = new Page<>(reqDto.getCurrent(), reqDto.getSize());
-		resultPage.setTotal(rulePage.getTotal());
-		resultPage.setPages(rulePage.getPages());
-
-		List<PageMktRuleVO> voList = rulePage.getRecords().stream().map(rule -> {
-			PageMktRuleVO vo = new PageMktRuleVO();
-			BeanUtils.copyProperties(rule, vo);
-
-			// 查询该规则对应的关键字列表
-			List<MktMgmtKeyword> keywords = mktMgmtKeywordMapper.selectList(
-					Wrappers.<MktMgmtKeyword>lambdaQuery()
-							.eq(MktMgmtKeyword::getRuleId, rule.getId())
-			);
-
-			List<String> keywordList = keywords.stream()
-					.map(MktMgmtKeyword::getKeyword)
-					.collect(Collectors.toList());
-			vo.setKeyword(keywordList);
-
-			return vo;
-		}).collect(Collectors.toList());
-
-		resultPage.setRecords(voList);
-
-		return resultPage;
-	}
-
 	/**
 	 * 根据ID查询营销规则
 	 * @param id id
@@ -623,22 +374,6 @@ public class MktMgmtRuleServiceImpl implements MktMgmtRuleService {
 
 		return result;
 	}
-	public PageMktRuleVO getMktRuleById2(Long id) {
-		MktMgmtRule mktMgmtRule = mktMgmtRuleMapper.selectById(id);
-		if (mktMgmtRule == null){
-			throw new BusinessException("规则不存在");
-		}
-		PageMktRuleVO result = new PageMktRuleVO();
-		BeanUtils.copyProperties(mktMgmtRule, result);
-		// 查询该规则对应关键字列表
-		List<MktMgmtKeyword> keywords = mktMgmtKeywordMapper.selectList(Wrappers.<MktMgmtKeyword>lambdaQuery()
-				.eq(MktMgmtKeyword::getRuleId, id));
-		List<String> keywordList = keywords.stream()
-				.map(MktMgmtKeyword::getKeyword)
-				.collect(Collectors.toList());
-		result.setKeyword(keywordList);
-		return result;
-	}
 
 	/**
 	 * 校验IP格式是否合法
@@ -663,81 +398,432 @@ public class MktMgmtRuleServiceImpl implements MktMgmtRuleService {
 	 * @param domain 域名
 	 */
 	private void validateDomain(String domain){
-		if (!DomainValidationUtil.isValidDomain(domain)) {
+		if (!DomainValidationUtil.isValidDomain2(domain)) {
 			throw new BusinessException("域名格式错误: " + domain);
 		}
 	}
 
 	/**
-	 * 检查规则IP是否重复
-	 * @param ruleIp 要检查的IP信息
-	 * @param ruleId 规则ID(更新时使用,新增时为null)
-	 * @return 是否存在重复
+	 * 验证规则名是否重复
+	 * @param ruleName 规则名
+	 * @param ruleId 规则ID(修改时为当前规则ID,新增时为null)
+	 */
+	private void validateRuleName(String ruleName, Long ruleId) {
+		if (!StringUtils.hasText(ruleName)) {
+			throw new BusinessException("规则名不能为空");
+		}
+
+		// 检查规则名是否已存在
+		LambdaQueryWrapper<MktMgmtRule> wrapper = Wrappers.<MktMgmtRule>lambdaQuery()
+				.eq(MktMgmtRule::getRuleName, ruleName.trim());
+
+		// 如果是修改操作,排除当前规则
+		if (ruleId != null) {
+			wrapper.ne(MktMgmtRule::getId, ruleId);
+		}
+
+		if (mktMgmtRuleMapper.selectCount(wrapper) > 0) {
+			throw new BusinessException("规则名 '" + ruleName.trim() + "' 已存在,不能重复");
+		}
+	}
+
+	/**
+	 * 验证IP列表,检查是否存在冲突
+	 * @param ipList IP列表
 	 */
-	private Boolean checkRuleIpExists(MktMgmtRuleIp ruleIp, Long ruleId){
-		QueryWrapper<MktMgmtRuleIp> query = new QueryWrapper<MktMgmtRuleIp>().eq("rule_id", ruleId);
-
-		query.and(wrapper -> {
-			// 单IP模式检查
-			if (ruleIp.getIpMode() == 1) {
-				String targetIp = ruleIp.getStartIp();
-				// 检查是否有相同单IP
-				wrapper.and(w -> w
-						.eq("ip_mode", 1)
-						.eq("start_ip", targetIp));
-
-				// 检查是否被IP段包含
-				wrapper.or(w -> w
-						.eq("ip_mode", 2)
-						.apply("INET_ATON({0}) BETWEEN INET_ATON(start_ip) AND INET_ATON(end_ip)", targetIp));
+	private void validateIpList(List<String> ipList) {
+		if (ipList == null || ipList.isEmpty()) {
+			return;
+		}
+
+		// 过滤空值和去重
+		List<String> validIps = ipList.stream()
+				.filter(StringUtils::hasText)
+				.map(String::trim)
+				.toList();
+
+		// 检查是否有重复的单IP
+		Set<String> singleIps = new HashSet<>();
+		Set<String> ipRanges = new HashSet<>();
+
+		for (String ip : validIps) {
+			if (ip.contains("/")) {
+				// IP段格式
+				ipRanges.add(ip);
+			} else {
+				// 单IP格式
+				if (!singleIps.add(ip)) {
+					throw new BusinessException("IP列表中存在重复的单IP: " + ip);
+				}
 			}
+		}
+
+		// 检查是否有重复的IP段
+		List<String> rangeList = validIps.stream()
+				.filter(ip -> ip.contains("/"))
+				.toList();
+		Set<String> uniqueRanges = new HashSet<>(rangeList);
+		if (rangeList.size() != uniqueRanges.size()) {
+			throw new BusinessException("IP列表中存在重复的IP段");
+		}
+
+		// 检查单IP是否在IP段中
+		for (String singleIp : singleIps) {
+			for (String ipRange : ipRanges) {
+				if (isIpInRange(singleIp, ipRange)) {
+					throw new BusinessException("单IP '" + singleIp + "' 在IP段 '" + ipRange + "' 中,存在冲突");
+				}
+			}
+		}
+
+		// 检查IP段之间是否重叠
+		List<String> uniqueRangeList = new ArrayList<>(ipRanges);
+		for (int i = 0; i < uniqueRangeList.size(); i++) {
+			for (int j = i + 1; j < uniqueRangeList.size(); j++) {
+				if (isIpRangesOverlap(uniqueRangeList.get(i), uniqueRangeList.get(j))) {
+					throw new BusinessException("IP段 '" + uniqueRangeList.get(i) + "' 和 '" + uniqueRangeList.get(j) + "' 存在重叠");
+				}
+			}
+		}
+	}
+
+	/**
+	 * 验证域名列表,检查格式和重复
+	 * @param domainList 域名列表
+	 */
+	private void validateDomainList(List<String> domainList) {
+		if (domainList == null || domainList.isEmpty()) {
+			return;
+		}
+
+		// 过滤空值,但不去重(保留重复项用于检测)
+		List<String> validDomains = domainList.stream()
+				.filter(StringUtils::hasText)
+				.map(String::trim)
+				.toList();
+
+		// 检查是否有重复的域名
+		Set<String> uniqueDomains = new HashSet<>();
+		for (String domain : validDomains) {
+			if (!uniqueDomains.add(domain)) {
+				throw new BusinessException("域名列表中存在重复的域名: " + domain);
+			}
+		}
+
+		// 校验每个域名的格式
+		for (String domain : validDomains) {
+			validateDomain(domain);
+		}
+	}
+
+	/**
+	 * 验证关键字列表,检查重复
+	 * @param keywordList 关键字列表
+	 */
+	private void validateKeywordList(List<String> keywordList) {
+		if (keywordList == null || keywordList.isEmpty()) {
+			return;
+		}
+
+		// 过滤空值,但不去重(保留重复项用于检测)
+		List<String> validKeywords = keywordList.stream()
+				.filter(StringUtils::hasText)
+				.map(String::trim)
+				.toList();
+
+		// 检查是否有重复的关键字
+		Set<String> uniqueKeywords = new HashSet<>();
+		for (String keyword : validKeywords) {
+			if (!uniqueKeywords.add(keyword)) {
+				throw new BusinessException("关键字列表中存在重复的关键字: " + keyword);
+			}
+		}
+	}
+
+	/**
+	 * 检查单IP是否在IP段范围内
+	 * @param singleIp 单IP
+	 * @param ipRange IP段 (格式: 192.168.10.200/220 表示 192.168.10.200~192.168.10.220)
+	 * @return 是否在范围内
+	 */
+	private boolean isIpInRange(String singleIp, String ipRange) {
+		if (!IPUtils.isValidIp(singleIp)) {
+			return false;
+		}
+
+		String[] parts = ipRange.split("/");
+		if (parts.length != 2) {
+			return false;
+		}
+
+		String startIp = parts[0];
+		String endPart = parts[1];
+
+		if (!IPUtils.isValidIp(startIp)) {
+			return false;
+		}
 
-			// IP段模式检查
-			if (ruleIp.getIpMode() == 2) {
-				String startIp = ruleIp.getStartIp();
-				String endIp = ruleIp.getEndIp();
-
-				// 检查是否与单IP重叠:单IP在输入IP段范围内
-				wrapper.and(w -> w
-						.eq("ip_mode", 1)
-						.apply("INET_ATON(start_ip) BETWEEN INET_ATON({0}) AND INET_ATON({1})", startIp, endIp));
-
-				// 检查是否与IP段重叠:两个IP段有交集
-				wrapper.or(w -> w
-						.eq("ip_mode", 2)
-						.and(w2 -> w2
-								.apply("INET_ATON(start_ip) BETWEEN INET_ATON({0}) AND INET_ATON({1})", startIp, endIp)
-								.or().apply("INET_ATON(end_ip) BETWEEN INET_ATON({0}) AND INET_ATON({1})", startIp, endIp)
-								.or().apply("INET_ATON({0}) BETWEEN INET_ATON(start_ip) AND INET_ATON(end_ip)", startIp)
-						)
-				);
+		// 构造完整的结束IP
+		String[] startParts = startIp.split("\\.");
+		String endIp = String.format("%s.%s.%s.%s",
+				startParts[0], startParts[1], startParts[2], endPart);
+
+		if (!IPUtils.isValidIp(endIp)) {
+			return false;
+		}
+
+		long singleIpLong = ipToLong(singleIp);
+		long startIpLong = ipToLong(startIp);
+		long endIpLong = ipToLong(endIp);
+
+		// 确保开始IP小于等于结束IP
+		if (startIpLong > endIpLong) {
+			long temp = startIpLong;
+			startIpLong = endIpLong;
+			endIpLong = temp;
+		}
+
+		return singleIpLong >= startIpLong && singleIpLong <= endIpLong;
+	}
+
+	/**
+	 * 检查两个IP段是否重叠
+	 * @param range1 第一个IP段
+	 * @param range2 第二个IP段
+	 * @return 是否重叠
+	 */
+	private boolean isIpRangesOverlap(String range1, String range2) {
+		String[] parts1 = range1.split("/");
+		String[] parts2 = range2.split("/");
+
+		if (parts1.length != 2 || parts2.length != 2) {
+			return false;
+		}
+
+		String network1 = parts1[0];
+		String network2 = parts2[0];
+		int prefix1 = Integer.parseInt(parts1[1]);
+		int prefix2 = Integer.parseInt(parts2[1]);
+
+		if (!IPUtils.isValidIp(network1) || !IPUtils.isValidIp(network2)) {
+			return false;
+		}
+
+		long network1Long = ipToLong(network1);
+		long network2Long = ipToLong(network2);
+
+		// 计算网络掩码
+		long mask1 = (0xFFFFFFFFL << (32 - prefix1)) & 0xFFFFFFFFL;
+		long mask2 = (0xFFFFFFFFL << (32 - prefix2)) & 0xFFFFFFFFL;
+
+		// 计算网络地址
+		long net1 = network1Long & mask1;
+		long net2 = network2Long & mask2;
+
+		// 计算广播地址
+		long broadcast1 = net1 | (~mask1 & 0xFFFFFFFFL);
+		long broadcast2 = net2 | (~mask2 & 0xFFFFFFFFL);
+
+		// 检查是否重叠:一个网络的开始地址小于等于另一个网络的结束地址,且一个网络的结束地址大于等于另一个网络的开始地址
+		return net1 <= broadcast2 && broadcast1 >= net2;
+	}
+
+	/**
+	 * 将IP地址转换为长整型
+	 * @param ip IP地址
+	 * @return 长整型值
+	 */
+	private 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字符串
+	 * @param ruleId 规则ID
+	 * @return IP规则对象
+	 */
+	private MktMgmtRuleIp buildRuleIp(String ip, Long ruleId) {
+		MktMgmtRuleIp ruleIp = new MktMgmtRuleIp();
+		ruleIp.setRuleId(ruleId);
+
+		if (ip.contains("/")) {
+			// IP段格式
+			String[] parts = ip.split("/");
+			String startIp = parts[0];
+			String endIpSuffix = parts[1];
+
+			String[] startParts = startIp.split("\\.");
+			int endSuffix = Integer.parseInt(endIpSuffix);
+			String endIp = String.format("%s.%s.%s.%d",
+					startParts[0], startParts[1], startParts[2], endSuffix);
+
+			ruleIp.setIpMode(2); // IP段模式
+			ruleIp.setStartIp(startIp);
+			ruleIp.setEndIp(endIp);
+		} else {
+			// 单IP格式
+			ruleIp.setIpMode(1);
+			ruleIp.setStartIp(ip);
+			ruleIp.setEndIp("");
+		}
+
+		// 校验IP合法性
+		validateIp(ruleIp);
+		return ruleIp;
+	}
+
+	/**
+	 * 更新IP列表
+	 * @param ipList 新IP列表
+	 * @param ruleId 规则ID
+	 */
+	private void updateIpList(List<String> ipList, Long ruleId) {
+		// 查询现有的IP记录
+		List<MktMgmtRuleIp> oldIpRecords = mktMgmtRuleIpMapper.selectList(Wrappers.<MktMgmtRuleIp>lambdaQuery()
+				.eq(MktMgmtRuleIp::getRuleId, ruleId));
+
+		// 构建现有IP的映射(使用复合键:startIp + endIp + ipMode)
+		Map<String, MktMgmtRuleIp> oldIpMap = oldIpRecords.stream()
+				.collect(Collectors.toMap(
+						ip -> ip.getStartIp() + "|" + ip.getEndIp() + "|" + ip.getIpMode(),
+						ip -> ip
+				));
+
+		// 构建新IP的映射
+		Map<String, MktMgmtRuleIp> newIpMap = new HashMap<>();
+		if (ipList != null && !ipList.isEmpty()) {
+			for (String ip : ipList) {
+				if (StringUtils.hasText(ip)) {
+					MktMgmtRuleIp ruleIp = buildRuleIp(ip, ruleId);
+					String key = ruleIp.getStartIp() + "|" + ruleIp.getEndIp() + "|" + ruleIp.getIpMode();
+					newIpMap.put(key, ruleIp);
+				}
 			}
-		});
+		}
 
-		return mktMgmtRuleIpMapper.selectCount(query) > 0;
+		// 处理需要删除的IP
+		List<Long> needDeleteIpIds = oldIpRecords.stream()
+				.filter(oldIp -> {
+					String oldKey = oldIp.getStartIp() + "|" + oldIp.getEndIp() + "|" + oldIp.getIpMode();
+					return !newIpMap.containsKey(oldKey);
+				})
+				.map(MktMgmtRuleIp::getId)
+				.toList();
+		if (!needDeleteIpIds.isEmpty()) {
+			mktMgmtRuleIpMapper.deleteByIds(needDeleteIpIds);
+		}
+
+		// 处理需要新增的IP
+		List<MktMgmtRuleIp> needAddIps = newIpMap.values().stream()
+				.filter(newIp -> {
+					String newKey = newIp.getStartIp() + "|" + newIp.getEndIp() + "|" + newIp.getIpMode();
+					return !oldIpMap.containsKey(newKey);
+				})
+				.toList();
+		if (!needAddIps.isEmpty()) {
+			for (MktMgmtRuleIp ruleIp : needAddIps) {
+				mktMgmtRuleIpMapper.insert(ruleIp);
+			}
+		}
 	}
 
 	/**
-	 * 检查规则域名是否存在重复
-	 * @param domain 要检查的域名
-	 * @param ruleId 规则ID(更新时使用,新增时为null)
-	 * @return 是否存在重复
+	 * 更新域名列表
+	 * @param domainList 新域名列表
+	 * @param ruleId 规则ID
 	 */
-	private Boolean checkRuleDomainExists(String domain, Long ruleId) {
-		LambdaQueryWrapper<MktMgmtRuleDomain> wrapper = Wrappers.<MktMgmtRuleDomain>lambdaQuery()
-				.eq(MktMgmtRuleDomain::getDomain, domain)
-				.ne(MktMgmtRuleDomain::getRuleId, ruleId);
-		return mktMgmtRuleDomainMapper.selectCount(wrapper) > 0;
+	private void updateDomainList(List<String> domainList, Long ruleId) {
+		// 查询现有的域名记录
+		List<MktMgmtRuleDomain> oldDomainRecords = mktMgmtRuleDomainMapper.selectList(Wrappers.<MktMgmtRuleDomain>lambdaQuery()
+				.eq(MktMgmtRuleDomain::getRuleId, ruleId));
+
+		// 构建现有域名的映射
+		Map<String, MktMgmtRuleDomain> oldDomainMap = oldDomainRecords.stream()
+				.collect(Collectors.toMap(MktMgmtRuleDomain::getDomain, domain -> domain));
+
+		// 构建新域名的映射
+		Map<String, MktMgmtRuleDomain> newDomainMap = new HashMap<>();
+		if (domainList != null && !domainList.isEmpty()) {
+			for (String domain : domainList) {
+				if (StringUtils.hasText(domain)) {
+					MktMgmtRuleDomain ruleDomain = new MktMgmtRuleDomain();
+					ruleDomain.setRuleId(ruleId);
+					ruleDomain.setDomain(domain.trim());
+					newDomainMap.put(domain.trim(), ruleDomain);
+				}
+			}
+		}
+
+		// 处理需要删除的域名
+		List<Long> needDeleteDomainIds = oldDomainRecords.stream()
+				.filter(oldDomain -> !newDomainMap.containsKey(oldDomain.getDomain()))
+				.map(MktMgmtRuleDomain::getId)
+				.toList();
+		if (!needDeleteDomainIds.isEmpty()) {
+			mktMgmtRuleDomainMapper.deleteByIds(needDeleteDomainIds);
+		}
+
+		// 处理需要新增的域名
+		List<MktMgmtRuleDomain> needAddDomains = newDomainMap.values().stream()
+				.filter(newDomain -> !oldDomainMap.containsKey(newDomain.getDomain()))
+				.toList();
+		if (!needAddDomains.isEmpty()) {
+			for (MktMgmtRuleDomain ruleDomain : needAddDomains) {
+				mktMgmtRuleDomainMapper.insert(ruleDomain);
+			}
+		}
 	}
 
 	/**
-	 * 检查规则关键则是否存在重复
-	 * @param keyword 要检查的域名
-	 * @return 是否存在重复
+	 * 更新关键字列表
+	 * @param keywords 新关键字列表
+	 * @param ruleId 规则ID
 	 */
-	private Boolean checkRuleKeywordExists(String keyword){
-		LambdaQueryWrapper<MktMgmtKeyword> wrapper = Wrappers.<MktMgmtKeyword>lambdaQuery()
-				.eq(MktMgmtKeyword::getKeyword, keyword);
-		return mktMgmtKeywordMapper.selectCount(wrapper) > 0;
+	private void updateKeywordList(List<String> keywords, Long ruleId) {
+		List<String> newKeywords = keywords == null ? Collections.emptyList() : keywords;
+
+		// 查询现有的关键字记录
+		List<MktMgmtKeyword> oldKeywords = mktMgmtKeywordMapper.selectList(Wrappers.<MktMgmtKeyword>lambdaQuery()
+				.eq(MktMgmtKeyword::getRuleId, ruleId));
+		Set<String> oldKeywordSet = oldKeywords.stream()
+				.map(MktMgmtKeyword::getKeyword)
+				.collect(Collectors.toSet());
+
+		// 提取新关键字集合
+		Set<String> newKeywordSet = new HashSet<>();
+		for (String keyword : newKeywords) {
+			if (StringUtils.hasText(keyword)) {
+				newKeywordSet.add(keyword.trim());
+			}
+		}
+
+		// 处理需要删除的关键字
+		List<Long> needDeleteIds = oldKeywords.stream()
+				.filter(keyword -> !newKeywordSet.contains(keyword.getKeyword()))
+				.map(MktMgmtKeyword::getId)
+				.toList();
+		if (!needDeleteIds.isEmpty()) {
+			mktMgmtKeywordMapper.deleteByIds(needDeleteIds);
+		}
+
+		// 处理需要新增的关键字
+		List<String> needAddKeywords = newKeywordSet.stream()
+				.filter(keyword -> !oldKeywordSet.contains(keyword))
+				.toList();
+		if (!needAddKeywords.isEmpty()) {
+			for (String keyword : needAddKeywords) {
+				MktMgmtKeyword mktMgmtKeyword = new MktMgmtKeyword();
+				mktMgmtKeyword.setKeyword(keyword);
+				mktMgmtKeyword.setRuleId(ruleId);
+				mktMgmtKeywordMapper.insert(mktMgmtKeyword);
+			}
+		}
 	}
 }