Quellcode durchsuchen

营销系统-关键字推送增加地区校验

wangcl vor 5 Tagen
Ursprung
Commit
44f9f1ad22

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

@@ -62,4 +62,7 @@ public class SaveMktRuleDTO implements Serializable {
 
 	@Schema(description = "备注")
 	private String remark;
+
+	@Schema(description = "推送地区")
+	private List<String> pushAddr;
 }

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

@@ -55,6 +55,12 @@ public class MktMgmtRule extends Model<MktMgmtRule> {
 	@Schema(description = "推送频率")
 	private String pushFrequency;
 
+	/**
+	 * 推送地区
+	 */
+	@Schema(description = "推送地区")
+	private String pushAddr;
+
 	/**
 	 * 延迟推送
 	 */

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

@@ -83,4 +83,10 @@ public class PageMktRuleVO implements Serializable {
 	@Schema(description = "备注")
 	private String remark;
 
+	/**
+	 * 推送地区列表(由规则表 pushAddr 字段拆分)
+	 */
+	@Schema(description = "推送地区列表")
+	private List<String> pushAddr;
+
 }

+ 3 - 74
pig-marketing/pig-marketing-biz/src/main/java/com/pig4cloud/pig/marketing/service/impl/MktMgmtHandPushServiceImpl.java

@@ -115,16 +115,10 @@ public class MktMgmtHandPushServiceImpl implements MktMgmtHandPushService {
 					if (resolvedAddress == null) {
 						return "无法解析推送IP【" + saveDTO.getPushIP() + "】的地址信息";
 					}
-					String fullAddress = normalizeAddress(resolvedAddress);
+					String fullAddress = ipLocationUtil.normalizeAddress(resolvedAddress);
 
 					// 检查是否匹配pushAddr中的任一地址
-					boolean addressMatched = false;
-					for (String allowedAddr : globalRule.getPushAddr()) {
-						if (isAddressMatched(fullAddress, allowedAddr)) {
-							addressMatched = true;
-							break;
-						}
-					}
+					boolean addressMatched = ipLocationUtil.matchAddress(fullAddress, globalRule.getPushAddr());
 
 					if (!addressMatched) {
 						return "推送地址【" + fullAddress + "】不在允许的地址范围内,允许的地址:" + globalRule.getPushAddr();
@@ -402,70 +396,5 @@ public class MktMgmtHandPushServiceImpl implements MktMgmtHandPushService {
      * @param allowedAddr 允许的地址配置
      * @return 是否匹配
      */
-    private boolean isAddressMatched(String fullAddress, String allowedAddr) {
-        if (fullAddress == null || allowedAddr == null) {
-            return false;
-        }
-
-        String normalizedFull = normalizeAddress(fullAddress);
-        String normalizedAllowed = normalizeAddress(allowedAddr);
-        if (!org.springframework.util.StringUtils.hasText(normalizedAllowed)) {
-            return false;
-        }
-        if ("all".equalsIgnoreCase(normalizedAllowed)) {
-            return true;
-        }
-
-        if (normalizedFull.equals(normalizedAllowed)) {
-            return true;
-        }
-
-        String[] allowedParts = normalizedAllowed.split("-");
-        String[] fullParts = normalizedFull.split("-");
-
-        if (allowedParts.length == 1 && fullParts.length >= 1) {
-            return allowedParts[0].equals(fullParts[0]);
-        }
-
-        if (allowedParts.length == 2 && fullParts.length >= 2) {
-            return allowedParts[0].equals(fullParts[0]) && allowedParts[1].equals(fullParts[1]);
-        }
-
-        if (allowedParts.length >= 3 && fullParts.length >= 3) {
-            return allowedParts[0].equals(fullParts[0]) &&
-                   allowedParts[1].equals(fullParts[1]) &&
-                   allowedParts[2].equals(fullParts[2]);
-        }
-
-        return false;
-    }
-
-    /**
-     * 规范化地址字符串:
-     * 1) 将分隔符统一为 '-'; 2) 去除分隔符两侧空格; 3) 去除多余空格
-     */
-    private String normalizeAddress(String input) {
-        if (input == null) {
-            return null;
-        }
-        String s = input.trim();
-        // 统一分隔符,去掉可能的 ISP 信息分隔符
-        s = s.replace('|', '-');
-        s = s.replace('-', '-');
-        s = s.replace('—', '-');
-        // 去除分隔符两侧空格
-        s = s.replaceAll("\\s*-\\s*", "-");
-        // 拆分并重组,移除空段
-        String[] parts = s.split("-");
-        StringBuilder sb = new StringBuilder();
-        for (String p : parts) {
-            if (org.springframework.util.StringUtils.hasText(p)) {
-                if (sb.length() > 0) {
-                    sb.append('-');
-                }
-                sb.append(p.trim());
-            }
-        }
-        return sb.toString();
-    }
+    // 地址匹配与规范化逻辑已迁移到 IPLocationUtil
 }

+ 38 - 0
pig-marketing/pig-marketing-biz/src/main/java/com/pig4cloud/pig/marketing/service/impl/MktMgmtPushRecordServiceImpl.java

@@ -10,8 +10,10 @@ import com.pig4cloud.pig.marketing.api.dto.MktMgmtPushRecordQueryDTO;
 import com.pig4cloud.pig.marketing.api.dto.MktMgmtPushRecordSaveDTO;
 import com.pig4cloud.pig.marketing.api.entity.*;
 import com.pig4cloud.pig.marketing.api.vo.rule.StatKeywordVO;
+import com.pig4cloud.pig.marketing.config.PushValidationConfig;
 import com.pig4cloud.pig.marketing.mapper.*;
 import com.pig4cloud.pig.marketing.service.MktMgmtPushRecordService;
+import com.pig4cloud.pig.marketing.util.IPLocationUtil;
 import com.pig4cloud.pig.marketing.util.PushFrequencyUtil;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -21,6 +23,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.util.StringUtils;
 
 import java.time.LocalDateTime;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -42,6 +45,8 @@ public class MktMgmtPushRecordServiceImpl implements MktMgmtPushRecordService {
 	private final MktMgmtRuleIpMapper mktMgmtRuleIpMapper;
 	private final MktMgmtRuleDomainMapper mktMgmtRuleDomainMapper;
 	private final RedisTemplate<String, Object> redisTemplate;
+	private final IPLocationUtil ipLocationUtil;
+	private final PushValidationConfig pushValidationConfig;
 
 	@Override
 	public Page<MktMgmtPushRecord> pageQuery(MktMgmtPushRecordQueryDTO queryDTO) {
@@ -105,6 +110,39 @@ public class MktMgmtPushRecordServiceImpl implements MktMgmtPushRecordService {
 				
 				List<String> matchedKeywords = getMatchedKeywordsForRule(saveDTO.getPushContent(), ruleId);
 				
+				// 地址校验(使用规则自身 pushAddr 字段,字符串以 && 分隔)
+				if (pushValidationConfig.getEnableAddressCheck()) {
+					String rulePushAddr = rule.getPushAddr();
+					if (StringUtils.hasText(rulePushAddr)) {
+						List<String> allowedAddrs;
+						String raw = rulePushAddr.trim();
+						if (raw.contains("&&")) {
+							allowedAddrs = Arrays.stream(raw.split("&&"))
+									.map(String::trim)
+									.filter(StringUtils::hasText)
+									.toList();
+						} else {
+							allowedAddrs = List.of(raw);
+						}
+
+						if (!allowedAddrs.isEmpty() && !"All".equalsIgnoreCase(allowedAddrs.get(0))) {
+							String addr = ipLocationUtil.getCityByIP(saveDTO.getPushIP());
+							String normalized = ipLocationUtil.normalizeAddress(addr);
+							boolean ok = ipLocationUtil.matchAddress(normalized, allowedAddrs);
+							if (!ok) {
+								errorDetails.append("规则").append(ruleId).append("(").append(rule.getRuleName()).append(")");
+								errorDetails.append("匹配").append(matchCount).append("个关键字[")
+										.append(String.join(",", matchedKeywords)).append("]");
+								errorDetails.append("但推送地址【").append(normalized)
+										.append("】不在允许的地址范围内,允许的地址:")
+										.append(allowedAddrs).append(";");
+								log.debug("规则{}地址校验失败,addr={}, allowed={}", ruleId, normalized, allowedAddrs);
+								continue;
+							}
+						}
+					}
+				}
+
 				// 验证IP和域名规则
 				ValidationResult validationResult = validateRuleIpAndDomainWithDetails(ruleId, saveDTO.getPushIP(), saveDTO.getPushDomain());
 				

+ 83 - 3
pig-marketing/pig-marketing-biz/src/main/java/com/pig4cloud/pig/marketing/service/impl/MktMgmtRuleServiceImpl.java

@@ -56,6 +56,10 @@ public class MktMgmtRuleServiceImpl implements MktMgmtRuleService {
 	@Override
 	@Transactional
 	public void saveMktRule(SaveMktRuleDTO reqDto){
+
+		// 新增规则
+		MktMgmtRule rule = new MktMgmtRule();
+		StringBuilder addrBuilder = new StringBuilder();
 		// 验证IP列表
 		List<String> ipList = reqDto.getIp();
 		// 验证域名列表
@@ -78,9 +82,30 @@ public class MktMgmtRuleServiceImpl implements MktMgmtRuleService {
 		validateRuleName(reqDto.getRuleName(), reqDto.getId());
 
 		if (reqDto.getId() == null) {
-			// 新增规则
-			MktMgmtRule rule = new MktMgmtRule();
+
+
+			//处理推送地址
+			if (reqDto.getPushAddr() != null && !reqDto.getPushAddr().isEmpty()){
+				if (reqDto.getPushAddr().size()>1){
+					for (String addr : reqDto.getPushAddr()){
+						addrBuilder.append(addr);
+						addrBuilder.append("&&");
+					}
+					addrBuilder.delete(addrBuilder.length()-2, addrBuilder.length());
+
+				} else {
+					if ("All".equals(reqDto.getPushAddr().get(0))){
+						addrBuilder.append("All");
+					} else {
+						addrBuilder.append(reqDto.getPushAddr().get(0));
+					}
+				}
+
+			}
 			BeanUtils.copyProperties(reqDto, rule);
+			if (StringUtils.hasText(addrBuilder)) {
+				rule.setPushAddr(addrBuilder.toString());
+			}
 			mktMgmtRuleMapper.insert(rule);
 
 			// 保存IP列表
@@ -93,6 +118,8 @@ public class MktMgmtRuleServiceImpl implements MktMgmtRuleService {
 				}
 			}
 
+
+
 			// 保存域名列表
 			if (domainList != null && !domainList.isEmpty()) {
 				for (String domain : domainList) {
@@ -116,6 +143,8 @@ public class MktMgmtRuleServiceImpl implements MktMgmtRuleService {
 					}
 				}
 			}
+
+
 		}
 		else {
 			// 修改规则
@@ -124,8 +153,29 @@ public class MktMgmtRuleServiceImpl implements MktMgmtRuleService {
 			if (mktMgmtRule == null) {
 				throw new BusinessException("规则不存在");
 			}
-			MktMgmtRule rule = new MktMgmtRule();
+
+			//处理推送地址
+			if (reqDto.getPushAddr() != null && !reqDto.getPushAddr().isEmpty()){
+				if (reqDto.getPushAddr().size()>1){
+					for (String addr : reqDto.getPushAddr()){
+						addrBuilder.append(addr);
+						addrBuilder.append("&&");
+					}
+					addrBuilder.delete(addrBuilder.length()-2, addrBuilder.length());
+
+				} else {
+					if ("All".equals(reqDto.getPushAddr().get(0))){
+						addrBuilder.append("All");
+					} else {
+						addrBuilder.append(reqDto.getPushAddr().get(0));
+					}
+				}
+
+			}
 			BeanUtils.copyProperties(reqDto, rule);
+			if (StringUtils.hasText(addrBuilder)) {
+				rule.setPushAddr(addrBuilder.toString());
+			}
 			mktMgmtRuleMapper.updateById(rule);
 
 			Long ruleId = reqDto.getId();
@@ -259,6 +309,21 @@ public class MktMgmtRuleServiceImpl implements MktMgmtRuleService {
 			PageMktRuleVO vo = new PageMktRuleVO();
 			BeanUtils.copyProperties(rule, vo);
 
+			// 规则 pushAddr 拆分
+			if (StringUtils.hasText(rule.getPushAddr())) {
+				String raw = rule.getPushAddr().trim();
+				java.util.List<String> addrs;
+				if (raw.contains("&&")) {
+					addrs = java.util.Arrays.stream(raw.split("&&"))
+							.map(String::trim)
+							.filter(StringUtils::hasText)
+							.toList();
+				} else {
+					addrs = java.util.List.of(raw);
+				}
+				vo.setPushAddr(addrs);
+			}
+
 			// 查询该规则对应的关键字列表
 			List<MktMgmtKeyword> keywords = mktMgmtKeywordMapper.selectList(
 					Wrappers.<MktMgmtKeyword>lambdaQuery()
@@ -327,6 +392,21 @@ public class MktMgmtRuleServiceImpl implements MktMgmtRuleService {
 		PageMktRuleVO result = new PageMktRuleVO();
 		BeanUtils.copyProperties(mktMgmtRule, result);
 
+		// 规则 pushAddr 拆分
+		if (StringUtils.hasText(mktMgmtRule.getPushAddr())) {
+			String raw = mktMgmtRule.getPushAddr().trim();
+			java.util.List<String> addrs;
+			if (raw.contains("&&")) {
+				addrs = java.util.Arrays.stream(raw.split("&&"))
+						.map(String::trim)
+						.filter(StringUtils::hasText)
+						.toList();
+			} else {
+				addrs = java.util.List.of(raw);
+			}
+			result.setPushAddr(addrs);
+		}
+
 		// 查询该规则对应的关键字列表
 		List<MktMgmtKeyword> keywords = mktMgmtKeywordMapper.selectList(
 				Wrappers.<MktMgmtKeyword>lambdaQuery()

+ 91 - 0
pig-marketing/pig-marketing-biz/src/main/java/com/pig4cloud/pig/marketing/util/IPLocationUtil.java

@@ -7,6 +7,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.lionsoul.ip2region.xdb.Searcher;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
 
 import java.io.File;
 import java.util.List;
@@ -186,6 +187,96 @@ public class IPLocationUtil {
         return null;
     }
     
+    /**
+     * 将地址字符串规范化为统一的“国家-省-市”分隔格式:
+     * - 分隔符统一为 '-'
+     * - 去除分隔符两侧空格
+     * - 去除空段
+     */
+    public String normalizeAddress(String input) {
+        if (input == null) {
+            return null;
+        }
+        String s = input.trim();
+        s = s.replace('|', '-');
+        s = s.replace('-', '-');
+        s = s.replace('—', '-');
+        // 去除分隔符两侧空格
+        s = s.replaceAll("\\s*-\\s*", "-");
+        // 拆分并重组
+        String[] parts = s.split("-");
+        StringBuilder sb = new StringBuilder();
+        for (String p : parts) {
+            if (StringUtils.hasText(p)) {
+                if (sb.length() > 0) {
+                    sb.append('-');
+                }
+                sb.append(p.trim());
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * 判断一个地址是否匹配另一个允许地址(允许地址可为 国家 / 国家-省 / 国家-省-市 或 "All")
+     */
+    public boolean isAddressMatched(String fullAddress, String allowedAddr) {
+        if (fullAddress == null || allowedAddr == null) {
+            return false;
+        }
+
+        String normalizedFull = normalizeAddress(fullAddress);
+        String normalizedAllowed = normalizeAddress(allowedAddr);
+        if (!org.springframework.util.StringUtils.hasText(normalizedAllowed)) {
+            return false;
+        }
+        if ("all".equalsIgnoreCase(normalizedAllowed)) {
+            return true;
+        }
+
+        if (normalizedFull.equals(normalizedAllowed)) {
+            return true;
+        }
+
+        String[] allowedParts = normalizedAllowed.split("-");
+        String[] fullParts = normalizedFull.split("-");
+
+        if (allowedParts.length == 1 && fullParts.length >= 1) {
+            return allowedParts[0].equals(fullParts[0]);
+        }
+
+        if (allowedParts.length == 2 && fullParts.length >= 2) {
+            return allowedParts[0].equals(fullParts[0]) && allowedParts[1].equals(fullParts[1]);
+        }
+
+        if (allowedParts.length >= 3 && fullParts.length >= 3) {
+            return allowedParts[0].equals(fullParts[0]) &&
+                   allowedParts[1].equals(fullParts[1]) &&
+                   allowedParts[2].equals(fullParts[2]);
+        }
+
+        return false;
+    }
+
+    /**
+     * 判断一个地址是否匹配允许地址列表
+     */
+    public boolean matchAddress(String fullAddress, List<String> allowedList) {
+        if (allowedList == null || allowedList.isEmpty()) {
+            return true;
+        }
+        // All 放在首位则跳过校验
+        if ("All".equalsIgnoreCase(allowedList.get(0))) {
+            return true;
+        }
+        for (String allowed : allowedList) {
+            if (isAddressMatched(fullAddress, allowed)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * 缓存结果
      *