|
|
|
|
|
|
|
|
|
|
|
package com.ruoyi.system.task; |
|
|
|
|
|
|
|
|
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
|
|
|
|
|
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; |
|
|
|
|
|
import com.ruoyi.system.domain.ZsOperationGoods; |
|
|
|
|
|
import com.ruoyi.system.domain.ZsOperationSendconfig; |
|
|
|
|
|
import com.ruoyi.system.domain.ZsOperationWarnconfig; |
|
|
|
|
|
import com.ruoyi.system.domain.ZsOperationWarnresult; |
|
|
|
|
|
import com.ruoyi.system.mapper.ZsOperationGoodsMapper; |
|
|
|
|
|
import com.ruoyi.system.mapper.ZsOperationSendconfigMapper; |
|
|
|
|
|
import com.ruoyi.system.mapper.ZsOperationWarnconfigMapper; |
|
|
|
|
|
import com.ruoyi.system.mapper.ZsOperationWarnresultMapper; |
|
|
|
|
|
import com.ruoyi.system.utils.EmailUtils; |
|
|
|
|
|
import com.ruoyi.system.utils.HttpUtil; |
|
|
|
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
|
|
|
import org.apache.commons.lang3.StringUtils; |
|
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired; |
|
|
|
|
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; |
|
|
|
|
|
import org.springframework.scheduling.support.CronTrigger; |
|
|
|
|
|
import org.springframework.stereotype.Component; |
|
|
|
|
|
import javax.annotation.PostConstruct; |
|
|
|
|
|
import java.math.BigDecimal; |
|
|
|
|
|
import java.math.RoundingMode; |
|
|
|
|
|
import java.time.LocalTime; |
|
|
|
|
|
import java.util.Date; |
|
|
|
|
|
import java.util.List; |
|
|
|
|
|
import java.util.Map; |
|
|
|
|
|
import java.util.concurrent.ScheduledFuture; |
|
|
|
|
|
|
|
|
|
|
|
@Component |
|
|
|
|
|
@Slf4j |
|
|
|
|
|
public class MonitorTaskScheduler { |
|
|
|
|
|
|
|
|
|
|
|
private final ThreadPoolTaskScheduler taskScheduler; |
|
|
|
|
|
private ScheduledFuture<?> scheduledTask; |
|
|
|
|
|
@Autowired |
|
|
|
|
|
private ZsOperationWarnconfigMapper zsOperationWarnconfigMapper; |
|
|
|
|
|
@Autowired |
|
|
|
|
|
private ZsOperationGoodsMapper zsOperationGoodsMapper; |
|
|
|
|
|
@Autowired |
|
|
|
|
|
private ZsOperationWarnresultMapper zsOperationWarnresultMapper; |
|
|
|
|
|
@Autowired |
|
|
|
|
|
private ZsOperationSendconfigMapper zsOperationSendconfigMapper; |
|
|
|
|
|
@Autowired |
|
|
|
|
|
private EmailUtils emailUtils; |
|
|
|
|
|
|
|
|
|
|
|
public MonitorTaskScheduler( ) { |
|
|
|
|
|
this.taskScheduler = new ThreadPoolTaskScheduler(); |
|
|
|
|
|
this.taskScheduler.setPoolSize(1); |
|
|
|
|
|
this.taskScheduler.initialize(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@PostConstruct |
|
|
|
|
|
public void init() { |
|
|
|
|
|
// 启动时从数据库加载初始配置 |
|
|
|
|
|
rescheduleTask(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//查询配置 并设置定时任务 |
|
|
|
|
|
public void rescheduleTask() { |
|
|
|
|
|
// 取消当前任务 |
|
|
|
|
|
if (scheduledTask != null) { |
|
|
|
|
|
scheduledTask.cancel(true); |
|
|
|
|
|
} |
|
|
|
|
|
// 根据新的频率重新调度 |
|
|
|
|
|
List<ZsOperationWarnconfig> zsOperationWarnconfigs = zsOperationWarnconfigMapper.selectList(); |
|
|
|
|
|
if (zsOperationWarnconfigs!=null&&zsOperationWarnconfigs.size()>0) { |
|
|
|
|
|
ZsOperationWarnconfig zsOperationWarnconfig = zsOperationWarnconfigs.get(0); |
|
|
|
|
|
String frequency = zsOperationWarnconfig.getMonitorFrequency(); |
|
|
|
|
|
//如果是null或者不是数字 默认为1小时 |
|
|
|
|
|
if (StringUtils.isBlank(frequency)||!frequency.matches("\\d+")) { |
|
|
|
|
|
frequency = "1"; |
|
|
|
|
|
} |
|
|
|
|
|
String cron = convertFrequencyToCron(Integer.parseInt(frequency)); |
|
|
|
|
|
// |
|
|
|
|
|
scheduledTask = taskScheduler.schedule( |
|
|
|
|
|
() -> monitorWarn(zsOperationWarnconfig), |
|
|
|
|
|
new CronTrigger(cron) |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//比对商品价格编号是否超过了阈值 并记录 |
|
|
|
|
|
public void monitorWarn(ZsOperationWarnconfig zsOperationWarnconfig) { |
|
|
|
|
|
//1.查询启用的商品 |
|
|
|
|
|
QueryWrapper<ZsOperationGoods> queryWrapper = new QueryWrapper<>(); |
|
|
|
|
|
queryWrapper.eq("is_disabled",1); |
|
|
|
|
|
List<ZsOperationGoods> zsOperationGoodsList =zsOperationGoodsMapper.selectList(queryWrapper); |
|
|
|
|
|
//2.遍历商品 |
|
|
|
|
|
if (zsOperationGoodsList!=null&&zsOperationGoodsList.size()>0) { |
|
|
|
|
|
for (ZsOperationGoods zsOperationGoods: zsOperationGoodsList) { |
|
|
|
|
|
String curUrl = zsOperationGoods.getGoodsSkuUrl(); |
|
|
|
|
|
//3.根据url 获取商品最新信息 |
|
|
|
|
|
Map<String, Object> reMap = HttpUtil.sendGetRequest(curUrl,true,zsOperationGoods.getPlatform()); |
|
|
|
|
|
if ((boolean)reMap.get("success")) { |
|
|
|
|
|
List dataList = (List)reMap.get("data"); |
|
|
|
|
|
Map dataMap = (Map)dataList.get(0); |
|
|
|
|
|
String newName = (String)dataMap.get("title"); |
|
|
|
|
|
String newSn = (String)dataMap.get("sku"); |
|
|
|
|
|
String newRemark = (String)dataMap.get("remark"); |
|
|
|
|
|
zsOperationGoods.setGoodsSkuName(newName); |
|
|
|
|
|
zsOperationGoods.setGoodsSkuSn(newSn); |
|
|
|
|
|
zsOperationGoods.setRemark(newRemark); |
|
|
|
|
|
//4.更新商品信息 |
|
|
|
|
|
zsOperationGoodsMapper.update(zsOperationGoods,new UpdateWrapper<>()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
String newPriceStr = (String)dataMap.get("price"); |
|
|
|
|
|
if (StringUtils.isNotBlank(newPriceStr)) { |
|
|
|
|
|
newPriceStr =newPriceStr.replace(",","").trim(); |
|
|
|
|
|
BigDecimal newPrice = new BigDecimal(newPriceStr); |
|
|
|
|
|
//如果新价格大于0 |
|
|
|
|
|
if (newPrice.compareTo(BigDecimal.ZERO) > 0) { |
|
|
|
|
|
//获取基准价格 |
|
|
|
|
|
BigDecimal initPrice = zsOperationGoods.getInitPrice(); |
|
|
|
|
|
//5. 获取基准价格和最新价格的差值比例 |
|
|
|
|
|
BigDecimal percentageChange= getChangeThreshold(initPrice,newPrice); |
|
|
|
|
|
//我们设置的预警阈值 |
|
|
|
|
|
BigDecimal priceChange = new BigDecimal(zsOperationWarnconfig.getPriceChangeThreshold()); |
|
|
|
|
|
// 6.比较差值比例是否大于等于我们设置的预警阈值 |
|
|
|
|
|
if (percentageChange.compareTo(priceChange) >= 0) { |
|
|
|
|
|
//7.生成 预警结果数据 |
|
|
|
|
|
String warnTypes = zsOperationWarnconfig.getWarnTypes(); |
|
|
|
|
|
ZsOperationWarnresult zsOperationWarnresult = new ZsOperationWarnresult(); |
|
|
|
|
|
zsOperationWarnresult.setWarnTime(new Date()); |
|
|
|
|
|
zsOperationWarnresult.setGoodsSkuName(zsOperationGoods.getGoodsSkuName()); |
|
|
|
|
|
zsOperationWarnresult.setGoodsSkuSn(zsOperationGoods.getGoodsSkuSn()); |
|
|
|
|
|
zsOperationWarnresult.setInitPrice(initPrice); |
|
|
|
|
|
zsOperationWarnresult.setCurPrice(newPrice); |
|
|
|
|
|
zsOperationWarnresult.setPriceChangeRatio(percentageChange.toString()); |
|
|
|
|
|
zsOperationWarnresult.setWarnTypes(warnTypes); |
|
|
|
|
|
String warnContent = "商品编号:"+newSn+"价格发生变动!"+ |
|
|
|
|
|
"原价格=:"+initPrice+"新价格=:"+newPrice.doubleValue(); |
|
|
|
|
|
zsOperationWarnresult.setWarnContent(warnContent); |
|
|
|
|
|
zsOperationWarnresult.setSnapshotUrl((String)dataMap.get("screenshotUrl")); |
|
|
|
|
|
zsOperationWarnresult.setStatus(0); |
|
|
|
|
|
zsOperationWarnresultMapper.insert(zsOperationWarnresult); |
|
|
|
|
|
|
|
|
|
|
|
//8.发送邮件 |
|
|
|
|
|
QueryWrapper<ZsOperationSendconfig> sendWrapper= new QueryWrapper<>(); |
|
|
|
|
|
sendWrapper.eq("module",0); |
|
|
|
|
|
sendWrapper.in("code",warnTypes.split(",")); |
|
|
|
|
|
//根据预警编码 查询出对应的预警信息接收地址 |
|
|
|
|
|
List<ZsOperationSendconfig> zsOperationSendconfigList = zsOperationSendconfigMapper.selectList(sendWrapper); |
|
|
|
|
|
if (zsOperationSendconfigList!=null&&zsOperationSendconfigList.size()>0) { |
|
|
|
|
|
for (ZsOperationSendconfig zsOperationSendconfig : zsOperationSendconfigList) { |
|
|
|
|
|
String receiveEmailStr = zsOperationSendconfig.getConfigValue(); |
|
|
|
|
|
String[] receiveEmails = receiveEmailStr.split(","); |
|
|
|
|
|
for (String receiveEmail : receiveEmails) { |
|
|
|
|
|
//发送数据 1立刻发送 0 9:00-18:00 发送 |
|
|
|
|
|
String warnTimeRange = zsOperationWarnconfig.getWarnTimeRange(); |
|
|
|
|
|
if ("1".equals(warnTimeRange)) { |
|
|
|
|
|
emailUtils.sendMailByTransport(zsOperationSendconfig.getSendEmail(),receiveEmail,"商品价格变化预警",warnContent,zsOperationSendconfig.getSmtpId()); |
|
|
|
|
|
} else { |
|
|
|
|
|
if (isBetween9And18()) { |
|
|
|
|
|
emailUtils.sendMailByTransport(zsOperationSendconfig.getSendEmail(),receiveEmail,"商品价格变化预警",warnContent,zsOperationSendconfig.getSmtpId()); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
log.info("商品同步数据失败! curUrl="+curUrl+",reMap="+reMap.toString()); |
|
|
|
|
|
//7.生成 预警结果数据 |
|
|
|
|
|
String warnTypes = zsOperationWarnconfig.getWarnTypes(); |
|
|
|
|
|
ZsOperationWarnresult zsOperationWarnresult = new ZsOperationWarnresult(); |
|
|
|
|
|
zsOperationWarnresult.setWarnTime(new Date()); |
|
|
|
|
|
zsOperationWarnresult.setGoodsSkuName(zsOperationGoods.getGoodsSkuName()); |
|
|
|
|
|
zsOperationWarnresult.setGoodsSkuSn(zsOperationGoods.getGoodsSkuSn()); |
|
|
|
|
|
zsOperationWarnresult.setInitPrice(zsOperationGoods.getInitPrice()); |
|
|
|
|
|
zsOperationWarnresult.setCurPrice(null); |
|
|
|
|
|
zsOperationWarnresult.setPriceChangeRatio(""); |
|
|
|
|
|
zsOperationWarnresult.setWarnTypes(warnTypes); |
|
|
|
|
|
String warnContent = "商品编号:"+zsOperationGoods.getGoodsSkuSn()+"抓取失败!"; |
|
|
|
|
|
zsOperationWarnresult.setWarnContent(warnContent); |
|
|
|
|
|
zsOperationWarnresult.setStatus(0); |
|
|
|
|
|
zsOperationWarnresultMapper.insert(zsOperationWarnresult); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//生成定时任务cron表达式 |
|
|
|
|
|
private String convertFrequencyToCron(int frequency) { |
|
|
|
|
|
// 将频率转换为Cron表达式,例如1小时一次:"0 0 0/1 * * ?" |
|
|
|
|
|
return "0 0 0/" + frequency + " * * ?"; |
|
|
|
|
|
} |
|
|
|
|
|
/** |
|
|
|
|
|
* 判断价格变化是否超过阈值 |
|
|
|
|
|
* @param curPrice 当前价格(基准价) |
|
|
|
|
|
* @param newPrice 新价格 |
|
|
|
|
|
* @return 变化幅度超过阈值返回true,否则false |
|
|
|
|
|
*/ |
|
|
|
|
|
public BigDecimal getChangeThreshold(BigDecimal curPrice,BigDecimal newPrice) { |
|
|
|
|
|
// 参数校验 |
|
|
|
|
|
if (curPrice == null || newPrice == null ) { |
|
|
|
|
|
throw new IllegalArgumentException("参数不能为空"); |
|
|
|
|
|
} |
|
|
|
|
|
// 计算价格变化绝对值 |
|
|
|
|
|
BigDecimal diff = newPrice.subtract(curPrice).abs(); |
|
|
|
|
|
// 计算百分比变化(保留4位小数) |
|
|
|
|
|
BigDecimal percentageChange = diff.divide(curPrice, 4, RoundingMode.HALF_UP) |
|
|
|
|
|
.multiply(new BigDecimal("100")); |
|
|
|
|
|
return percentageChange; |
|
|
|
|
|
} |
|
|
|
|
|
//判断是否是9-18点 |
|
|
|
|
|
public boolean isBetween9And18() { |
|
|
|
|
|
LocalTime currentTime = LocalTime.now(); |
|
|
|
|
|
LocalTime startTime = LocalTime.of(9, 0); // 9:00 |
|
|
|
|
|
LocalTime endTime = LocalTime.of(18, 0); // 18:00 |
|
|
|
|
|
// 判断当前时间是否在 [9:00, 18:00] 区间(包含边界) |
|
|
|
|
|
return !currentTime.isBefore(startTime) && !currentTime.isAfter(endTime); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |