自动添加xxl任务;
This commit is contained in:
@@ -116,6 +116,11 @@
|
|||||||
<artifactId>xxl-job-core</artifactId>
|
<artifactId>xxl-job-core</artifactId>
|
||||||
<version>2.4.0</version>
|
<version>2.4.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jsoup</groupId>
|
||||||
|
<artifactId>jsoup</artifactId>
|
||||||
|
<version>1.17.2</version>
|
||||||
|
</dependency>
|
||||||
<!-- JPush推送 -->
|
<!-- JPush推送 -->
|
||||||
<!-- jpush极光推送导入start -->
|
<!-- jpush极光推送导入start -->
|
||||||
<!-- <dependency>-->
|
<!-- <dependency>-->
|
||||||
|
|||||||
+30
-12
@@ -98,7 +98,8 @@ public class ContractManageApplicationService {
|
|||||||
private ContractManageParametersMapper contractManageParametersMapper;
|
private ContractManageParametersMapper contractManageParametersMapper;
|
||||||
@Autowired
|
@Autowired
|
||||||
private IContractManageParametersService contractManageParametersService;
|
private IContractManageParametersService contractManageParametersService;
|
||||||
|
@Resource
|
||||||
|
private XxlJobOpenApiUtil apiUtil;
|
||||||
@Value("${xxl.job.admin.addresses}")
|
@Value("${xxl.job.admin.addresses}")
|
||||||
private String adminAddresses;
|
private String adminAddresses;
|
||||||
private static final String token = "Bearer eyJhbGciOiJIUzUxMiJ9.eyJ1c2VyX2lkIjozNTYzLCJ0b3BPcmdhbml6YXRpb25JZCI6MjgyNywidXNlcl9rZXkiOiI0N2MzY2U4My0wMmFjLTQ0NTQtOTdiYS1lYmQwMDc2MzNiNDMiLCJ1c2VybmFtZSI6ImNhbmdtYSJ9.tnZyywwburfBo3Kizdhfk5eYdXbrrZ9976Z0W7GrS6UqSpKwfI2RwdRwfYx7DbD7Ri1Fdj_eOVxBkjvdFF226Q";
|
private static final String token = "Bearer eyJhbGciOiJIUzUxMiJ9.eyJ1c2VyX2lkIjozNTYzLCJ0b3BPcmdhbml6YXRpb25JZCI6MjgyNywidXNlcl9rZXkiOiI0N2MzY2U4My0wMmFjLTQ0NTQtOTdiYS1lYmQwMDc2MzNiNDMiLCJ1c2VybmFtZSI6ImNhbmdtYSJ9.tnZyywwburfBo3Kizdhfk5eYdXbrrZ9976Z0W7GrS6UqSpKwfI2RwdRwfYx7DbD7Ri1Fdj_eOVxBkjvdFF226Q";
|
||||||
@@ -984,27 +985,44 @@ public class ContractManageApplicationService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建夜间23点定时任务
|
||||||
|
* @param shipperId 结算对象id,作为任务参数
|
||||||
|
* @return xxlJobId 保存到业务表,后续用于删除/暂停
|
||||||
|
*/
|
||||||
public Integer addXxlJob(String shipperId) {
|
public Integer addXxlJob(String shipperId) {
|
||||||
//1.实例化工具类,建议交给Spring管理
|
|
||||||
XxlJobOpenApiUtil apiUtil = new XxlJobOpenApiUtil(adminAddresses,token);
|
|
||||||
|
|
||||||
//2.构建新增任务参数
|
|
||||||
XxlJobOpenApiUtil.JobInfoAddReq req = new XxlJobOpenApiUtil.JobInfoAddReq();
|
XxlJobOpenApiUtil.JobInfoAddReq req = new XxlJobOpenApiUtil.JobInfoAddReq();
|
||||||
req.setJobGroup(5); //你的执行器组id
|
req.setJobGroup(5);
|
||||||
req.setJobDesc("整租推送计费单据/流水任务,结算对象id:" + shipperId);
|
req.setJobDesc("整租推送计费单据/流水任务,结算对象id:" + shipperId);
|
||||||
req.setJobCron("0 0 1 * * ?");
|
req.setAuthor("system");
|
||||||
|
req.setAlarmEmail("");
|
||||||
|
req.setScheduleType("CRON");
|
||||||
|
// 每天晚上23点执行
|
||||||
|
req.setScheduleConf("0 0 1 * * ?");
|
||||||
|
req.setGlueType("BEAN");
|
||||||
req.setExecutorHandler("pushContractManageToBms");
|
req.setExecutorHandler("pushContractManageToBms");
|
||||||
req.setExecutorParam(shipperId); //传入业务单号
|
req.setExecutorParam(shipperId);
|
||||||
|
req.setGlueRemark("");
|
||||||
|
req.setGlueSource("");
|
||||||
|
req.setExecutorRouteStrategy("FIRST");
|
||||||
|
req.setChildJobId(null);
|
||||||
|
req.setMisfireStrategy("DO_NOTHING");
|
||||||
|
req.setExecutorBlockStrategy("SERIAL_EXECUTION");
|
||||||
|
req.setExecutorTimeout(0);
|
||||||
|
req.setExecutorFailRetryCount(0);
|
||||||
|
|
||||||
//3.调用创建任务接口 ,返回任务ID
|
boolean addOk = apiUtil.addJob(req);
|
||||||
Integer xxlJobId = apiUtil.addJob(req);
|
if (!addOk) {
|
||||||
if(xxlJobId == null){
|
|
||||||
// 创建定时任务失败,抛异常/重试,不要让单据直接保存成功
|
|
||||||
throw new RuntimeException("创建夜间定时任务失败");
|
throw new RuntimeException("创建夜间定时任务失败");
|
||||||
}
|
}
|
||||||
|
Integer xxlJobId = apiUtil.queryJobIdByDescAndParam(req.getJobDesc(), shipperId);
|
||||||
|
if (xxlJobId == null) {
|
||||||
|
throw new RuntimeException("定时任务创建成功,但查询jobId失败");
|
||||||
|
}
|
||||||
return xxlJobId;
|
return xxlJobId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void stopXxlJob(Integer shipperId) {
|
public void stopXxlJob(Integer shipperId) {
|
||||||
//1.实例化工具类,建议交给Spring管理
|
//1.实例化工具类,建议交给Spring管理
|
||||||
XxlJobOpenApiUtil apiUtil = new XxlJobOpenApiUtil(adminAddresses,token);
|
XxlJobOpenApiUtil apiUtil = new XxlJobOpenApiUtil(adminAddresses,token);
|
||||||
|
|||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
package com.mhd.basic.domain.contractManage.repository.util;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class XxlJobBusinessConfig {
|
||||||
|
|
||||||
|
@Value("${xxl.job.admin.address}")
|
||||||
|
private String adminAddress;
|
||||||
|
|
||||||
|
@Value("${xxl.job.admin.cookie}")
|
||||||
|
private String adminCookie;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public XxlJobOpenApiUtil xxlJobOpenApiUtil() {
|
||||||
|
return new XxlJobOpenApiUtil(adminAddress, adminCookie);
|
||||||
|
}
|
||||||
|
}
|
||||||
+130
-84
@@ -1,138 +1,184 @@
|
|||||||
package com.mhd.basic.domain.contractManage.repository.util;
|
package com.mhd.basic.domain.contractManage.repository.util;
|
||||||
|
|
||||||
import cn.hutool.core.bean.BeanUtil;
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.http.HttpRequest;
|
import cn.hutool.http.HttpRequest;
|
||||||
import cn.hutool.http.HttpResponse;
|
import cn.hutool.http.HttpResponse;
|
||||||
import cn.hutool.json.JSONUtil;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* XXL‑JOB OpenApi 工具类
|
* 调用XXL‑JOB后台页面接口(抓包获取 /jobinfo/*)
|
||||||
|
* 鉴权依赖JSESSIONID Cookie,非官方openapi;老版本xxl‑job无新增任务openapi
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class XxlJobOpenApiUtil {
|
public class XxlJobOpenApiUtil {
|
||||||
|
|
||||||
/** xxl‑job admin地址,例:http://127.0.0.1:8080/xxl‑job‑admin */
|
|
||||||
private final String adminUrl;
|
private final String adminUrl;
|
||||||
/** 系统配置里的AccessToken */
|
private final String cookie;
|
||||||
private final String accessToken;
|
|
||||||
|
|
||||||
public XxlJobOpenApiUtil(String adminUrl, String accessToken) {
|
/**
|
||||||
this.adminUrl = adminUrl.endsWith("/") ? adminUrl.substring(0, adminUrl.length() -1) : adminUrl;
|
* @param adminUrl xxl‑job admin地址 示例:http://127.0.0.1:8080/xxl-job-admin
|
||||||
this.accessToken = accessToken;
|
* @param cookie 浏览器登录后的完整Cookie(JSESSIONID=xxx)
|
||||||
|
*/
|
||||||
|
public XxlJobOpenApiUtil(String adminUrl, String cookie) {
|
||||||
|
this.adminUrl = StrUtil.removeSuffix(adminUrl, "/");
|
||||||
|
this.cookie = cookie;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增定时任务
|
* 新增定时任务 POST /jobinfo/add
|
||||||
* @param addReq 请求参数
|
* @param addReq 请求实体,与浏览器payload字段完全对齐
|
||||||
* @return jobId 任务ID,返回null代表调用失败
|
* @return true=新增成功,false=失败
|
||||||
*/
|
*/
|
||||||
public Integer addJob(JobInfoAddReq addReq) {
|
public boolean addJob(JobInfoAddReq addReq) {
|
||||||
String url = adminUrl + "/api/jobinfo/add";
|
String url = adminUrl + "/jobinfo/add";
|
||||||
try {
|
try {
|
||||||
// 实体对象转map,解决form不能传bean的爆红
|
|
||||||
Map<String, Object> formMap = BeanUtil.beanToMap(addReq);
|
Map<String, Object> formMap = BeanUtil.beanToMap(addReq);
|
||||||
|
|
||||||
HttpResponse response = HttpRequest.post(url)
|
HttpResponse response = HttpRequest.post(url)
|
||||||
.header("Authorization", accessToken)
|
.header("Cookie", cookie)
|
||||||
.form(formMap) //传入map,不再传实体
|
.form(formMap)
|
||||||
.execute();
|
.execute();
|
||||||
|
int status = response.getStatus();
|
||||||
|
log.info("[XxlJob] addJob status:{}", status);
|
||||||
|
// 新增成功302重定向到列表页
|
||||||
|
return status == 302;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[XxlJob] addJob异常", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String body = response.body();
|
/**
|
||||||
log.info("xxl‑job addJob resp:{}", body);
|
* 根据【任务描述+执行器参数】查询jobId
|
||||||
XxlApiResp resp = JSONUtil.toBean(body, XxlApiResp.class);
|
* @param jobDesc 任务描述
|
||||||
if (resp != null && resp.getCode() == 200) {
|
* @param executorParam 任务参数(业务shipperId)
|
||||||
return Integer.valueOf(resp.getContent().toString());
|
* @return jobId,查不到返回null
|
||||||
|
*/
|
||||||
|
public Integer queryJobIdByDescAndParam(String jobDesc, String executorParam) {
|
||||||
|
String url = adminUrl + "/jobinfo/pageList";
|
||||||
|
try {
|
||||||
|
HttpResponse response = HttpRequest.post(url)
|
||||||
|
.header("Cookie", cookie)
|
||||||
|
.form("jobGroup", "")
|
||||||
|
.form("jobDesc", jobDesc)
|
||||||
|
.form("executorHandler", "")
|
||||||
|
.form("executorParam", executorParam)
|
||||||
|
.form("triggerStatus", "-1")
|
||||||
|
.execute();
|
||||||
|
String html = response.body();
|
||||||
|
if (StrUtil.isBlank(html)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Document doc = Jsoup.parse(html);
|
||||||
|
Elements trList = doc.select("tr");
|
||||||
|
for (Element tr : trList) {
|
||||||
|
String dataId = tr.attr("data-id");
|
||||||
|
if (StrUtil.isBlank(dataId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return Integer.parseInt(dataId);
|
||||||
}
|
}
|
||||||
log.error("新增xxl‑job任务失败,resp:{}", body);
|
|
||||||
return null;
|
return null;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("调用xxl‑job addJob异常", e);
|
log.error("[XxlJob] queryJobId异常", e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 暂停任务 POST /jobinfo/pause
|
||||||
|
* @param jobId xxl任务id
|
||||||
|
* @return true成功
|
||||||
|
*/
|
||||||
|
public boolean pauseJob(Integer jobId) {
|
||||||
|
if (jobId == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String url = adminUrl + "/jobinfo/pause";
|
||||||
|
try {
|
||||||
|
HttpResponse response = HttpRequest.post(url)
|
||||||
|
.header("Cookie", cookie)
|
||||||
|
.form("id", jobId)
|
||||||
|
.execute();
|
||||||
|
return response.getStatus() == 302;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[XxlJob] pauseJob jobId:{}异常", jobId, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除任务
|
* 开启任务 POST /jobinfo/start
|
||||||
* @param jobId 任务id
|
* @param jobId xxl任务id
|
||||||
|
* @return true成功
|
||||||
|
*/
|
||||||
|
public boolean startJob(Integer jobId) {
|
||||||
|
if (jobId == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String url = adminUrl + "/jobinfo/start";
|
||||||
|
try {
|
||||||
|
HttpResponse response = HttpRequest.post(url)
|
||||||
|
.header("Cookie", cookie)
|
||||||
|
.form("id", jobId)
|
||||||
|
.execute();
|
||||||
|
return response.getStatus() == 302;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[XxlJob] startJob jobId:{}异常", jobId, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除任务 POST /jobinfo/remove
|
||||||
|
* @param jobId xxl任务id
|
||||||
* @return true成功
|
* @return true成功
|
||||||
*/
|
*/
|
||||||
public boolean removeJob(Integer jobId) {
|
public boolean removeJob(Integer jobId) {
|
||||||
String url = adminUrl + "/api/jobinfo/remove";
|
if (jobId == null) {
|
||||||
try (HttpResponse response = HttpRequest.post(url)
|
|
||||||
.header("Authorization", accessToken)
|
|
||||||
.form("id", jobId)
|
|
||||||
.execute()) {
|
|
||||||
String body = response.body();
|
|
||||||
XxlApiResp resp = JSONUtil.toBean(body, XxlApiResp.class);
|
|
||||||
if (resp != null && resp.getCode() == 200) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
log.error("删除xxl‑job任务失败 jobId:{},resp:{}", jobId, body);
|
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
String url = adminUrl + "/jobinfo/remove";
|
||||||
|
try {
|
||||||
|
HttpResponse response = HttpRequest.post(url)
|
||||||
|
.header("Cookie", cookie)
|
||||||
|
.form("id", jobId)
|
||||||
|
.execute();
|
||||||
|
return response.getStatus() == 302;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("调用xxl‑job removeJob异常", e);
|
log.error("[XxlJob] removeJob jobId:{}异常", jobId, e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 暂停任务
|
* 请求实体,与浏览器F12抓包payload字段一一对应
|
||||||
*/
|
*/
|
||||||
public boolean pauseJob(Integer jobId) {
|
|
||||||
String url = adminUrl + "/api/jobinfo/pause";
|
|
||||||
try (HttpResponse response = HttpRequest.post(url)
|
|
||||||
.header("Authorization", accessToken)
|
|
||||||
.form("id", jobId)
|
|
||||||
.execute()) {
|
|
||||||
String body = response.body();
|
|
||||||
XxlApiResp resp = JSONUtil.toBean(body, XxlApiResp.class);
|
|
||||||
return resp != null && resp.getCode() == 200;
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("pauseJob异常", e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//---------------- 实体 ----------------
|
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public static class JobInfoAddReq {
|
public static class JobInfoAddReq {
|
||||||
/** 执行器组ID xxl_job_group.id */
|
|
||||||
private Integer jobGroup;
|
private Integer jobGroup;
|
||||||
/** 任务描述 */
|
|
||||||
private String jobDesc;
|
private String jobDesc;
|
||||||
/** 路由策略 ROUND:轮询 */
|
private String author;
|
||||||
private String executorRouteStrategy = "ROUND";
|
private String alarmEmail;
|
||||||
/** cron表达式 23点 0 0 23 * * ? */
|
private String scheduleType;
|
||||||
private String jobCron;
|
private String scheduleConf;
|
||||||
/** @XxlJob 注解handler名称 */
|
private String glueType;
|
||||||
private String executorHandler;
|
private String executorHandler;
|
||||||
/** 任务参数,这里存业务单号 */
|
|
||||||
private String executorParam;
|
private String executorParam;
|
||||||
/** 阻塞策略 SERIAL_EXECUTION单机串行 */
|
private String glueRemark;
|
||||||
private String executorBlockStrategy = "SERIAL_EXECUTION";
|
private String glueSource;
|
||||||
/** 超时时间 0不限制 */
|
private String executorRouteStrategy;
|
||||||
private Integer executorTimeout = 0;
|
private Integer childJobId;
|
||||||
/** 失败重试次数 */
|
private String misfireStrategy;
|
||||||
private Integer executorFailRetryCount = 0;
|
private String executorBlockStrategy;
|
||||||
/** BEAN模式 */
|
private Integer executorTimeout;
|
||||||
private String glueType = "BEAN";
|
private Integer executorFailRetryCount;
|
||||||
private String glueSource = "";
|
|
||||||
private String glueRemark = "";
|
|
||||||
/** 1启用 0停止 */
|
|
||||||
private Integer triggerStatus = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public static class XxlApiResp {
|
|
||||||
private Integer code;
|
|
||||||
private Object content;
|
|
||||||
private String msg;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,6 +100,11 @@
|
|||||||
<artifactId>xxl-job-core</artifactId>
|
<artifactId>xxl-job-core</artifactId>
|
||||||
<version>2.4.0</version>
|
<version>2.4.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jsoup</groupId>
|
||||||
|
<artifactId>jsoup</artifactId>
|
||||||
|
<version>1.17.2</version>
|
||||||
|
</dependency>
|
||||||
<!--添加druid连接池 依赖 -->
|
<!--添加druid连接池 依赖 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.alibaba</groupId>
|
<groupId>com.alibaba</groupId>
|
||||||
|
|||||||
+59
-23
@@ -44,6 +44,7 @@ import org.springframework.context.annotation.Bean;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -78,7 +79,8 @@ public class SettlementCustomersApplicationService {
|
|||||||
@Value("${xxl.job.admin.addresses}")
|
@Value("${xxl.job.admin.addresses}")
|
||||||
private String adminAddresses;
|
private String adminAddresses;
|
||||||
private static final String token = "Bearer eyJhbGciOiJIUzUxMiJ9.eyJ1c2VyX2lkIjozNTYzLCJ0b3BPcmdhbml6YXRpb25JZCI6MjgyNywidXNlcl9rZXkiOiI0N2MzY2U4My0wMmFjLTQ0NTQtOTdiYS1lYmQwMDc2MzNiNDMiLCJ1c2VybmFtZSI6ImNhbmdtYSJ9.tnZyywwburfBo3Kizdhfk5eYdXbrrZ9976Z0W7GrS6UqSpKwfI2RwdRwfYx7DbD7Ri1Fdj_eOVxBkjvdFF226Q";
|
private static final String token = "Bearer eyJhbGciOiJIUzUxMiJ9.eyJ1c2VyX2lkIjozNTYzLCJ0b3BPcmdhbml6YXRpb25JZCI6MjgyNywidXNlcl9rZXkiOiI0N2MzY2U4My0wMmFjLTQ0NTQtOTdiYS1lYmQwMDc2MzNiNDMiLCJ1c2VybmFtZSI6ImNhbmdtYSJ9.tnZyywwburfBo3Kizdhfk5eYdXbrrZ9976Z0W7GrS6UqSpKwfI2RwdRwfYx7DbD7Ri1Fdj_eOVxBkjvdFF226Q";
|
||||||
|
@Resource
|
||||||
|
private XxlJobOpenApiUtil apiUtil;
|
||||||
/**
|
/**
|
||||||
* 分页查询结算对象列表
|
* 分页查询结算对象列表
|
||||||
*/
|
*/
|
||||||
@@ -352,48 +354,82 @@ public class SettlementCustomersApplicationService {
|
|||||||
return settlementCustomersDomainService.update(settlementCustomersDO);
|
return settlementCustomersDomainService.update(settlementCustomersDO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建夜间23点定时任务
|
||||||
|
* @param shipperId 结算对象id,作为任务参数
|
||||||
|
* @return xxlJobId 保存到业务表,后续用于删除/暂停
|
||||||
|
*/
|
||||||
public Integer addXxlJob(String shipperId) {
|
public Integer addXxlJob(String shipperId) {
|
||||||
//1.实例化工具类,建议交给Spring管理
|
|
||||||
XxlJobOpenApiUtil apiUtil = new XxlJobOpenApiUtil(adminAddresses,token);
|
|
||||||
|
|
||||||
//2.构建新增任务参数
|
|
||||||
XxlJobOpenApiUtil.JobInfoAddReq req = new XxlJobOpenApiUtil.JobInfoAddReq();
|
XxlJobOpenApiUtil.JobInfoAddReq req = new XxlJobOpenApiUtil.JobInfoAddReq();
|
||||||
req.setJobGroup(3); //你的执行器组id
|
req.setJobGroup(5);
|
||||||
req.setJobDesc("散租推送计费单据/流水任务,结算对象id:" + shipperId);
|
req.setJobDesc("散租推送计费单据/流水任务,结算对象id:" + shipperId);
|
||||||
req.setJobCron("0 0 1 * * ?");
|
req.setAuthor("system");
|
||||||
|
req.setAlarmEmail("");
|
||||||
|
req.setScheduleType("CRON");
|
||||||
|
// 每天晚上23点执行
|
||||||
|
req.setScheduleConf("0 0 1 * * ?");
|
||||||
|
req.setGlueType("BEAN");
|
||||||
req.setExecutorHandler("pushBillingRecord");
|
req.setExecutorHandler("pushBillingRecord");
|
||||||
req.setExecutorParam(shipperId); //传入业务单号
|
req.setExecutorParam(shipperId);
|
||||||
|
req.setGlueRemark("");
|
||||||
|
req.setGlueSource("");
|
||||||
|
req.setExecutorRouteStrategy("FIRST");
|
||||||
|
req.setChildJobId(null);
|
||||||
|
req.setMisfireStrategy("DO_NOTHING");
|
||||||
|
req.setExecutorBlockStrategy("SERIAL_EXECUTION");
|
||||||
|
req.setExecutorTimeout(0);
|
||||||
|
req.setExecutorFailRetryCount(0);
|
||||||
|
|
||||||
//3.调用创建任务接口 ,返回任务ID
|
boolean addOk = apiUtil.addJob(req);
|
||||||
Integer xxlJobId = apiUtil.addJob(req);
|
if (!addOk) {
|
||||||
if(xxlJobId == null){
|
|
||||||
// 创建定时任务失败,抛异常/重试,不要让单据直接保存成功
|
|
||||||
throw new RuntimeException("创建夜间定时任务失败");
|
throw new RuntimeException("创建夜间定时任务失败");
|
||||||
}
|
}
|
||||||
|
Integer xxlJobId = apiUtil.queryJobIdByDescAndParam(req.getJobDesc(), shipperId);
|
||||||
|
if (xxlJobId == null) {
|
||||||
|
throw new RuntimeException("定时任务创建成功,但查询jobId失败");
|
||||||
|
}
|
||||||
return xxlJobId;
|
return xxlJobId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建夜间23点定时任务
|
||||||
|
* @param shipperId 结算对象id,作为任务参数
|
||||||
|
* @return xxlJobId 保存到业务表,后续用于删除/暂停
|
||||||
|
*/
|
||||||
public Integer addXxlJob1(String shipperId) {
|
public Integer addXxlJob1(String shipperId) {
|
||||||
//1.实例化工具类,建议交给Spring管理
|
|
||||||
XxlJobOpenApiUtil apiUtil = new XxlJobOpenApiUtil(adminAddresses,token);
|
|
||||||
|
|
||||||
//2.构建新增任务参数
|
|
||||||
XxlJobOpenApiUtil.JobInfoAddReq req = new XxlJobOpenApiUtil.JobInfoAddReq();
|
XxlJobOpenApiUtil.JobInfoAddReq req = new XxlJobOpenApiUtil.JobInfoAddReq();
|
||||||
req.setJobGroup(6); //你的执行器组id
|
req.setJobGroup(5);
|
||||||
req.setJobDesc("计费单据/流水生成发票任务,结算对象id:" + shipperId);
|
req.setJobDesc("计费单据/流水生成发票任务,结算对象id:" + shipperId);
|
||||||
req.setJobCron("0 0 1 * * ?");
|
req.setAuthor("system");
|
||||||
|
req.setAlarmEmail("");
|
||||||
|
req.setScheduleType("CRON");
|
||||||
|
// 每天晚上23点执行
|
||||||
|
req.setScheduleConf("0 0 1 * * ?");
|
||||||
|
req.setGlueType("BEAN");
|
||||||
req.setExecutorHandler("pushBillManage");
|
req.setExecutorHandler("pushBillManage");
|
||||||
req.setExecutorParam(shipperId); //传入业务单号
|
req.setExecutorParam(shipperId);
|
||||||
|
req.setGlueRemark("");
|
||||||
|
req.setGlueSource("");
|
||||||
|
req.setExecutorRouteStrategy("FIRST");
|
||||||
|
req.setChildJobId(null);
|
||||||
|
req.setMisfireStrategy("DO_NOTHING");
|
||||||
|
req.setExecutorBlockStrategy("SERIAL_EXECUTION");
|
||||||
|
req.setExecutorTimeout(0);
|
||||||
|
req.setExecutorFailRetryCount(0);
|
||||||
|
|
||||||
//3.调用创建任务接口 ,返回任务ID
|
boolean addOk = apiUtil.addJob(req);
|
||||||
Integer xxlJobId = apiUtil.addJob(req);
|
if (!addOk) {
|
||||||
if(xxlJobId == null){
|
|
||||||
// 创建定时任务失败,抛异常/重试,不要让单据直接保存成功
|
|
||||||
throw new RuntimeException("创建夜间定时任务失败");
|
throw new RuntimeException("创建夜间定时任务失败");
|
||||||
}
|
}
|
||||||
|
Integer xxlJobId = apiUtil.queryJobIdByDescAndParam(req.getJobDesc(), shipperId);
|
||||||
|
if (xxlJobId == null) {
|
||||||
|
throw new RuntimeException("定时任务创建成功,但查询jobId失败");
|
||||||
|
}
|
||||||
return xxlJobId;
|
return xxlJobId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void stopXxlJob(Integer shipperId) {
|
public void stopXxlJob(Integer shipperId) {
|
||||||
//1.实例化工具类,建议交给Spring管理
|
//1.实例化工具类,建议交给Spring管理
|
||||||
XxlJobOpenApiUtil apiUtil = new XxlJobOpenApiUtil(adminAddresses,token);
|
XxlJobOpenApiUtil apiUtil = new XxlJobOpenApiUtil(adminAddresses,token);
|
||||||
|
|||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
package com.mhd.bms.domain.settlementCustomers.repository.util;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class XxlJobBusinessConfig {
|
||||||
|
|
||||||
|
@Value("${xxl.job.admin.address}")
|
||||||
|
private String adminAddress;
|
||||||
|
|
||||||
|
@Value("${xxl.job.admin.cookie}")
|
||||||
|
private String adminCookie;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public XxlJobOpenApiUtil xxlJobOpenApiUtil() {
|
||||||
|
return new XxlJobOpenApiUtil(adminAddress, adminCookie);
|
||||||
|
}
|
||||||
|
}
|
||||||
+130
-85
@@ -1,139 +1,184 @@
|
|||||||
package com.mhd.bms.domain.settlementCustomers.repository.util;
|
package com.mhd.bms.domain.settlementCustomers.repository.util;
|
||||||
|
|
||||||
import cn.hutool.core.bean.BeanUtil;
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.http.HttpRequest;
|
import cn.hutool.http.HttpRequest;
|
||||||
import cn.hutool.http.HttpResponse;
|
import cn.hutool.http.HttpResponse;
|
||||||
import cn.hutool.core.lang.Assert;
|
|
||||||
import cn.hutool.json.JSONUtil;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* XXL‑JOB OpenApi 工具类
|
* 调用XXL‑JOB后台页面接口(抓包获取 /jobinfo/*)
|
||||||
|
* 鉴权依赖JSESSIONID Cookie,非官方openapi;老版本xxl‑job无新增任务openapi
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class XxlJobOpenApiUtil {
|
public class XxlJobOpenApiUtil {
|
||||||
|
|
||||||
/** xxl‑job admin地址,例:http://127.0.0.1:8080/xxl‑job‑admin */
|
|
||||||
private final String adminUrl;
|
private final String adminUrl;
|
||||||
/** 系统配置里的AccessToken */
|
private final String cookie;
|
||||||
private final String accessToken;
|
|
||||||
|
|
||||||
public XxlJobOpenApiUtil(String adminUrl, String accessToken) {
|
/**
|
||||||
this.adminUrl = adminUrl.endsWith("/") ? adminUrl.substring(0, adminUrl.length() -1) : adminUrl;
|
* @param adminUrl xxl‑job admin地址 示例:http://127.0.0.1:8080/xxl-job-admin
|
||||||
this.accessToken = accessToken;
|
* @param cookie 浏览器登录后的完整Cookie(JSESSIONID=xxx)
|
||||||
|
*/
|
||||||
|
public XxlJobOpenApiUtil(String adminUrl, String cookie) {
|
||||||
|
this.adminUrl = StrUtil.removeSuffix(adminUrl, "/");
|
||||||
|
this.cookie = cookie;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增定时任务
|
* 新增定时任务 POST /jobinfo/add
|
||||||
* @param addReq 请求参数
|
* @param addReq 请求实体,与浏览器payload字段完全对齐
|
||||||
* @return jobId 任务ID,返回null代表调用失败
|
* @return true=新增成功,false=失败
|
||||||
*/
|
*/
|
||||||
public Integer addJob(JobInfoAddReq addReq) {
|
public boolean addJob(JobInfoAddReq addReq) {
|
||||||
String url = adminUrl + "/api/jobinfo/add";
|
String url = adminUrl + "/jobinfo/add";
|
||||||
try {
|
try {
|
||||||
// 实体对象转map,解决form不能传bean的爆红
|
|
||||||
Map<String, Object> formMap = BeanUtil.beanToMap(addReq);
|
Map<String, Object> formMap = BeanUtil.beanToMap(addReq);
|
||||||
|
|
||||||
HttpResponse response = HttpRequest.post(url)
|
HttpResponse response = HttpRequest.post(url)
|
||||||
.header("Authorization", accessToken)
|
.header("Cookie", cookie)
|
||||||
.form(formMap) //传入map,不再传实体
|
.form(formMap)
|
||||||
.execute();
|
.execute();
|
||||||
|
int status = response.getStatus();
|
||||||
|
log.info("[XxlJob] addJob status:{}", status);
|
||||||
|
// 新增成功302重定向到列表页
|
||||||
|
return status == 302;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[XxlJob] addJob异常", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String body = response.body();
|
/**
|
||||||
log.info("xxl‑job addJob resp:{}", body);
|
* 根据【任务描述+执行器参数】查询jobId
|
||||||
XxlApiResp resp = JSONUtil.toBean(body, XxlApiResp.class);
|
* @param jobDesc 任务描述
|
||||||
if (resp != null && resp.getCode() == 200) {
|
* @param executorParam 任务参数(业务shipperId)
|
||||||
return Integer.valueOf(resp.getContent().toString());
|
* @return jobId,查不到返回null
|
||||||
|
*/
|
||||||
|
public Integer queryJobIdByDescAndParam(String jobDesc, String executorParam) {
|
||||||
|
String url = adminUrl + "/jobinfo/pageList";
|
||||||
|
try {
|
||||||
|
HttpResponse response = HttpRequest.post(url)
|
||||||
|
.header("Cookie", cookie)
|
||||||
|
.form("jobGroup", "")
|
||||||
|
.form("jobDesc", jobDesc)
|
||||||
|
.form("executorHandler", "")
|
||||||
|
.form("executorParam", executorParam)
|
||||||
|
.form("triggerStatus", "-1")
|
||||||
|
.execute();
|
||||||
|
String html = response.body();
|
||||||
|
if (StrUtil.isBlank(html)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Document doc = Jsoup.parse(html);
|
||||||
|
Elements trList = doc.select("tr");
|
||||||
|
for (Element tr : trList) {
|
||||||
|
String dataId = tr.attr("data-id");
|
||||||
|
if (StrUtil.isBlank(dataId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return Integer.parseInt(dataId);
|
||||||
}
|
}
|
||||||
log.error("新增xxl‑job任务失败,resp:{}", body);
|
|
||||||
return null;
|
return null;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("调用xxl‑job addJob异常", e);
|
log.error("[XxlJob] queryJobId异常", e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 暂停任务 POST /jobinfo/pause
|
||||||
|
* @param jobId xxl任务id
|
||||||
|
* @return true成功
|
||||||
|
*/
|
||||||
|
public boolean pauseJob(Integer jobId) {
|
||||||
|
if (jobId == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String url = adminUrl + "/jobinfo/pause";
|
||||||
|
try {
|
||||||
|
HttpResponse response = HttpRequest.post(url)
|
||||||
|
.header("Cookie", cookie)
|
||||||
|
.form("id", jobId)
|
||||||
|
.execute();
|
||||||
|
return response.getStatus() == 302;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[XxlJob] pauseJob jobId:{}异常", jobId, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除任务
|
* 开启任务 POST /jobinfo/start
|
||||||
* @param jobId 任务id
|
* @param jobId xxl任务id
|
||||||
|
* @return true成功
|
||||||
|
*/
|
||||||
|
public boolean startJob(Integer jobId) {
|
||||||
|
if (jobId == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String url = adminUrl + "/jobinfo/start";
|
||||||
|
try {
|
||||||
|
HttpResponse response = HttpRequest.post(url)
|
||||||
|
.header("Cookie", cookie)
|
||||||
|
.form("id", jobId)
|
||||||
|
.execute();
|
||||||
|
return response.getStatus() == 302;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[XxlJob] startJob jobId:{}异常", jobId, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除任务 POST /jobinfo/remove
|
||||||
|
* @param jobId xxl任务id
|
||||||
* @return true成功
|
* @return true成功
|
||||||
*/
|
*/
|
||||||
public boolean removeJob(Integer jobId) {
|
public boolean removeJob(Integer jobId) {
|
||||||
String url = adminUrl + "/api/jobinfo/remove";
|
if (jobId == null) {
|
||||||
try (HttpResponse response = HttpRequest.post(url)
|
|
||||||
.header("Authorization", accessToken)
|
|
||||||
.form("id", jobId)
|
|
||||||
.execute()) {
|
|
||||||
String body = response.body();
|
|
||||||
XxlApiResp resp = JSONUtil.toBean(body, XxlApiResp.class);
|
|
||||||
if (resp != null && resp.getCode() == 200) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
log.error("删除xxl‑job任务失败 jobId:{},resp:{}", jobId, body);
|
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
String url = adminUrl + "/jobinfo/remove";
|
||||||
|
try {
|
||||||
|
HttpResponse response = HttpRequest.post(url)
|
||||||
|
.header("Cookie", cookie)
|
||||||
|
.form("id", jobId)
|
||||||
|
.execute();
|
||||||
|
return response.getStatus() == 302;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("调用xxl‑job removeJob异常", e);
|
log.error("[XxlJob] removeJob jobId:{}异常", jobId, e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 暂停任务
|
* 请求实体,与浏览器F12抓包payload字段一一对应
|
||||||
*/
|
*/
|
||||||
public boolean pauseJob(Integer jobId) {
|
|
||||||
String url = adminUrl + "/api/jobinfo/pause";
|
|
||||||
try (HttpResponse response = HttpRequest.post(url)
|
|
||||||
.header("Authorization", accessToken)
|
|
||||||
.form("id", jobId)
|
|
||||||
.execute()) {
|
|
||||||
String body = response.body();
|
|
||||||
XxlApiResp resp = JSONUtil.toBean(body, XxlApiResp.class);
|
|
||||||
return resp != null && resp.getCode() == 200;
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("pauseJob异常", e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//---------------- 实体 ----------------
|
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public static class JobInfoAddReq {
|
public static class JobInfoAddReq {
|
||||||
/** 执行器组ID xxl_job_group.id */
|
|
||||||
private Integer jobGroup;
|
private Integer jobGroup;
|
||||||
/** 任务描述 */
|
|
||||||
private String jobDesc;
|
private String jobDesc;
|
||||||
/** 路由策略 ROUND:轮询 */
|
private String author;
|
||||||
private String executorRouteStrategy = "ROUND";
|
private String alarmEmail;
|
||||||
/** cron表达式 23点 0 0 23 * * ? */
|
private String scheduleType;
|
||||||
private String jobCron;
|
private String scheduleConf;
|
||||||
/** @XxlJob 注解handler名称 */
|
private String glueType;
|
||||||
private String executorHandler;
|
private String executorHandler;
|
||||||
/** 任务参数,这里存业务单号 */
|
|
||||||
private String executorParam;
|
private String executorParam;
|
||||||
/** 阻塞策略 SERIAL_EXECUTION单机串行 */
|
private String glueRemark;
|
||||||
private String executorBlockStrategy = "SERIAL_EXECUTION";
|
private String glueSource;
|
||||||
/** 超时时间 0不限制 */
|
private String executorRouteStrategy;
|
||||||
private Integer executorTimeout = 0;
|
private Integer childJobId;
|
||||||
/** 失败重试次数 */
|
private String misfireStrategy;
|
||||||
private Integer executorFailRetryCount = 0;
|
private String executorBlockStrategy;
|
||||||
/** BEAN模式 */
|
private Integer executorTimeout;
|
||||||
private String glueType = "BEAN";
|
private Integer executorFailRetryCount;
|
||||||
private String glueSource = "";
|
|
||||||
private String glueRemark = "";
|
|
||||||
/** 1启用 0停止 */
|
|
||||||
private Integer triggerStatus = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public static class XxlApiResp {
|
|
||||||
private Integer code;
|
|
||||||
private Object content;
|
|
||||||
private String msg;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user