RestTaskInvok.java 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. /*
  2. * Copyright (c) 2018-2025, hnqz All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions are met:
  6. *
  7. * Redistributions of source code must retain the above copyright notice,
  8. * this list of conditions and the following disclaimer.
  9. * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * Neither the name of the pig4cloud.com developer nor the names of its
  13. * contributors may be used to endorse or promote products derived from
  14. * this software without specific prior written permission.
  15. * Author: hnqz
  16. */
  17. package net.yyc.quartz.util;
  18. import cn.hutool.core.util.StrUtil;
  19. import cn.hutool.http.HttpRequest;
  20. import cn.hutool.http.HttpUtil;
  21. import lombok.extern.slf4j.Slf4j;
  22. import net.yyc.common.core.constant.CommonConstants;
  23. import net.yyc.common.core.constant.SecurityConstants;
  24. import net.yyc.quartz.entity.SysJob;
  25. import net.yyc.quartz.exception.TaskException;
  26. import org.springframework.beans.factory.annotation.Autowired;
  27. import org.springframework.cloud.client.ServiceInstance;
  28. import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
  29. import org.springframework.stereotype.Component;
  30. import java.util.HashMap;
  31. import java.util.Map;
  32. /**
  33. * 定时任务 REST 反射实现
  34. * 支持 service-name 模式(http://service-name/path)和 host:port 模式(http://host:port/path)
  35. *
  36. * @author 郑健楠
  37. */
  38. @Slf4j
  39. @Component
  40. public class RestTaskInvok implements ITaskInvok {
  41. @Autowired
  42. private LoadBalancerClient loadBalancer;
  43. @Override
  44. public void invokMethod(SysJob sysJob) throws TaskException {
  45. try {
  46. String executePath = sysJob.getExecutePath();
  47. String resolvedUrl = resolveUrl(executePath);
  48. HttpRequest request = HttpUtil.createPost(resolvedUrl);
  49. // 租户 header
  50. if (sysJob.getTenantId() != null) {
  51. request.header(CommonConstants.TENANT_ID, sysJob.getTenantId().toString());
  52. }
  53. // 内部调用 header(绕过安全拦截器)
  54. request.header(SecurityConstants.FROM, SecurityConstants.FROM_IN);
  55. // 预留扩展 header(可由子类或配置覆盖)
  56. addExtraHeaders(request, sysJob);
  57. // 表单参数(params 对应 Controller @RequestParam)
  58. String methodParams = sysJob.getMethodParamsValue();
  59. if (StrUtil.isNotBlank(methodParams)) {
  60. Map<String, Object> formData = new HashMap<>();
  61. formData.put("params", methodParams);
  62. request.form(formData);
  63. }
  64. request.execute();
  65. log.debug("REST任务调用完成, url={}, params={}", resolvedUrl, methodParams);
  66. }
  67. catch (Exception e) {
  68. log.error("定时任务restTaskInvok异常,执行任务:{}", sysJob.getExecutePath());
  69. throw new TaskException("定时任务restTaskInvok业务执行失败,任务:" + sysJob.getExecutePath());
  70. }
  71. }
  72. /**
  73. * 解析 URL,支持两种模式:
  74. * 1. service-name 模式:http://service-name/path → 通过 LoadBalancer 解析为实际地址
  75. * 2. host:port 模式:http://host:port/path → 直接使用
  76. *
  77. * @param executePath 原始执行路径
  78. * @return 解析后的实际 URL
  79. */
  80. private String resolveUrl(String executePath) {
  81. if (executePath == null || !executePath.startsWith("http://")) {
  82. return executePath;
  83. }
  84. try {
  85. // 提取服务名和路径,例如: http://hnqz-upms-biz/admin/schedule-task/xxx
  86. String path = executePath.substring(7);
  87. int slashIndex = path.indexOf('/');
  88. if (slashIndex == -1) {
  89. return executePath;
  90. }
  91. String serviceName = path.substring(0, slashIndex);
  92. String servicePath = path.substring(slashIndex);
  93. // 尝试通过 LoadBalancer 解析服务名
  94. ServiceInstance instance = loadBalancer.choose(serviceName);
  95. if (instance != null) {
  96. return instance.getUri().toString() + servicePath;
  97. }
  98. } catch (Exception e) {
  99. log.warn("服务名解析失败,使用原路径: {}", executePath, e);
  100. }
  101. // host:port 模式直接返回原路径
  102. return executePath;
  103. }
  104. /**
  105. * 扩展点:子类可覆盖此方法添加额外的 header
  106. * 目前预留,未来可通过 SysJob 新增字段(如 invokeHeaders)实现动态配置
  107. *
  108. * @param request HTTP 请求
  109. * @param sysJob 任务实体
  110. */
  111. protected void addExtraHeaders(HttpRequest request, SysJob sysJob) {
  112. // 预留扩展点,可通过 sysJob.invokeHeaders 或配置文件动态添加 header
  113. }
  114. }