|
@@ -0,0 +1,287 @@
|
|
|
+package com.pig4cloud.pig.marketing.service.impl;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
+import com.pig4cloud.pig.marketing.api.dto.MktMgmtHandPushQueryDTO;
|
|
|
+import com.pig4cloud.pig.marketing.api.dto.MktMgmtPushRecordSaveDTO;
|
|
|
+import com.pig4cloud.pig.marketing.api.entity.MktMgmtPushRecord;
|
|
|
+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.mapper.MktMgmtPushRecordMapper;
|
|
|
+import com.pig4cloud.pig.marketing.util.PushFrequencyUtil;
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.beans.BeanUtils;
|
|
|
+import org.springframework.data.redis.core.RedisTemplate;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.util.StringUtils;
|
|
|
+
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 手动推送服务实现类
|
|
|
+ *
|
|
|
+ * @author pig4cloud
|
|
|
+ * @date 2025-01-20
|
|
|
+ * @description 手动推送相关业务实现
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+@Service
|
|
|
+@RequiredArgsConstructor
|
|
|
+public class MktMgmtHandPushServiceImpl implements MktMgmtHandPushService {
|
|
|
+
|
|
|
+ private final MktMgmtPushRecordMapper mktMgmtPushRecordMapper;
|
|
|
+ private final MarketingConfigService marketingConfigService;
|
|
|
+ private final RedisTemplate<String, Object> redisTemplate;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Page<HandPushVO> pageQuery(MktMgmtHandPushQueryDTO queryDTO) {
|
|
|
+ Page<MktMgmtPushRecord> page = new Page<>(queryDTO.getPageNum(), queryDTO.getPageSize());
|
|
|
+
|
|
|
+ LambdaQueryWrapper<MktMgmtPushRecord> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
+ queryWrapper.eq(MktMgmtPushRecord::getDelFlag, "0");
|
|
|
+ queryWrapper.eq(MktMgmtPushRecord::getAutoPush, 1);
|
|
|
+
|
|
|
+ // 根据时间范围查询
|
|
|
+ if (StringUtils.hasText(queryDTO.getStartTime())) {
|
|
|
+ LocalDateTime startTime = LocalDateTime.parse(queryDTO.getStartTime() + " 00:00:00",
|
|
|
+ DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
|
|
+ queryWrapper.ge(MktMgmtPushRecord::getCreateTime, startTime);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (StringUtils.hasText(queryDTO.getEndTime())) {
|
|
|
+ LocalDateTime endTime = LocalDateTime.parse(queryDTO.getEndTime() + " 23:59:59",
|
|
|
+ DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
|
|
+ queryWrapper.le(MktMgmtPushRecord::getCreateTime, endTime);
|
|
|
+ }
|
|
|
+
|
|
|
+ queryWrapper.orderByDesc(MktMgmtPushRecord::getCreateTime);
|
|
|
+
|
|
|
+ Page<MktMgmtPushRecord> recordPage = mktMgmtPushRecordMapper.selectPage(page, queryWrapper);
|
|
|
+
|
|
|
+ // 转换为HandPushVO
|
|
|
+ Page<HandPushVO> voPage = new Page<>(recordPage.getCurrent(), recordPage.getSize(), recordPage.getTotal());
|
|
|
+ List<HandPushVO> voList = recordPage.getRecords().stream()
|
|
|
+ .map(this::convertToHandPushVO)
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ voPage.setRecords(voList);
|
|
|
+
|
|
|
+ return voPage;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String handPush(MktMgmtPushRecordSaveDTO saveDTO) {
|
|
|
+ try {
|
|
|
+ log.info("开始手动推送,推送内容:{}", saveDTO.getPushContent());
|
|
|
+
|
|
|
+ // 1. 获取全局手动推送规则
|
|
|
+ SaveGlobalRuleDTO globalRule = marketingConfigService.getGlobalRule();
|
|
|
+ if (globalRule == null) {
|
|
|
+ return "获取全局手动推送规则失败";
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 校验IP
|
|
|
+ if (!validateGlobalRuleIP(globalRule.getIp(), saveDTO.getPushIP())) {
|
|
|
+ return "推送IP【" + saveDTO.getPushIP() + "】不在允许的IP范围内";
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 校验域名
|
|
|
+ if (!validateGlobalRuleDomain(globalRule.getDomain(), saveDTO.getPushDomain())) {
|
|
|
+ return "推送域名【" + saveDTO.getPushDomain() + "】不在允许的域名范围内";
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 校验推送频率
|
|
|
+ if (!PushFrequencyUtil.checkPushFrequency(globalRule.getPushFrequency(), 0L, saveDTO.getPushContent(), redisTemplate)) {
|
|
|
+ return "推送频率检查未通过,推送频率:" + globalRule.getPushFrequency();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 构建推送记录
|
|
|
+ MktMgmtPushRecord pushRecord = buildHandPushRecord(saveDTO, globalRule);
|
|
|
+
|
|
|
+ // 6. 保存推送记录
|
|
|
+ int result = mktMgmtPushRecordMapper.insert(pushRecord);
|
|
|
+ if (result > 0) {
|
|
|
+ log.info("手动推送成功,推送内容:{}", saveDTO.getPushContent());
|
|
|
+ return "手动推送成功";
|
|
|
+ } else {
|
|
|
+ return "手动推送失败,数据库保存异常";
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("手动推送异常,推送内容:{}", saveDTO.getPushContent(), e);
|
|
|
+ return "手动推送异常:" + e.getMessage();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 校验全局规则IP
|
|
|
+ *
|
|
|
+ * @param allowedIPs 允许的IP列表
|
|
|
+ * @param pushIP 推送IP
|
|
|
+ * @return 是否匹配
|
|
|
+ */
|
|
|
+ private boolean validateGlobalRuleIP(List<String> allowedIPs, String pushIP) {
|
|
|
+ if (allowedIPs == null || allowedIPs.isEmpty()) {
|
|
|
+ return true; // 如果没有配置IP限制,则允许
|
|
|
+ }
|
|
|
+
|
|
|
+ if (pushIP == null || pushIP.trim().isEmpty()) {
|
|
|
+ return false; // 推送IP为空,不允许
|
|
|
+ }
|
|
|
+
|
|
|
+ for (String allowedIP : allowedIPs) {
|
|
|
+ if (allowedIP.contains("/")) {
|
|
|
+ // IP段格式:192.168.10.101/192.168.10.110
|
|
|
+ String[] ipRange = allowedIP.split("/");
|
|
|
+ if (ipRange.length == 2) {
|
|
|
+ String startIP = ipRange[0].trim();
|
|
|
+ String endIP = ipRange[1].trim();
|
|
|
+ if (isIPInRange(pushIP, startIP, endIP)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 单个IP格式:192.168.10.102
|
|
|
+ if (pushIP.equals(allowedIP.trim())) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 校验全局规则域名
|
|
|
+ *
|
|
|
+ * @param allowedDomains 允许的域名列表
|
|
|
+ * @param pushDomain 推送域名
|
|
|
+ * @return 是否匹配
|
|
|
+ */
|
|
|
+ private boolean validateGlobalRuleDomain(List<String> allowedDomains, String pushDomain) {
|
|
|
+ if (allowedDomains == null || allowedDomains.isEmpty()) {
|
|
|
+ return true; // 如果没有配置域名限制,则允许
|
|
|
+ }
|
|
|
+
|
|
|
+ if (pushDomain == null || pushDomain.trim().isEmpty()) {
|
|
|
+ return false; // 推送域名为空,不允许
|
|
|
+ }
|
|
|
+
|
|
|
+ for (String allowedDomain : allowedDomains) {
|
|
|
+ if (pushDomain.equals(allowedDomain.trim())) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断IP是否在指定范围内
|
|
|
+ *
|
|
|
+ * @param ip 要检查的IP
|
|
|
+ * @param startIP 起始IP
|
|
|
+ * @param endIP 结束IP
|
|
|
+ * @return 是否在范围内
|
|
|
+ */
|
|
|
+ private boolean isIPInRange(String ip, String startIP, String endIP) {
|
|
|
+ try {
|
|
|
+ long ipLong = ipToLong(ip);
|
|
|
+ long startIPLong = ipToLong(startIP);
|
|
|
+ long endIPLong = ipToLong(endIP);
|
|
|
+ return ipLong >= startIPLong && ipLong <= endIPLong;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("IP范围检查异常,IP:{},起始IP:{},结束IP:{}", ip, startIP, endIP, e);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将IP地址转换为Long类型
|
|
|
+ *
|
|
|
+ * @param ip IP地址
|
|
|
+ * @return Long值
|
|
|
+ */
|
|
|
+ private long ipToLong(String ip) {
|
|
|
+ String[] parts = ip.split("\\.");
|
|
|
+ if (parts.length != 4) {
|
|
|
+ throw new IllegalArgumentException("Invalid IP address: " + ip);
|
|
|
+ }
|
|
|
+
|
|
|
+ long result = 0;
|
|
|
+ for (int i = 0; i < 4; i++) {
|
|
|
+ int part = Integer.parseInt(parts[i]);
|
|
|
+ if (part < 0 || part > 255) {
|
|
|
+ throw new IllegalArgumentException("Invalid IP address: " + ip);
|
|
|
+ }
|
|
|
+ result = result * 256 + part;
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建手动推送记录
|
|
|
+ *
|
|
|
+ * @param saveDTO 保存参数
|
|
|
+ * @param globalRule 全局规则信息
|
|
|
+ * @return 推送记录
|
|
|
+ */
|
|
|
+ private MktMgmtPushRecord buildHandPushRecord(MktMgmtPushRecordSaveDTO saveDTO, SaveGlobalRuleDTO globalRule) {
|
|
|
+ MktMgmtPushRecord pushRecord = new MktMgmtPushRecord();
|
|
|
+
|
|
|
+ // 基本信息
|
|
|
+ pushRecord.setRuleId(0L); // 全局规则ID为0
|
|
|
+ pushRecord.setRuleName("全局手动推送规则");
|
|
|
+ pushRecord.setPushContent(globalRule.getPushContent());
|
|
|
+ pushRecord.setPushIP(saveDTO.getPushIP());
|
|
|
+ pushRecord.setPushDomain(saveDTO.getPushDomain());
|
|
|
+ pushRecord.setAutoPush(1);
|
|
|
+ pushRecord.setPushStatus(true);
|
|
|
+ pushRecord.setCreateTime(LocalDateTime.now());
|
|
|
+ pushRecord.setUpdateTime(LocalDateTime.now());
|
|
|
+
|
|
|
+ // 构建触发条件JSON
|
|
|
+ Map<String, Object> triggerCondition = new HashMap<>();
|
|
|
+ triggerCondition.put("type", "manual");
|
|
|
+ triggerCondition.put("ruleId", 0L);
|
|
|
+ triggerCondition.put("ruleName", "全局手动推送规则");
|
|
|
+ triggerCondition.put("pushFrequency", globalRule.getPushFrequency());
|
|
|
+ triggerCondition.put("allowedIPs", globalRule.getIp());
|
|
|
+ triggerCondition.put("allowedDomains", globalRule.getDomain());
|
|
|
+ pushRecord.setTriggerCondition(triggerCondition.toString());
|
|
|
+
|
|
|
+ // 构建推送详情JSON
|
|
|
+ Map<String, Object> pushDetail = new HashMap<>();
|
|
|
+ pushDetail.put("pushType", "manual");
|
|
|
+ pushDetail.put("pushAction", globalRule.getAction());
|
|
|
+ pushDetail.put("pushContent", saveDTO.getPushContent());
|
|
|
+ pushDetail.put("pushIP", saveDTO.getPushIP());
|
|
|
+ pushDetail.put("pushDomain", saveDTO.getPushDomain());
|
|
|
+ pushDetail.put("pushTime", LocalDateTime.now().toString());
|
|
|
+ pushDetail.put("delayPush", globalRule.getDelayPush());
|
|
|
+ pushRecord.setPushDetail(pushDetail.toString());
|
|
|
+
|
|
|
+ return pushRecord;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 转换为HandPushVO
|
|
|
+ *
|
|
|
+ * @param record 推送记录
|
|
|
+ * @return HandPushVO
|
|
|
+ */
|
|
|
+ private HandPushVO convertToHandPushVO(MktMgmtPushRecord record) {
|
|
|
+ HandPushVO vo = new HandPushVO();
|
|
|
+ BeanUtils.copyProperties(record, vo);
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+}
|