|
@@ -4,15 +4,23 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
import com.pig4cloud.pig.marketing.api.dto.MktMgmtHandPushQueryDTO;
|
|
import com.pig4cloud.pig.marketing.api.dto.MktMgmtHandPushQueryDTO;
|
|
import com.pig4cloud.pig.marketing.api.dto.MktMgmtPushRecordSaveDTO;
|
|
import com.pig4cloud.pig.marketing.api.dto.MktMgmtPushRecordSaveDTO;
|
|
|
|
+import com.pig4cloud.pig.marketing.api.dto.config.SaveGlobalRuleDTO;
|
|
|
|
+import com.pig4cloud.pig.marketing.api.dto.config.PushAPPDTO;
|
|
import com.pig4cloud.pig.marketing.api.entity.MktMgmtPushRecord;
|
|
import com.pig4cloud.pig.marketing.api.entity.MktMgmtPushRecord;
|
|
|
|
+import com.pig4cloud.pig.marketing.api.entity.mongo.Device;
|
|
import com.pig4cloud.pig.marketing.api.service.MktMgmtHandPushService;
|
|
import com.pig4cloud.pig.marketing.api.service.MktMgmtHandPushService;
|
|
-import com.pig4cloud.pig.marketing.api.dto.config.SaveGlobalRuleDTO;
|
|
|
|
-import com.pig4cloud.pig.marketing.service.MarketingConfigService;
|
|
|
|
import com.pig4cloud.pig.marketing.api.vo.rule.push.HandPushVO;
|
|
import com.pig4cloud.pig.marketing.api.vo.rule.push.HandPushVO;
|
|
|
|
+import com.pig4cloud.pig.marketing.config.PushValidationConfig;
|
|
import com.pig4cloud.pig.marketing.mapper.MktMgmtPushRecordMapper;
|
|
import com.pig4cloud.pig.marketing.mapper.MktMgmtPushRecordMapper;
|
|
|
|
+import com.pig4cloud.pig.marketing.service.MarketingConfigService;
|
|
|
|
+import com.pig4cloud.pig.marketing.service.TcpDataService;
|
|
|
|
+import com.pig4cloud.pig.marketing.util.DeviceInfoUtil;
|
|
|
|
+import com.pig4cloud.pig.marketing.util.IPLocationUtil;
|
|
import com.pig4cloud.pig.marketing.util.PushFrequencyUtil;
|
|
import com.pig4cloud.pig.marketing.util.PushFrequencyUtil;
|
|
import lombok.RequiredArgsConstructor;
|
|
import lombok.RequiredArgsConstructor;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
+import org.springframework.context.annotation.Lazy;
|
|
import org.springframework.beans.BeanUtils;
|
|
import org.springframework.beans.BeanUtils;
|
|
import org.springframework.data.redis.core.RedisTemplate;
|
|
import org.springframework.data.redis.core.RedisTemplate;
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.stereotype.Service;
|
|
@@ -28,8 +36,8 @@ import java.util.stream.Collectors;
|
|
/**
|
|
/**
|
|
* 手动推送服务实现类
|
|
* 手动推送服务实现类
|
|
*
|
|
*
|
|
- * @author pig4cloud
|
|
|
|
- * @date 2025-01-20
|
|
|
|
|
|
+ * @author WCL
|
|
|
|
+ * @date 2025-09-04
|
|
* @description 手动推送相关业务实现
|
|
* @description 手动推送相关业务实现
|
|
*/
|
|
*/
|
|
@Slf4j
|
|
@Slf4j
|
|
@@ -39,6 +47,12 @@ public class MktMgmtHandPushServiceImpl implements MktMgmtHandPushService {
|
|
|
|
|
|
private final MktMgmtPushRecordMapper mktMgmtPushRecordMapper;
|
|
private final MktMgmtPushRecordMapper mktMgmtPushRecordMapper;
|
|
private final MarketingConfigService marketingConfigService;
|
|
private final MarketingConfigService marketingConfigService;
|
|
|
|
+ @Autowired
|
|
|
|
+ @Lazy
|
|
|
|
+ private TcpDataService tcpDataService;
|
|
|
|
+ private final IPLocationUtil ipLocationUtil;
|
|
|
|
+ private final DeviceInfoUtil deviceInfoUtil;
|
|
|
|
+ private final PushValidationConfig pushValidationConfig;
|
|
private final RedisTemplate<String, Object> redisTemplate;
|
|
private final RedisTemplate<String, Object> redisTemplate;
|
|
|
|
|
|
@Override
|
|
@Override
|
|
@@ -79,7 +93,7 @@ public class MktMgmtHandPushServiceImpl implements MktMgmtHandPushService {
|
|
@Override
|
|
@Override
|
|
public String handPush(MktMgmtPushRecordSaveDTO saveDTO) {
|
|
public String handPush(MktMgmtPushRecordSaveDTO saveDTO) {
|
|
try {
|
|
try {
|
|
- log.info("开始手动推送,推送内容:{}", saveDTO.getPushContent());
|
|
|
|
|
|
+ log.info("开始手动推送,推送内容:{},客户端ID:{}", saveDTO.getPushContent(), saveDTO.getClientId());
|
|
|
|
|
|
// 1. 获取全局手动推送规则
|
|
// 1. 获取全局手动推送规则
|
|
SaveGlobalRuleDTO globalRule = marketingConfigService.getGlobalRule();
|
|
SaveGlobalRuleDTO globalRule = marketingConfigService.getGlobalRule();
|
|
@@ -87,35 +101,120 @@ public class MktMgmtHandPushServiceImpl implements MktMgmtHandPushService {
|
|
return "获取全局手动推送规则失败";
|
|
return "获取全局手动推送规则失败";
|
|
}
|
|
}
|
|
|
|
|
|
- // 2. 校验IP
|
|
|
|
- if (!validateGlobalRuleIP(globalRule.getIp(), saveDTO.getPushIP())) {
|
|
|
|
|
|
+ // 2. IP校验(配置化控制)
|
|
|
|
+ if (pushValidationConfig.getEnableIpCheck() && !validateGlobalRuleIP(globalRule.getIp(), saveDTO.getPushIP())) {
|
|
return "推送IP【" + saveDTO.getPushIP() + "】不在允许的IP范围内";
|
|
return "推送IP【" + saveDTO.getPushIP() + "】不在允许的IP范围内";
|
|
}
|
|
}
|
|
|
|
|
|
- // 3. 校验域名
|
|
|
|
- if (!validateGlobalRuleDomain(globalRule.getDomain(), saveDTO.getPushDomain())) {
|
|
|
|
|
|
+ // 3. 地址校验 - 根据IP获取三级地址信息并匹配pushAddr
|
|
|
|
+ if (pushValidationConfig.getEnableAddressCheck() &&
|
|
|
|
+ globalRule.getPushAddr() != null && !globalRule.getPushAddr().isEmpty()) {
|
|
|
|
+
|
|
|
|
+ if ("All".equals(globalRule.getPushAddr().get(0))){
|
|
|
|
+ return "推送地址【All】";
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ String fullAddress = ipLocationUtil.getCityByIP(saveDTO.getPushIP());
|
|
|
|
+ if (fullAddress == null) {
|
|
|
|
+ return "无法解析推送IP【" + saveDTO.getPushIP() + "】的地址信息";
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 检查是否匹配pushAddr中的任一地址
|
|
|
|
+ boolean addressMatched = false;
|
|
|
|
+ for (String allowedAddr : globalRule.getPushAddr()) {
|
|
|
|
+ if (isAddressMatched(fullAddress, allowedAddr)) {
|
|
|
|
+ addressMatched = true;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!addressMatched) {
|
|
|
|
+ return "推送地址【" + fullAddress + "】不在允许的地址范围内,允许的地址:" + globalRule.getPushAddr();
|
|
|
|
+ }
|
|
|
|
+ log.info("地址校验通过,IP:{},地址:{}", saveDTO.getPushIP(), fullAddress);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 4. 应用校验 - 根据客户端ID获取设备信息并匹配pushApp和pushBundle
|
|
|
|
+ if (pushValidationConfig.getEnableAppCheck()) {
|
|
|
|
+ Device device = tcpDataService.getDeviceByClientID(saveDTO.getClientId());
|
|
|
|
+ if (device == null) {
|
|
|
|
+ return "未找到客户端ID【" + saveDTO.getClientId() + "】对应的设备信息";
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ String[] bundleAndAppId = deviceInfoUtil.extractBundleAndAppId(device.getDeviceInfo());
|
|
|
|
+ String bundle = bundleAndAppId[0];
|
|
|
|
+ String appId = bundleAndAppId[1];
|
|
|
|
+
|
|
|
|
+ boolean appMatched = false;
|
|
|
|
+ String matchedType = "";
|
|
|
|
+
|
|
|
|
+ // 检查pushApp(appId匹配)
|
|
|
|
+ if (globalRule.getPushApp() != null && !globalRule.getPushApp().isEmpty() && appId != null) {
|
|
|
|
+ // 检查是否为All
|
|
|
|
+ if (globalRule.getPushApp().stream()
|
|
|
|
+ .anyMatch(pushApp -> pushApp != null && "All".equals(pushApp.getAppId()))) {
|
|
|
|
+ appMatched = true;
|
|
|
|
+ matchedType = "appId(All)";
|
|
|
|
+ } else {
|
|
|
|
+ // 检查appId是否匹配
|
|
|
|
+ if (globalRule.getPushApp().stream()
|
|
|
|
+ .anyMatch(pushApp -> pushApp != null && appId.equals(pushApp.getAppId()))) {
|
|
|
|
+ appMatched = true;
|
|
|
|
+ matchedType = "appId";
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 检查pushBundle(bundle匹配)
|
|
|
|
+ if (!appMatched && globalRule.getPushBundle() != null && !globalRule.getPushBundle().isEmpty() && bundle != null) {
|
|
|
|
+ if (globalRule.getPushBundle().contains(bundle)) {
|
|
|
|
+ appMatched = true;
|
|
|
|
+ matchedType = "bundle";
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!appMatched) {
|
|
|
|
+ StringBuilder allowedApps = new StringBuilder();
|
|
|
|
+ if (globalRule.getPushApp() != null && !globalRule.getPushApp().isEmpty()) {
|
|
|
|
+ allowedApps.append("允许的appId:").append(globalRule.getPushApp().stream().map(PushAPPDTO::getAppId).toList());
|
|
|
|
+ }
|
|
|
|
+ if (globalRule.getPushBundle() != null && !globalRule.getPushBundle().isEmpty()) {
|
|
|
|
+ if (allowedApps.length() > 0) {
|
|
|
|
+ allowedApps.append(",");
|
|
|
|
+ }
|
|
|
|
+ allowedApps.append("允许的bundle:").append(globalRule.getPushBundle());
|
|
|
|
+ }
|
|
|
|
+ return "应用校验失败,bundle【" + bundle + "】和appId【" + appId + "】都不在允许的应用范围内," + allowedApps.toString();
|
|
|
|
+ }
|
|
|
|
+ log.info("应用校验通过,匹配类型:{},bundle:{},appId:{}", matchedType, bundle, appId);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 5. 校验域名
|
|
|
|
+ if (pushValidationConfig.getEnableDomainCheck() &&
|
|
|
|
+ !validateGlobalRuleDomain(globalRule.getDomain(), saveDTO.getPushDomain())) {
|
|
return "推送域名【" + saveDTO.getPushDomain() + "】不在允许的域名范围内";
|
|
return "推送域名【" + saveDTO.getPushDomain() + "】不在允许的域名范围内";
|
|
}
|
|
}
|
|
|
|
|
|
- // 4. 校验推送频率
|
|
|
|
- if (!PushFrequencyUtil.checkPushFrequency(globalRule.getPushFrequency(), 0L, saveDTO.getClientId(), redisTemplate)) {
|
|
|
|
|
|
+ // 6. 校验推送频率
|
|
|
|
+ if (pushValidationConfig.getEnableFrequencyCheck() &&
|
|
|
|
+ !PushFrequencyUtil.checkPushFrequency(globalRule.getPushFrequency(), 0L, saveDTO.getClientId(), redisTemplate)) {
|
|
return "推送频率检查未通过,推送频率:" + globalRule.getPushFrequency();
|
|
return "推送频率检查未通过,推送频率:" + globalRule.getPushFrequency();
|
|
}
|
|
}
|
|
|
|
|
|
- // 5. 构建推送记录
|
|
|
|
|
|
+ // 7. 构建推送记录
|
|
MktMgmtPushRecord pushRecord = buildHandPushRecord(saveDTO, globalRule);
|
|
MktMgmtPushRecord pushRecord = buildHandPushRecord(saveDTO, globalRule);
|
|
|
|
|
|
- // 6. 保存推送记录
|
|
|
|
|
|
+ // 8. 保存推送记录
|
|
int result = mktMgmtPushRecordMapper.insert(pushRecord);
|
|
int result = mktMgmtPushRecordMapper.insert(pushRecord);
|
|
if (result > 0) {
|
|
if (result > 0) {
|
|
- log.info("手动推送成功,推送内容:{}", saveDTO.getPushContent());
|
|
|
|
|
|
+ log.info("手动推送成功,推送内容:{},客户端ID:{}", saveDTO.getPushContent(), saveDTO.getClientId());
|
|
return "手动推送成功";
|
|
return "手动推送成功";
|
|
} else {
|
|
} else {
|
|
return "手动推送失败,数据库保存异常";
|
|
return "手动推送失败,数据库保存异常";
|
|
}
|
|
}
|
|
|
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
- log.error("手动推送异常,推送内容:{}", saveDTO.getPushContent(), e);
|
|
|
|
|
|
+ log.error("手动推送异常,推送内容:{},客户端ID:{}", saveDTO.getPushContent(), saveDTO.getClientId(), e);
|
|
return "手动推送异常:" + e.getMessage();
|
|
return "手动推送异常:" + e.getMessage();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -253,6 +352,7 @@ public class MktMgmtHandPushServiceImpl implements MktMgmtHandPushService {
|
|
pushRecord.setPushType(globalRule.getPushType());
|
|
pushRecord.setPushType(globalRule.getPushType());
|
|
pushRecord.setPushAction(globalRule.getAction());
|
|
pushRecord.setPushAction(globalRule.getAction());
|
|
pushRecord.setDelayPush(Integer.valueOf(globalRule.getDelayPush()));
|
|
pushRecord.setDelayPush(Integer.valueOf(globalRule.getDelayPush()));
|
|
|
|
+ pushRecord.setPushAddr(saveDTO.getPushIP());
|
|
pushRecord.setCreateTime(LocalDateTime.now());
|
|
pushRecord.setCreateTime(LocalDateTime.now());
|
|
pushRecord.setUpdateTime(LocalDateTime.now());
|
|
pushRecord.setUpdateTime(LocalDateTime.now());
|
|
|
|
|
|
@@ -293,4 +393,51 @@ public class MktMgmtHandPushServiceImpl implements MktMgmtHandPushService {
|
|
BeanUtils.copyProperties(record, vo);
|
|
BeanUtils.copyProperties(record, vo);
|
|
return vo;
|
|
return vo;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 检查地址是否匹配
|
|
|
|
+ *
|
|
|
|
+ * @param fullAddress 完整的三级地址,格式:国家-省-市
|
|
|
|
+ * @param allowedAddr 允许的地址配置
|
|
|
|
+ * @return 是否匹配
|
|
|
|
+ */
|
|
|
|
+ private boolean isAddressMatched(String fullAddress, String allowedAddr) {
|
|
|
|
+ if (fullAddress == null || allowedAddr == null) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 如果允许的地址配置为空,则不匹配
|
|
|
|
+ if (allowedAddr.trim().isEmpty()) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ String trimmedAllowedAddr = allowedAddr.trim();
|
|
|
|
+
|
|
|
|
+ // 1. 完全匹配
|
|
|
|
+ if (fullAddress.equals(trimmedAllowedAddr)) {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 2. 检查是否匹配国家级别(如果允许的地址只有国家)
|
|
|
|
+ if (!trimmedAllowedAddr.contains("-")) {
|
|
|
|
+ return fullAddress.startsWith(trimmedAllowedAddr + "-");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 3. 检查是否匹配国家-省级别
|
|
|
|
+ String[] allowedParts = trimmedAllowedAddr.split("-");
|
|
|
|
+ String[] fullParts = fullAddress.split("-");
|
|
|
|
+
|
|
|
|
+ if (allowedParts.length == 2 && fullParts.length >= 2) {
|
|
|
|
+ return allowedParts[0].equals(fullParts[0]) && allowedParts[1].equals(fullParts[1]);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 4. 检查是否匹配国家-省-市级别
|
|
|
|
+ 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;
|
|
|
|
+ }
|
|
}
|
|
}
|