diff --git a/doc/XXL-JOB官方文档.md b/doc/XXL-JOB官方文档.md index 9263e9a7..d99f3cba 100644 --- a/doc/XXL-JOB官方文档.md +++ b/doc/XXL-JOB官方文档.md @@ -2447,18 +2447,31 @@ public void execute() { - 3、【优化】IP获取逻辑优化,优先遍历网卡来获取可用IP; - 4、【优化】通用命令行任务(“commandJobHandler”)优化,支持多参数执行,命令及参数之间通过空格隔开;如任务参数 "ls la" 或 "pwd" 将会执行命令并输出数据; - 5、【优化】通用HTTP任务(httpJobHandler)优化,任务参数格式调整为json格式; -- 6、【升级】多个项目依赖升级至较新稳定版本,涉及 gson、groovy、spring/springboot 等; +- 6、【升级】多个项目依赖升级至较新稳定版本,涉及 netty、groovy、spring/springboot 等; **备注:** - a、本次升级数据模型及通讯协议向前兼容,v2.4.*及后续版本可无缝升级; - b、版本3.x开始要求Jdk17;版本2.x及以下支持Jdk1.8。如对Jdk版本有诉求,可选择接入不同版本; ### 7.38 版本 v3.0.1 Release Notes[规划中] -- 1、【修复】任务操作逻辑优化,修复边界情况下逻辑中断问题(ISSUE-2081)。 -- 2、【修复】调度中心Cron前端组件优化,解决week配置与后端兼容性问题(ISSUE-2220)。 -- 3、【优化】Glue IDE优化,版本回溯支持查看修改时间; -- 4、[规划中]登陆态Token生成逻辑优化,混淆登陆时间属性,降低token泄漏风险; -- 5、[规划中]组件扫描改为BeanPostProcessor方式,避免小概率情况下提前初始化;底层组件移除单例写法,汇总factory统一管理; +- 1、【新增】新增AI执行器示例,并与spring-ai集成打通;内置一系列AIXxlJob,支持快速开发AI类任务(xxl-job-executor-sample-springboot-ai)。 +- 2、【新增】新增通用OllamaChat任务(ollamaJobHandler),支持自定义prompt、input等输入信息,示例参数如下; +``` +{ + "input": "{输入信息,必填信息}", + "prompt": "{模型prompt,可选信息}" +} +``` +- 3、【修复】任务操作逻辑优化,修复边界情况下逻辑中断问题(ISSUE-2081)。 +- 4、【修复】调度中心Cron前端组件优化,解决week配置与后端兼容性问题(ISSUE-2220)。 +- 5、【优化】Glue IDE调整,版本回溯支持查看修改时间; +- 6、【优化】任务RollingLog调整,XSS过滤支持白名单排出,提升日志易读性; +- 7、【升级】多个项目依赖升级至较新稳定版本,涉及 gson、groovy、spring/springboot 等; + + +### 7.39 版本 v3.0.2 Release Notes[规划中] +- 1、[规划中]登陆态Token生成逻辑优化,混淆登陆时间属性,降低token泄漏风险; +- 2、[规划中]组件扫描改为BeanPostProcessor方式,避免小概率情况下提前初始化;底层组件移除单例写法,汇总factory统一管理; ### TODO LIST - 1、调度隔离:调度中心针对不同执行器,各自维护不同的调度和远程触发组件。 diff --git a/pom.xml b/pom.xml index b9d5e9ba..da6fd241 100644 --- a/pom.xml +++ b/pom.xml @@ -29,20 +29,20 @@ 3.11.2 3.2.7 - 2.0.16 - 5.11.4 + 2.0.17 + 5.12.1 3.0.0 - 4.1.117.Final + 4.2.0.Final 2.12.1 - 3.4.2 - 6.2.2 + 3.4.4 + 6.2.5 3.0.4 9.2.0 - 4.0.25 + 4.0.26 diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobLogController.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobLogController.java index 45df783d..2598e6cd 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobLogController.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobLogController.java @@ -127,7 +127,7 @@ public class JobLogController { public String logDetailPage(@RequestParam("id") int id, Model model){ // base check - ReturnT logStatue = ReturnT.SUCCESS; + //ReturnT logStatue = ReturnT.SUCCESS; XxlJobLog jobLog = xxlJobLogDao.load(id); if (jobLog == null) { throw new RuntimeException(I18nUtil.getString("joblog_logid_unvalid")); @@ -162,8 +162,7 @@ public class JobLogController { // fix xss if (logResult.getContent()!=null && StringUtils.hasText(logResult.getContent().getLogContent())) { - String newLogContent = logResult.getContent().getLogContent(); - newLogContent = HtmlUtils.htmlEscape(newLogContent, "UTF-8"); + String newLogContent = filter(logResult.getContent().getLogContent()); logResult.getContent().setLogContent(newLogContent); } @@ -174,6 +173,38 @@ public class JobLogController { } } + /** + * filter xss tag + * + * @param originData + * @return + */ + private String filter(String originData){ + + // exclude tag + Map excludeTagMap = new HashMap(); + excludeTagMap.put("
", "###TAG_BR###"); + excludeTagMap.put("", "###TAG_BOLD###"); + excludeTagMap.put("", "###TAG_BOLD_END###"); + + // replace + for (String key : excludeTagMap.keySet()) { + String value = excludeTagMap.get(key); + originData = originData.replaceAll(key, value); + } + + // htmlEscape + originData = HtmlUtils.htmlEscape(originData, "UTF-8"); + + // replace back + for (String key : excludeTagMap.keySet()) { + String value = excludeTagMap.get(key); + originData = originData.replaceAll(value, key); + } + + return originData; + } + @RequestMapping("/logKill") @ResponseBody public ReturnT logKill(@RequestParam("id") int id){ diff --git a/xxl-job-executor-samples/pom.xml b/xxl-job-executor-samples/pom.xml index 0fde398e..b6b61427 100644 --- a/xxl-job-executor-samples/pom.xml +++ b/xxl-job-executor-samples/pom.xml @@ -12,6 +12,7 @@ xxl-job-executor-sample-frameless xxl-job-executor-sample-springboot + xxl-job-executor-sample-springboot-ai diff --git a/xxl-job-executor-samples/xxl-job-executor-sample-springboot-ai/pom.xml b/xxl-job-executor-samples/xxl-job-executor-sample-springboot-ai/pom.xml new file mode 100644 index 00000000..c631b7e7 --- /dev/null +++ b/xxl-job-executor-samples/xxl-job-executor-sample-springboot-ai/pom.xml @@ -0,0 +1,83 @@ + + + 4.0.0 + + com.xuxueli + xxl-job-executor-samples + 3.0.1-SNAPSHOT + + xxl-job-executor-sample-springboot-ai + jar + + ${project.artifactId} + Example executor project for spring boot. + https://www.xuxueli.com/ + + + 1.0.0-M6 + + + + + + org.springframework.boot + spring-boot-starter-parent + ${spring-boot.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + + + com.xuxueli + xxl-job-core + ${project.parent.version} + + + + + org.springframework.ai + spring-ai-ollama-spring-boot-starter + ${spring-ai.version} + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + repackage + + + + + true + + + + + + \ No newline at end of file diff --git a/xxl-job-executor-samples/xxl-job-executor-sample-springboot-ai/src/main/java/com/xxl/job/executor/XxlJobAIExecutorApplication.java b/xxl-job-executor-samples/xxl-job-executor-sample-springboot-ai/src/main/java/com/xxl/job/executor/XxlJobAIExecutorApplication.java new file mode 100644 index 00000000..aebc58d2 --- /dev/null +++ b/xxl-job-executor-samples/xxl-job-executor-sample-springboot-ai/src/main/java/com/xxl/job/executor/XxlJobAIExecutorApplication.java @@ -0,0 +1,16 @@ +package com.xxl.job.executor; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author xuxueli 2018-10-28 00:38:13 + */ +@SpringBootApplication +public class XxlJobAIExecutorApplication { + + public static void main(String[] args) { + SpringApplication.run(XxlJobAIExecutorApplication.class, args); + } + +} \ No newline at end of file diff --git a/xxl-job-executor-samples/xxl-job-executor-sample-springboot-ai/src/main/java/com/xxl/job/executor/config/XxlJobConfig.java b/xxl-job-executor-samples/xxl-job-executor-sample-springboot-ai/src/main/java/com/xxl/job/executor/config/XxlJobConfig.java new file mode 100644 index 00000000..2823a09e --- /dev/null +++ b/xxl-job-executor-samples/xxl-job-executor-sample-springboot-ai/src/main/java/com/xxl/job/executor/config/XxlJobConfig.java @@ -0,0 +1,91 @@ +package com.xxl.job.executor.config; + +import com.xxl.job.core.executor.impl.XxlJobSpringExecutor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ai.chat.client.ChatClient; +import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor; +import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor; +import org.springframework.ai.chat.memory.InMemoryChatMemory; +import org.springframework.ai.ollama.OllamaChatModel; +import org.springframework.ai.ollama.api.OllamaOptions; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * xxl-job config + * + * @author xuxueli 2017-04-28 + */ +@Configuration +public class XxlJobConfig { + private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class); + + @Value("${xxl.job.admin.addresses}") + private String adminAddresses; + + @Value("${xxl.job.admin.accessToken}") + private String accessToken; + + @Value("${xxl.job.admin.timeout}") + private int timeout; + + @Value("${xxl.job.executor.appname}") + private String appname; + + @Value("${xxl.job.executor.address}") + private String address; + + @Value("${xxl.job.executor.ip}") + private String ip; + + @Value("${xxl.job.executor.port}") + private int port; + + @Value("${xxl.job.executor.logpath}") + private String logPath; + + @Value("${xxl.job.executor.logretentiondays}") + private int logRetentionDays; + + + @Bean + public XxlJobSpringExecutor xxlJobExecutor() { + logger.info(">>>>>>>>>>> xxl-job config init."); + XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor(); + xxlJobSpringExecutor.setAdminAddresses(adminAddresses); + xxlJobSpringExecutor.setAppname(appname); + xxlJobSpringExecutor.setAddress(address); + xxlJobSpringExecutor.setIp(ip); + xxlJobSpringExecutor.setPort(port); + xxlJobSpringExecutor.setAccessToken(accessToken); + xxlJobSpringExecutor.setTimeout(timeout); + xxlJobSpringExecutor.setLogPath(logPath); + xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays); + + return xxlJobSpringExecutor; + } + + /** + * ChatClient + * + * @param ollamaChatModel + * @return + * @throws Exception + */ + @Bean + public ChatClient chatClient(OllamaChatModel ollamaChatModel) throws Exception { + // init ollamaiChatClient + ChatClient ollamaiChatClient = ChatClient + .builder(ollamaChatModel) + .defaultAdvisors(new MessageChatMemoryAdvisor(new InMemoryChatMemory())) // 管理对话上下文记忆 + .defaultAdvisors(new SimpleLoggerAdvisor()) // 记录日志的Advisor, + .defaultOptions(OllamaOptions.builder().topP(0.7).build()) // 设置ChatModel参数 + .build(); + + return ollamaiChatClient; + } + + +} \ No newline at end of file diff --git a/xxl-job-executor-samples/xxl-job-executor-sample-springboot-ai/src/main/java/com/xxl/job/executor/controller/IndexController.java b/xxl-job-executor-samples/xxl-job-executor-sample-springboot-ai/src/main/java/com/xxl/job/executor/controller/IndexController.java new file mode 100644 index 00000000..4b0b3f60 --- /dev/null +++ b/xxl-job-executor-samples/xxl-job-executor-sample-springboot-ai/src/main/java/com/xxl/job/executor/controller/IndexController.java @@ -0,0 +1,63 @@ +package com.xxl.job.executor.controller;//package com.xxl.job.executor.mvc.controller; + +import com.xxl.job.executor.config.XxlJobConfig; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ai.chat.client.ChatClient; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import reactor.core.publisher.Flux; + +@Controller +@EnableAutoConfiguration +public class IndexController { + private static Logger logger = LoggerFactory.getLogger(IndexController.class); + + + @RequestMapping("/") + @ResponseBody + String index() { + return "xxl job executor running."; + } + + + @Resource + private ChatClient chatClient; + private static String prompt = "你好,你是一个研发工程师,擅长解决技术类问题。"; + + + /** + * ChatClient 简单调用 + */ + @GetMapping("/chat/simple") + @ResponseBody + public String simpleChat(@RequestParam(value = "input") String input) { + String result = chatClient + .prompt(prompt) + .user(input) + .call() + .content(); + System.out.println("result: " + result); + return result; + } + + /** + * ChatClient 流式调用 + */ + @GetMapping("/chat/stream") + public Flux streamChat(HttpServletResponse response, @RequestParam(value = "input") String input) { + response.setCharacterEncoding("UTF-8"); + return chatClient + .prompt(prompt) + .user(input) + .stream() + .content(); + } + +} \ No newline at end of file diff --git a/xxl-job-executor-samples/xxl-job-executor-sample-springboot-ai/src/main/java/com/xxl/job/executor/jobhandler/AIXxlJob.java b/xxl-job-executor-samples/xxl-job-executor-sample-springboot-ai/src/main/java/com/xxl/job/executor/jobhandler/AIXxlJob.java new file mode 100644 index 00000000..01a21957 --- /dev/null +++ b/xxl-job-executor-samples/xxl-job-executor-sample-springboot-ai/src/main/java/com/xxl/job/executor/jobhandler/AIXxlJob.java @@ -0,0 +1,79 @@ +package com.xxl.job.executor.jobhandler; + +import com.xxl.job.core.context.XxlJobHelper; +import com.xxl.job.core.handler.annotation.XxlJob; +import com.xxl.job.core.util.GsonTool; +import jakarta.annotation.Resource; +import org.springframework.ai.chat.client.ChatClient; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * AI 任务开发示例 + * + * @author xuxueli 2025-04-06 + */ +@Component +public class AIXxlJob { + + @Resource + private ChatClient chatClient; + + /** + * 1、ollama Chat任务 + * + * 参数示例: + *
+     *      {
+     *          "input": "{输入信息,必填信息}",
+     *          "prompt": "{模型prompt,可选信息}"
+     *      }
+     *  
+ */ + @XxlJob("ollamaJobHandler") + public void ollamaJobHandler() throws Exception { + + // param + String param = XxlJobHelper.getJobParam(); + if (param==null || param.trim().isEmpty()) { + XxlJobHelper.log("param is empty."); + + XxlJobHelper.handleFail(); + return; + } + + // param parse + String prompt = "你是一个研发工程师,擅长解决技术类问题。"; + String input; + try { + Map paramMap =GsonTool.fromJson(param, Map.class); + if (paramMap.containsKey("prompt")) { + prompt = paramMap.get("prompt"); + } + input = paramMap.get("input"); + if (input == null || input.trim().isEmpty()) { + XxlJobHelper.log("input is empty."); + + XxlJobHelper.handleFail(); + return; + } + } catch (Exception e) { + XxlJobHelper.log(e); + XxlJobHelper.handleFail(); + return; + } + + // input + XxlJobHelper.log("

【Input】: " + input + "

"); + + // invoke + String result = chatClient + .prompt(prompt) + .user(input) + .call() + .content(); + XxlJobHelper.log("

【Output】: " + result+ "

"); + } + +} diff --git a/xxl-job-executor-samples/xxl-job-executor-sample-springboot-ai/src/main/java/com/xxl/job/executor/jobhandler/SampleXxlJob.java b/xxl-job-executor-samples/xxl-job-executor-sample-springboot-ai/src/main/java/com/xxl/job/executor/jobhandler/SampleXxlJob.java new file mode 100644 index 00000000..76e15c72 --- /dev/null +++ b/xxl-job-executor-samples/xxl-job-executor-sample-springboot-ai/src/main/java/com/xxl/job/executor/jobhandler/SampleXxlJob.java @@ -0,0 +1,271 @@ +package com.xxl.job.executor.jobhandler; + +import com.xxl.job.core.context.XxlJobHelper; +import com.xxl.job.core.handler.annotation.XxlJob; +import com.xxl.job.core.util.GsonTool; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Arrays; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * XxlJob开发示例(Bean模式) + * + * 开发步骤: + * 1、任务开发:在Spring Bean实例中,开发Job方法; + * 2、注解配置:为Job方法添加注解 "@XxlJob(value="自定义jobhandler名称", init = "JobHandler初始化方法", destroy = "JobHandler销毁方法")",注解value值对应的是调度中心新建任务的JobHandler属性的值。 + * 3、执行日志:需要通过 "XxlJobHelper.log" 打印执行日志; + * 4、任务结果:默认任务结果为 "成功" 状态,不需要主动设置;如有诉求,比如设置任务结果为失败,可以通过 "XxlJobHelper.handleFail/handleSuccess" 自主设置任务结果; + * + * @author xuxueli 2019-12-11 21:52:51 + */ +@Component +public class SampleXxlJob { + private static Logger logger = LoggerFactory.getLogger(SampleXxlJob.class); + + + /** + * 1、简单任务示例(Bean模式) + */ + @XxlJob("demoJobHandler") + public void demoJobHandler() throws Exception { + XxlJobHelper.log("XXL-JOB, Hello World."); + + for (int i = 0; i < 5; i++) { + XxlJobHelper.log("beat at:" + i); + TimeUnit.SECONDS.sleep(2); + } + // default success + } + + + /** + * 2、分片广播任务 + */ + @XxlJob("shardingJobHandler") + public void shardingJobHandler() throws Exception { + + // 分片参数 + int shardIndex = XxlJobHelper.getShardIndex(); + int shardTotal = XxlJobHelper.getShardTotal(); + + XxlJobHelper.log("分片参数:当前分片序号 = {}, 总分片数 = {}", shardIndex, shardTotal); + + // 业务逻辑 + for (int i = 0; i < shardTotal; i++) { + if (i == shardIndex) { + XxlJobHelper.log("第 {} 片, 命中分片开始处理", i); + } else { + XxlJobHelper.log("第 {} 片, 忽略", i); + } + } + + } + + + /** + * 3、命令行任务 + * + * 参数示例:"ls -a" 或者 "pwd" + */ + @XxlJob("commandJobHandler") + public void commandJobHandler() throws Exception { + String command = XxlJobHelper.getJobParam(); + int exitValue = -1; + + BufferedReader bufferedReader = null; + try { + // valid + if (command==null || command.trim().length()==0) { + XxlJobHelper.handleFail("command empty."); + return; + } + + // command split + String[] commandArray = command.split(" "); + + // command process + ProcessBuilder processBuilder = new ProcessBuilder(); + processBuilder.command(commandArray); + processBuilder.redirectErrorStream(true); + + Process process = processBuilder.start(); + //Process process = Runtime.getRuntime().exec(command); + + BufferedInputStream bufferedInputStream = new BufferedInputStream(process.getInputStream()); + bufferedReader = new BufferedReader(new InputStreamReader(bufferedInputStream)); + + // command log + String line; + while ((line = bufferedReader.readLine()) != null) { + XxlJobHelper.log(line); + } + + // command exit + process.waitFor(); + exitValue = process.exitValue(); + } catch (Exception e) { + XxlJobHelper.log(e); + } finally { + if (bufferedReader != null) { + bufferedReader.close(); + } + } + + if (exitValue == 0) { + // default success + } else { + XxlJobHelper.handleFail("command exit value("+exitValue+") is failed"); + } + + } + + + /** + * 4、跨平台Http任务 + * + * 参数示例: + *
+     *      {
+     *          "url": "http://www.baidu.com",
+     *          "method": "get",
+     *          "data": "hello world"
+     *      }
+     *  
+ */ + @XxlJob("httpJobHandler") + public void httpJobHandler() throws Exception { + + // param + String param = XxlJobHelper.getJobParam(); + if (param==null || param.trim().length()==0) { + XxlJobHelper.log("param["+ param +"] invalid."); + + XxlJobHelper.handleFail(); + return; + } + + // param parse + String url; + String method; + String data; + try { + Map paramMap =GsonTool.fromJson(param, Map.class); + url = paramMap.get("url"); + method = paramMap.get("method"); + data = paramMap.get("data"); + } catch (Exception e) { + XxlJobHelper.log(e); + XxlJobHelper.handleFail(); + return; + } + + // param valid + if (url==null || url.trim().length()==0) { + XxlJobHelper.log("url["+ url +"] invalid."); + + XxlJobHelper.handleFail(); + return; + } + if (method==null || !Arrays.asList("GET", "POST").contains(method.toUpperCase())) { + XxlJobHelper.log("method["+ method +"] invalid."); + + XxlJobHelper.handleFail(); + return; + } + method = method.toUpperCase(); + boolean isPostMethod = method.equals("POST"); + + // request + HttpURLConnection connection = null; + BufferedReader bufferedReader = null; + try { + // connection + URL realUrl = new URL(url); + connection = (HttpURLConnection) realUrl.openConnection(); + + // connection setting + connection.setRequestMethod(method); + connection.setDoOutput(isPostMethod); + connection.setDoInput(true); + connection.setUseCaches(false); + connection.setReadTimeout(5 * 1000); + connection.setConnectTimeout(3 * 1000); + connection.setRequestProperty("connection", "Keep-Alive"); + connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8"); + connection.setRequestProperty("Accept-Charset", "application/json;charset=UTF-8"); + + // do connection + connection.connect(); + + // data + if (isPostMethod && data!=null && data.trim().length()>0) { + DataOutputStream dataOutputStream = new DataOutputStream(connection.getOutputStream()); + dataOutputStream.write(data.getBytes("UTF-8")); + dataOutputStream.flush(); + dataOutputStream.close(); + } + + // valid StatusCode + int statusCode = connection.getResponseCode(); + if (statusCode != 200) { + throw new RuntimeException("Http Request StatusCode(" + statusCode + ") Invalid."); + } + + // result + bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8")); + StringBuilder result = new StringBuilder(); + String line; + while ((line = bufferedReader.readLine()) != null) { + result.append(line); + } + String responseMsg = result.toString(); + + XxlJobHelper.log(responseMsg); + + return; + } catch (Exception e) { + XxlJobHelper.log(e); + + XxlJobHelper.handleFail(); + return; + } finally { + try { + if (bufferedReader != null) { + bufferedReader.close(); + } + if (connection != null) { + connection.disconnect(); + } + } catch (Exception e2) { + XxlJobHelper.log(e2); + } + } + + } + + /** + * 5、生命周期任务示例:任务初始化与销毁时,支持自定义相关逻辑; + */ + @XxlJob(value = "demoJobHandler2", init = "init", destroy = "destroy") + public void demoJobHandler2() throws Exception { + XxlJobHelper.log("XXL-JOB, Hello World."); + } + public void init(){ + logger.info("init"); + } + public void destroy(){ + logger.info("destroy"); + } + + +} diff --git a/xxl-job-executor-samples/xxl-job-executor-sample-springboot-ai/src/main/resources/application.properties b/xxl-job-executor-samples/xxl-job-executor-sample-springboot-ai/src/main/resources/application.properties new file mode 100644 index 00000000..df35be04 --- /dev/null +++ b/xxl-job-executor-samples/xxl-job-executor-sample-springboot-ai/src/main/resources/application.properties @@ -0,0 +1,34 @@ +# web port +server.port=8082 +# no web +#spring.main.web-environment=false + +# log config +logging.config=classpath:logback.xml + + +### xxl-job admin address list, such as "http://address" or "http://address01,http://address02" +xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin +### xxl-job, access token +xxl.job.admin.accessToken=default_token +### xxl-job timeout by second, default 3s +xxl.job.admin.timeout=3 + +### xxl-job executor appname +xxl.job.executor.appname=xxl-job-executor-sample-ai +### xxl-job executor registry-address: default use address to registry , otherwise use ip:port if address is null +xxl.job.executor.address= +### xxl-job executor server-info +xxl.job.executor.ip= +xxl.job.executor.port=9999 +### xxl-job executor log-path +xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler +### xxl-job executor log-retention-days +xxl.job.executor.logretentiondays=30 + + +### ollama +spring.ai.ollama.base-url=http://localhost:11434 +spring.ai.ollama.chat.enabled=true +spring.ai.ollama.chat.options.model=qwen2.5:1.5b +spring.ai.ollama.chat.options.temperature=0.8 diff --git a/xxl-job-executor-samples/xxl-job-executor-sample-springboot-ai/src/main/resources/logback.xml b/xxl-job-executor-samples/xxl-job-executor-sample-springboot-ai/src/main/resources/logback.xml new file mode 100644 index 00000000..ad04febb --- /dev/null +++ b/xxl-job-executor-samples/xxl-job-executor-sample-springboot-ai/src/main/resources/logback.xml @@ -0,0 +1,29 @@ + + + + logback + + + + + %d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n + + + + + ${log.path} + + ${log.path}.%d{yyyy-MM-dd}.zip + + + %date %level [%thread] %logger{36} [%file : %line] %msg%n + + + + + + + + + + \ No newline at end of file diff --git a/xxl-job-executor-samples/xxl-job-executor-sample-springboot-ai/src/test/java/com/xxl/job/executor/test/XxlJobExecutorExampleBootApplicationTests.java b/xxl-job-executor-samples/xxl-job-executor-sample-springboot-ai/src/test/java/com/xxl/job/executor/test/XxlJobExecutorExampleBootApplicationTests.java new file mode 100644 index 00000000..456a7d5a --- /dev/null +++ b/xxl-job-executor-samples/xxl-job-executor-sample-springboot-ai/src/test/java/com/xxl/job/executor/test/XxlJobExecutorExampleBootApplicationTests.java @@ -0,0 +1,14 @@ +package com.xxl.job.executor.test; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +public class XxlJobExecutorExampleBootApplicationTests { + + @Test + public void test() { + System.out.println(11); + } + +} \ No newline at end of file