Merge pull request '当请求为Nacos时,Nacos返回的所有非JSON响应都会被自动转换为结构化的错误信息,同时将HTTP状态码设置为200,适合用于规范化微服务架构的响应格式。' (#831) from otto/microservices:third-party-tool-forward into third-party-tool-forward
This commit is contained in:
commit
edb5e65035
|
@ -0,0 +1,46 @@
|
||||||
|
package com.microservices.system.api;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import com.microservices.common.core.constant.ServiceNameConstants;
|
||||||
|
import com.microservices.system.api.factory.RemoteGatewayFallbackFactory;
|
||||||
|
import feign.Response;
|
||||||
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网关
|
||||||
|
*
|
||||||
|
* @author microservices
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@FeignClient(contextId = "remoteGatewayService", value = ServiceNameConstants.GATEWAY_SERVICE, fallbackFactory = RemoteGatewayFallbackFactory.class)
|
||||||
|
public interface RemoteGatewayService {
|
||||||
|
/**
|
||||||
|
* 登录Sentinel
|
||||||
|
*
|
||||||
|
* @return 响应
|
||||||
|
*/
|
||||||
|
@PostMapping("/sentinel/auth/login")
|
||||||
|
Response loginSentinel(@RequestParam("username") String username, @RequestParam("password") String password);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录Nacos
|
||||||
|
*
|
||||||
|
* @return 响应
|
||||||
|
*/
|
||||||
|
@PostMapping(value = "/nacos/v1/auth/users/login", consumes = {"application/x-www-form-urlencoded"})
|
||||||
|
Response loginNacos(Map<String, ?> formParams);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录Portainer
|
||||||
|
*
|
||||||
|
* @return 响应
|
||||||
|
*/
|
||||||
|
@PostMapping("/portainer/api/auth")
|
||||||
|
Response loginPortainer(@RequestBody JSONObject loginBody);
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ import com.microservices.common.core.constant.SecurityConstants;
|
||||||
import com.microservices.common.core.constant.ServiceNameConstants;
|
import com.microservices.common.core.constant.ServiceNameConstants;
|
||||||
import com.microservices.common.core.domain.R;
|
import com.microservices.common.core.domain.R;
|
||||||
import com.microservices.system.api.domain.SysDept;
|
import com.microservices.system.api.domain.SysDept;
|
||||||
import com.microservices.system.api.factory.RemoteCmsFallbackFactory;
|
import com.microservices.system.api.factory.RemoteZoneFallbackFactory;
|
||||||
import org.springframework.cloud.openfeign.FeignClient;
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ import java.util.List;
|
||||||
*
|
*
|
||||||
* @author microservices
|
* @author microservices
|
||||||
*/
|
*/
|
||||||
@FeignClient(contextId = "remoteZoneService", value = ServiceNameConstants.ZONE_SERVICE, fallbackFactory = RemoteCmsFallbackFactory.class)
|
@FeignClient(contextId = "remoteZoneService", value = ServiceNameConstants.ZONE_SERVICE, fallbackFactory = RemoteZoneFallbackFactory.class)
|
||||||
public interface RemoteZoneService {
|
public interface RemoteZoneService {
|
||||||
/**
|
/**
|
||||||
* 新增组织时自动创建特色专区
|
* 新增组织时自动创建特色专区
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
package com.microservices.system.api.factory;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import com.microservices.system.api.RemoteGatewayService;
|
||||||
|
import feign.Response;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.cloud.openfeign.FallbackFactory;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户服务降级处理
|
||||||
|
*
|
||||||
|
* @author microservices
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class RemoteGatewayFallbackFactory implements FallbackFactory<RemoteGatewayService> {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(RemoteGatewayFallbackFactory.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RemoteGatewayService create(Throwable throwable) {
|
||||||
|
log.error("网关服务调用失败:{}", throwable.getMessage());
|
||||||
|
return new RemoteGatewayService() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response loginSentinel(String password, String username) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response loginNacos(Map<String, ?> formParams) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response loginPortainer(JSONObject loginBody) {
|
||||||
|
System.out.println(throwable.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,3 +5,4 @@ com.microservices.system.api.factory.RemoteFileFallbackFactory
|
||||||
com.microservices.system.api.factory.RemoteCmsFallbackFactory
|
com.microservices.system.api.factory.RemoteCmsFallbackFactory
|
||||||
com.microservices.system.api.factory.RemoteZoneFallbackFactory
|
com.microservices.system.api.factory.RemoteZoneFallbackFactory
|
||||||
com.microservices.system.api.factory.RemotePmsFallbackFactory
|
com.microservices.system.api.factory.RemotePmsFallbackFactory
|
||||||
|
com.microservices.system.api.factory.RemoteGatewayFallbackFactory
|
||||||
|
|
|
@ -208,6 +208,22 @@ public class CacheConstants {
|
||||||
return GITLINK_ORG_ID_OPEN_ENTERPRISE_KEY + gitlinkOrgId;
|
return GITLINK_ORG_ID_OPEN_ENTERPRISE_KEY + gitlinkOrgId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sentinel Token缓存Key
|
||||||
|
*/
|
||||||
|
public final static String SENTINEL_TOKEN = "sentinel_token";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nacos Token缓存Key
|
||||||
|
*/
|
||||||
|
public static final String NACOS_TOKEN = "nacos_token";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Portainer Token缓存Key
|
||||||
|
*/
|
||||||
|
public static final String PORTAINER_TOKEN = "portainer_token";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 专区项目Gitlink信息 Key前缀
|
* 专区项目Gitlink信息 Key前缀
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -32,4 +32,8 @@ public class ServiceNameConstants {
|
||||||
* 项目管理模块的serviceid
|
* 项目管理模块的serviceid
|
||||||
*/
|
*/
|
||||||
public static final String PMS_SERVICE = "microservices-pms";
|
public static final String PMS_SERVICE = "microservices-pms";
|
||||||
|
/**
|
||||||
|
* 网关模块的serviceid
|
||||||
|
*/
|
||||||
|
public static final String GATEWAY_SERVICE = "microservices-gateway";
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,14 @@ public class TokenConstants {
|
||||||
* GitLink令牌标识
|
* GitLink令牌标识
|
||||||
*/
|
*/
|
||||||
public static final String GitLink_Token_Key = "autologin_trustie=";
|
public static final String GitLink_Token_Key = "autologin_trustie=";
|
||||||
|
/**
|
||||||
|
* Sentinel令牌标识
|
||||||
|
*/
|
||||||
|
public static final String Sentinel_Token_Key = "sentinel_dashboard_cookie";
|
||||||
|
/**
|
||||||
|
* Sentinel令牌标识
|
||||||
|
*/
|
||||||
|
public static final String Nacos_Token_Key = "Accesstoken";
|
||||||
/**
|
/**
|
||||||
* Cookie中令牌标识
|
* Cookie中令牌标识
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
package com.microservices.common.core.utils;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public class CookieUtil {
|
||||||
|
|
||||||
|
|
||||||
|
public static HashMap<String, String> getCookieMap(String cookie) {
|
||||||
|
HashMap<String, String> map = new HashMap<>();
|
||||||
|
if (cookie != null) {
|
||||||
|
String[] cookies = cookie.split(";");
|
||||||
|
for (String cookieStr : cookies) {
|
||||||
|
String[] cookieArr = cookieStr.split("=");
|
||||||
|
String key = cookieArr[0].trim();
|
||||||
|
String value = cookieArr[1].trim();
|
||||||
|
map.put(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getCookieValue(String cookie, String key) {
|
||||||
|
HashMap<String, String> map = getCookieMap(cookie);
|
||||||
|
return map.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String removeCookieKey(String cookie, String key) {
|
||||||
|
HashMap<String, String> map = getCookieMap(cookie);
|
||||||
|
map.remove(key);
|
||||||
|
return genCookieStr(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String genCookieStr(HashMap<String, String> cookieMap) {
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
for (String key : cookieMap.keySet()) {
|
||||||
|
stringBuilder.append(key).append("=").append(cookieMap.get(key)).append(";");
|
||||||
|
}
|
||||||
|
return stringBuilder.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,16 @@
|
||||||
package com.microservices.common.security.feign;
|
package com.microservices.common.security.feign;
|
||||||
|
|
||||||
import feign.RequestInterceptor;
|
import feign.RequestInterceptor;
|
||||||
|
import feign.codec.Encoder;
|
||||||
|
import feign.form.FormEncoder;
|
||||||
|
import org.springframework.beans.factory.ObjectFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
||||||
|
import org.springframework.cloud.openfeign.support.SpringEncoder;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Feign 配置注册
|
* Feign 配置注册
|
||||||
*
|
*
|
||||||
|
@ -12,9 +19,20 @@ import org.springframework.context.annotation.Configuration;
|
||||||
@Configuration
|
@Configuration
|
||||||
public class FeignAutoConfiguration
|
public class FeignAutoConfiguration
|
||||||
{
|
{
|
||||||
|
// 这里会由容器自动注入HttpMessageConverters的对象工厂
|
||||||
|
@Autowired
|
||||||
|
private ObjectFactory<HttpMessageConverters> messageConverters;
|
||||||
@Bean
|
@Bean
|
||||||
public RequestInterceptor requestInterceptor()
|
public RequestInterceptor requestInterceptor()
|
||||||
{
|
{
|
||||||
return new FeignRequestInterceptor();
|
return new FeignRequestInterceptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// new一个form编码器,实现支持form表单提交
|
||||||
|
// 注意这里方法名称,也就是bean的名称是什么不重要,
|
||||||
|
// 重要的是返回类型要是 Encoder 并且实现类必须是 FormEncoder 或者其子类
|
||||||
|
@Bean
|
||||||
|
public Encoder feignFormEncoder() {
|
||||||
|
return new FormEncoder(new SpringEncoder(messageConverters));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
package com.microservices.gateway;
|
||||||
|
|
||||||
|
import com.microservices.common.core.exception.ServiceException;
|
||||||
|
import com.microservices.common.core.threadPool.ThreadPoolExecutorWrap;
|
||||||
|
|
||||||
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author OTTO
|
||||||
|
*/
|
||||||
|
public class CustomExecutorFactory {
|
||||||
|
/**
|
||||||
|
* 创建线程池
|
||||||
|
* 存在并发处理的情况,设置核心线程为2,设置有界队列长度为100,最大线程数为10
|
||||||
|
* 当超出队列已满且达到最大线程数时抛出异常
|
||||||
|
* Ncpu=CPU数量
|
||||||
|
* Ucpu=目标CPU的使用率,0<=Ucpu<=1
|
||||||
|
* W/C=任务等待时间与任务计算时间的比率
|
||||||
|
* Nthreads =Ncpu*Ucpu*(1+W/C)
|
||||||
|
*/
|
||||||
|
public static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutorWrap(
|
||||||
|
4
|
||||||
|
, 10
|
||||||
|
, 10
|
||||||
|
, TimeUnit.SECONDS
|
||||||
|
, new ArrayBlockingQueue<>(100)
|
||||||
|
, Executors.defaultThreadFactory()
|
||||||
|
, (r, executor) -> {
|
||||||
|
throw new ServiceException("目前处理的人太多了,请稍后再试");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
|
@ -2,13 +2,18 @@ package com.microservices.gateway.config;
|
||||||
|
|
||||||
import feign.codec.Decoder;
|
import feign.codec.Decoder;
|
||||||
import org.springframework.beans.factory.ObjectFactory;
|
import org.springframework.beans.factory.ObjectFactory;
|
||||||
|
import org.springframework.beans.factory.ObjectProvider;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
||||||
import org.springframework.cloud.openfeign.support.ResponseEntityDecoder;
|
import org.springframework.cloud.openfeign.support.ResponseEntityDecoder;
|
||||||
import org.springframework.cloud.openfeign.support.SpringDecoder;
|
import org.springframework.cloud.openfeign.support.SpringDecoder;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.http.converter.HttpMessageConverter;
|
||||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||||
|
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author otto
|
* @author otto
|
||||||
*/
|
*/
|
||||||
|
@ -25,4 +30,10 @@ public class FeignConfig {
|
||||||
(new MappingJackson2HttpMessageConverter());
|
(new MappingJackson2HttpMessageConverter());
|
||||||
return () -> httpMessageConverters;
|
return () -> httpMessageConverters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
public HttpMessageConverters messageConverters(ObjectProvider<HttpMessageConverter<?>> converters) {
|
||||||
|
return new HttpMessageConverters(converters.orderedStream().collect(Collectors.toList()));
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -10,7 +10,9 @@ import com.microservices.common.core.utils.JwtUtils;
|
||||||
import com.microservices.common.core.utils.ServletUtils;
|
import com.microservices.common.core.utils.ServletUtils;
|
||||||
import com.microservices.common.core.utils.StringUtils;
|
import com.microservices.common.core.utils.StringUtils;
|
||||||
import com.microservices.common.redis.service.RedisService;
|
import com.microservices.common.redis.service.RedisService;
|
||||||
|
import com.microservices.gateway.CustomExecutorFactory;
|
||||||
import com.microservices.gateway.config.properties.IgnoreWhiteProperties;
|
import com.microservices.gateway.config.properties.IgnoreWhiteProperties;
|
||||||
|
import com.microservices.gateway.service.ThirdPartyToolService;
|
||||||
import com.microservices.system.api.RemoteUserService;
|
import com.microservices.system.api.RemoteUserService;
|
||||||
import com.microservices.system.api.domain.SysUserDeptRole;
|
import com.microservices.system.api.domain.SysUserDeptRole;
|
||||||
import com.microservices.system.api.utils.FeignUtils;
|
import com.microservices.system.api.utils.FeignUtils;
|
||||||
|
@ -28,9 +30,8 @@ import org.springframework.web.server.ServerWebExchange;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 网关鉴权
|
* 网关鉴权
|
||||||
|
@ -51,7 +52,12 @@ public class AuthFilter implements GlobalFilter, Ordered {
|
||||||
@Autowired
|
@Autowired
|
||||||
private RemoteUserService remoteUserService;
|
private RemoteUserService remoteUserService;
|
||||||
|
|
||||||
ExecutorService executorService = Executors.newFixedThreadPool(1);
|
@Lazy
|
||||||
|
@Autowired
|
||||||
|
private ThirdPartyToolService thirdPartyToolService;
|
||||||
|
|
||||||
|
ThreadPoolExecutor threadPoolExecutor = CustomExecutorFactory.threadPoolExecutor;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||||
|
@ -75,6 +81,9 @@ public class AuthFilter implements GlobalFilter, Ordered {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StringUtils.isEmpty(token)) {
|
if (StringUtils.isEmpty(token)) {
|
||||||
|
if (StringUtils.isNotEmpty(gitLinkCookie)) {
|
||||||
|
return unauthorizedResponse(exchange, "令牌已过期或验证不正确!");
|
||||||
|
}
|
||||||
return unauthorizedResponse(exchange, "令牌不能为空");
|
return unauthorizedResponse(exchange, "令牌不能为空");
|
||||||
}
|
}
|
||||||
Claims claims;
|
Claims claims;
|
||||||
|
@ -105,6 +114,11 @@ public class AuthFilter implements GlobalFilter, Ordered {
|
||||||
ServletUtils.addHeader(mutate, SecurityConstants.DETAILS_USERNAME, username);
|
ServletUtils.addHeader(mutate, SecurityConstants.DETAILS_USERNAME, username);
|
||||||
// 内部请求来源参数清除
|
// 内部请求来源参数清除
|
||||||
ServletUtils.removeHeader(mutate, SecurityConstants.FROM_SOURCE);
|
ServletUtils.removeHeader(mutate, SecurityConstants.FROM_SOURCE);
|
||||||
|
|
||||||
|
Mono<Void> thirdPartyRes = thirdPartyToolService.handleRequestForThirdPartyTools(exchange, request, mutate);
|
||||||
|
if (thirdPartyRes != null) {
|
||||||
|
return thirdPartyRes;
|
||||||
|
}
|
||||||
return chain.filter(exchange.mutate().request(mutate.build()).build());
|
return chain.filter(exchange.mutate().request(mutate.build()).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,7 +130,7 @@ public class AuthFilter implements GlobalFilter, Ordered {
|
||||||
boolean hasUserIdentifyList = redisService.hasKey(redisKey);
|
boolean hasUserIdentifyList = redisService.hasKey(redisKey);
|
||||||
if (!hasUserIdentifyList) {
|
if (!hasUserIdentifyList) {
|
||||||
// 网关采用异步架构,所以此处需要通过异步请求获取本系统Token
|
// 网关采用异步架构,所以此处需要通过异步请求获取本系统Token
|
||||||
Future<R<List<SysUserDeptRole>>> future = executorService.submit(
|
Future<R<List<SysUserDeptRole>>> future = threadPoolExecutor.submit(
|
||||||
() -> remoteUserService.getSysUserDeptRoleListByUserName(username, SecurityConstants.INNER));
|
() -> remoteUserService.getSysUserDeptRoleListByUserName(username, SecurityConstants.INNER));
|
||||||
try {
|
try {
|
||||||
List<SysUserDeptRole> feignResult = FeignUtils.getReturnData(
|
List<SysUserDeptRole> feignResult = FeignUtils.getReturnData(
|
||||||
|
@ -130,7 +144,7 @@ public class AuthFilter implements GlobalFilter, Ordered {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String genCookieByToken(String token) {
|
private String genCookieByToken(String token) {
|
||||||
return String.format("%s%s;",TokenConstants.GitLink_Token_Key, token);
|
return String.format("%s%s;", TokenConstants.GitLink_Token_Key, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getGitLinkRequestCookie(ServerHttpRequest request) {
|
private String getGitLinkRequestCookie(ServerHttpRequest request) {
|
||||||
|
@ -141,7 +155,7 @@ public class AuthFilter implements GlobalFilter, Ordered {
|
||||||
String token = request.getHeaders().getFirst(TokenConstants.AUTHENTICATION);
|
String token = request.getHeaders().getFirst(TokenConstants.AUTHENTICATION);
|
||||||
if (token != null) {
|
if (token != null) {
|
||||||
// 网关采用异步架构,所以此处需要通过异步请求获取本系统Token
|
// 网关采用异步架构,所以此处需要通过异步请求获取本系统Token
|
||||||
Future<R<Boolean>> future = executorService.submit(
|
Future<R<Boolean>> future = threadPoolExecutor.submit(
|
||||||
() -> remoteUserService.checkGitLinkUserLogin(token, SecurityConstants.INNER));
|
() -> remoteUserService.checkGitLinkUserLogin(token, SecurityConstants.INNER));
|
||||||
try {
|
try {
|
||||||
Boolean feignResult = FeignUtils.getReturnData(
|
Boolean feignResult = FeignUtils.getReturnData(
|
||||||
|
@ -194,7 +208,7 @@ public class AuthFilter implements GlobalFilter, Ordered {
|
||||||
private String getGitLinkToken(String cookie) {
|
private String getGitLinkToken(String cookie) {
|
||||||
if (StringUtils.isNotEmpty(cookie)) {
|
if (StringUtils.isNotEmpty(cookie)) {
|
||||||
// 网关采用异步架构,所以此处需要通过异步请求获取本系统Token
|
// 网关采用异步架构,所以此处需要通过异步请求获取本系统Token
|
||||||
Future<R<String>> future = executorService.submit(() -> remoteUserService
|
Future<R<String>> future = threadPoolExecutor.submit(() -> remoteUserService
|
||||||
.getSysUserTokenByGitLinkCookie(cookie, SecurityConstants.INNER));
|
.getSysUserTokenByGitLinkCookie(cookie, SecurityConstants.INNER));
|
||||||
R<String> feignResult;
|
R<String> feignResult;
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
package com.microservices.gateway.filter;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import com.microservices.common.core.web.domain.AjaxResult;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.reactivestreams.Publisher;
|
||||||
|
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||||
|
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||||
|
import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.core.io.buffer.DataBuffer;
|
||||||
|
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||||
|
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||||
|
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||||
|
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class GlobalResponseFilter implements GlobalFilter, Ordered {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||||
|
ServerHttpRequest request = exchange.getRequest();
|
||||||
|
|
||||||
|
String url = request.getURI().getPath();
|
||||||
|
// 当请求为Nacos时,Nacos返回的所有非JSON响应都会被自动转换为结构化的错误信息,同时将HTTP状态码设置为200,适合用于规范化微服务架构的响应格式。
|
||||||
|
if (url.toLowerCase().startsWith("/nacos")) {
|
||||||
|
ServerHttpResponse originalResponse = exchange.getResponse();
|
||||||
|
DataBufferFactory bufferFactory = originalResponse.bufferFactory();
|
||||||
|
|
||||||
|
ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
|
||||||
|
@Override
|
||||||
|
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
|
||||||
|
MediaType contentType = getHeaders().getContentType();
|
||||||
|
HttpStatus status = getStatusCode() != null ? getStatusCode() : HttpStatus.INTERNAL_SERVER_ERROR;
|
||||||
|
|
||||||
|
// 排除无内容响应和二进制类型
|
||||||
|
if (status == HttpStatus.NO_CONTENT ||
|
||||||
|
(contentType != null && (contentType.includes(MediaType.APPLICATION_OCTET_STREAM) ||
|
||||||
|
contentType.includes(MediaType.MULTIPART_FORM_DATA)))) {
|
||||||
|
return super.writeWith(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保留原始状态码
|
||||||
|
originalResponse.setStatusCode(HttpStatus.OK);
|
||||||
|
getHeaders().setContentType(MediaType.APPLICATION_JSON);
|
||||||
|
|
||||||
|
return Flux.from(body).<String>handle((dataBuffer, synchronousSink) -> {
|
||||||
|
byte[] bytes = new byte[dataBuffer.readableByteCount()];
|
||||||
|
dataBuffer.read(bytes);
|
||||||
|
//释放掉内存
|
||||||
|
DataBufferUtils.release(dataBuffer);
|
||||||
|
String result = new String(bytes, StandardCharsets.UTF_8);
|
||||||
|
if (!JSON.isValid(result)) {
|
||||||
|
synchronousSink.next(result);
|
||||||
|
} else {
|
||||||
|
synchronousSink.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
}).flatMap(resBody -> {
|
||||||
|
byte[] bytes = JSON.toJSONBytes(AjaxResult.error(resBody));
|
||||||
|
|
||||||
|
getHeaders().setContentLength(bytes.length);
|
||||||
|
DataBuffer buffer = bufferFactory.wrap(bytes);
|
||||||
|
return super.writeWith(Mono.just(buffer));
|
||||||
|
}).then();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return chain.filter(exchange.mutate().response(decoratedResponse).build());
|
||||||
|
}
|
||||||
|
return chain.filter(exchange);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOrder() {
|
||||||
|
//WRITE_RESPONSE_FILTER 之前执行
|
||||||
|
return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package com.microservices.gateway.handler;
|
package com.microservices.gateway.handler;
|
||||||
|
|
||||||
|
import com.microservices.common.core.exception.ServiceException;
|
||||||
import com.microservices.common.core.utils.ServletUtils;
|
import com.microservices.common.core.utils.ServletUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -19,33 +20,27 @@ import reactor.core.publisher.Mono;
|
||||||
*/
|
*/
|
||||||
@Order(-1)
|
@Order(-1)
|
||||||
@Configuration
|
@Configuration
|
||||||
public class GatewayExceptionHandler implements ErrorWebExceptionHandler
|
public class GatewayExceptionHandler implements ErrorWebExceptionHandler {
|
||||||
{
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(GatewayExceptionHandler.class);
|
private static final Logger log = LoggerFactory.getLogger(GatewayExceptionHandler.class);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex)
|
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
|
||||||
{
|
|
||||||
ServerHttpResponse response = exchange.getResponse();
|
ServerHttpResponse response = exchange.getResponse();
|
||||||
|
|
||||||
if (exchange.getResponse().isCommitted())
|
if (exchange.getResponse().isCommitted()) {
|
||||||
{
|
|
||||||
return Mono.error(ex);
|
return Mono.error(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
String msg;
|
String msg;
|
||||||
|
|
||||||
if (ex instanceof NotFoundException)
|
if (ex instanceof NotFoundException) {
|
||||||
{
|
|
||||||
msg = "服务未找到";
|
msg = "服务未找到";
|
||||||
}
|
} else if (ex instanceof ServiceException) {
|
||||||
else if (ex instanceof ResponseStatusException)
|
msg = ex.getMessage();
|
||||||
{
|
} else if (ex instanceof ResponseStatusException) {
|
||||||
ResponseStatusException responseStatusException = (ResponseStatusException) ex;
|
ResponseStatusException responseStatusException = (ResponseStatusException) ex;
|
||||||
msg = responseStatusException.getMessage();
|
msg = responseStatusException.getMessage();
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
msg = "内部服务器错误";
|
msg = "内部服务器错误";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.microservices.gateway.service;
|
||||||
|
|
||||||
|
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 第三方工具服务类
|
||||||
|
*
|
||||||
|
* @author OTTO
|
||||||
|
*/
|
||||||
|
public interface ThirdPartyToolService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理第三方工具请求
|
||||||
|
*
|
||||||
|
* @param exchange
|
||||||
|
* @param request 请求
|
||||||
|
* @param mutate 请求头获取器
|
||||||
|
*/
|
||||||
|
Mono<Void> handleRequestForThirdPartyTools(ServerWebExchange exchange, ServerHttpRequest request, ServerHttpRequest.Builder mutate);
|
||||||
|
}
|
|
@ -0,0 +1,189 @@
|
||||||
|
package com.microservices.gateway.service.impl;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import com.microservices.common.core.constant.CacheConstants;
|
||||||
|
import com.microservices.common.core.constant.TokenConstants;
|
||||||
|
import com.microservices.common.core.exception.ServiceException;
|
||||||
|
import com.microservices.common.core.utils.CookieUtil;
|
||||||
|
import com.microservices.common.core.utils.ServletUtils;
|
||||||
|
import com.microservices.common.core.utils.StringUtils;
|
||||||
|
import com.microservices.common.redis.service.RedisService;
|
||||||
|
import com.microservices.gateway.CustomExecutorFactory;
|
||||||
|
import com.microservices.gateway.service.ThirdPartyToolService;
|
||||||
|
import com.microservices.system.api.RemoteGatewayService;
|
||||||
|
import feign.Response;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author OTTO
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class ThirdPartyToolServiceImpl implements ThirdPartyToolService {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(ThirdPartyToolServiceImpl.class);
|
||||||
|
@Value("${thirdPartyTools.nacos.auth.username:}")
|
||||||
|
public String nacosUsername;
|
||||||
|
@Value("${thirdPartyTools.nacos.auth.password:}")
|
||||||
|
public String nacosPassword;
|
||||||
|
@Value("${thirdPartyTools.sentinel.auth.username:}")
|
||||||
|
public String sentinelUsername;
|
||||||
|
@Value("${thirdPartyTools.sentinel.auth.password:}")
|
||||||
|
public String sentinelPassword;
|
||||||
|
@Value("${thirdPartyTools.portainer.auth.username:}")
|
||||||
|
public String portainerUsername;
|
||||||
|
@Value("${thirdPartyTools.portainer.auth.password:}")
|
||||||
|
public String portainerPassword;
|
||||||
|
ThreadPoolExecutor threadPoolExecutor = CustomExecutorFactory.threadPoolExecutor;
|
||||||
|
@Lazy
|
||||||
|
@Autowired
|
||||||
|
private RemoteGatewayService remoteGatewayService;
|
||||||
|
@Autowired
|
||||||
|
private RedisService redisService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理第三方工具请求
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Mono<Void> handleRequestForThirdPartyTools(ServerWebExchange exchange, ServerHttpRequest request, ServerHttpRequest.Builder mutate) {
|
||||||
|
String url = request.getURI().getPath();
|
||||||
|
if (StringUtils.isEmpty(url)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// 处理Sentinel请求
|
||||||
|
if (url.toLowerCase().startsWith("/sentinel")) {
|
||||||
|
// 检查Cookie中是否携带sentinel cookie
|
||||||
|
String cookie = request.getHeaders().getFirst(TokenConstants.Cookie);
|
||||||
|
if (StringUtils.isNotEmpty(CookieUtil.getCookieValue(cookie, TokenConstants.Sentinel_Token_Key))) {
|
||||||
|
cookie = CookieUtil.removeCookieKey(cookie, TokenConstants.Sentinel_Token_Key);
|
||||||
|
}
|
||||||
|
String sentinelCookie = null;
|
||||||
|
if (redisService.hasKey(CacheConstants.SENTINEL_TOKEN)) {
|
||||||
|
sentinelCookie = redisService.getCacheObject(CacheConstants.SENTINEL_TOKEN);
|
||||||
|
} else {
|
||||||
|
// 网关采用异步架构,所以此处需要通过异步请求获取Sentinel Token
|
||||||
|
Future<Response> future = threadPoolExecutor.submit(() -> remoteGatewayService.loginSentinel(sentinelUsername, sentinelPassword));
|
||||||
|
try {
|
||||||
|
Response response = future.get(1, TimeUnit.SECONDS);
|
||||||
|
Map<String, Collection<String>> header = response.headers();
|
||||||
|
if (header != null && header.containsKey("set-cookie")) {
|
||||||
|
sentinelCookie = header.get("set-cookie").iterator().next();
|
||||||
|
redisService.setCacheObject(CacheConstants.SENTINEL_TOKEN, sentinelCookie, 12L, TimeUnit.HOURS);
|
||||||
|
}
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
log.error("获取Sentinel Token超时");
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
|
log.error("获取Sentinel Token失败:{}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotEmpty(sentinelCookie)) {
|
||||||
|
ServletUtils.removeHeader(mutate, TokenConstants.Cookie);
|
||||||
|
mutate.header(TokenConstants.Cookie, String.format("%s;%s;", cookie, sentinelCookie));
|
||||||
|
} else {
|
||||||
|
throw new ServiceException("Sentinel服务获取Token失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 处理Nacos请求
|
||||||
|
if (url.toLowerCase().startsWith("/nacos")) {
|
||||||
|
String nacosToken = null;
|
||||||
|
if (redisService.hasKey(CacheConstants.NACOS_TOKEN)) {
|
||||||
|
nacosToken = redisService.getCacheObject(CacheConstants.NACOS_TOKEN);
|
||||||
|
} else {
|
||||||
|
Map<String, String> nacosMap = new HashMap<>();
|
||||||
|
nacosMap.put("username", nacosUsername);
|
||||||
|
nacosMap.put("password", nacosPassword);
|
||||||
|
// 网关采用异步架构,所以此处需要通过异步请求获取Nacos Token
|
||||||
|
Future<Response> future = threadPoolExecutor.submit(() -> remoteGatewayService.loginNacos(nacosMap));
|
||||||
|
try {
|
||||||
|
Response res = future.get(1, TimeUnit.SECONDS);
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
IOUtils.copy(res.body().asInputStream(), writer, StandardCharsets.UTF_8.name());
|
||||||
|
String str = writer.toString();
|
||||||
|
if (!JSON.isValid(str)) {
|
||||||
|
return exceptionResponse(exchange, str, res.status());
|
||||||
|
}
|
||||||
|
JSONObject resJsonObject = JSONObject.parseObject(str);
|
||||||
|
nacosToken = resJsonObject.getString("accessToken");
|
||||||
|
Long tokenTtl = resJsonObject.getLong("tokenTtl");
|
||||||
|
if (nacosToken != null && tokenTtl != null) {
|
||||||
|
redisService.setCacheObject(CacheConstants.NACOS_TOKEN, nacosToken, tokenTtl - 10, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
log.error("获取Nacos Token超时");
|
||||||
|
} catch (InterruptedException | ExecutionException | IOException | NullPointerException e) {
|
||||||
|
log.error("获取Nacos Token失败:{}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotEmpty(nacosToken)) {
|
||||||
|
mutate.header(TokenConstants.Nacos_Token_Key, nacosToken);
|
||||||
|
} else {
|
||||||
|
throw new ServiceException("Nacos服务获取Token失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理portainer请求
|
||||||
|
if (url.toLowerCase().startsWith("/portainer")) {
|
||||||
|
String portainerToken = null;
|
||||||
|
if (redisService.hasKey(CacheConstants.PORTAINER_TOKEN)) {
|
||||||
|
portainerToken = redisService.getCacheObject(CacheConstants.PORTAINER_TOKEN);
|
||||||
|
} else {
|
||||||
|
// 网关采用异步架构,所以此处需要通过异步请求获取Portainer Token
|
||||||
|
JSONObject loginPortainerBody = new JSONObject();
|
||||||
|
loginPortainerBody.put("username", portainerUsername);
|
||||||
|
loginPortainerBody.put("password", portainerPassword);
|
||||||
|
Future<Response> future = threadPoolExecutor.submit(() -> remoteGatewayService.loginPortainer(loginPortainerBody));
|
||||||
|
try {
|
||||||
|
Response res = future.get(1, TimeUnit.SECONDS);
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
IOUtils.copy(res.body().asInputStream(), writer, StandardCharsets.UTF_8.name());
|
||||||
|
String str = writer.toString();
|
||||||
|
if (!JSON.isValid(str)) {
|
||||||
|
return exceptionResponse(exchange, str, res.status());
|
||||||
|
}
|
||||||
|
JSONObject resJsonObject = JSONObject.parseObject(str);
|
||||||
|
portainerToken = resJsonObject.getString("jwt");
|
||||||
|
String[] tokenSplit = portainerToken.split("\\.");
|
||||||
|
String tokenBodyStr = new String(Base64.getDecoder().decode(tokenSplit[1]), StandardCharsets.UTF_8);
|
||||||
|
JSONObject tokenBody = JSONObject.parseObject(tokenBodyStr);
|
||||||
|
Long expiration = tokenBody.getLong("exp");
|
||||||
|
Long now = new Date().getTime() / 1000;
|
||||||
|
|
||||||
|
long portainerTokenTtl = expiration - now;
|
||||||
|
if (portainerTokenTtl > 20) {
|
||||||
|
redisService.setCacheObject(CacheConstants.PORTAINER_TOKEN, portainerToken, portainerTokenTtl - 10, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
log.error("获取Portainer Token超时");
|
||||||
|
} catch (InterruptedException | ExecutionException | IOException | NullPointerException e) {
|
||||||
|
log.error("获取Portainer Token失败:{}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotEmpty(portainerToken)) {
|
||||||
|
mutate.header(TokenConstants.AUTHENTICATION, TokenConstants.PREFIX + portainerToken);
|
||||||
|
} else {
|
||||||
|
throw new ServiceException("Portainer服务获取Token失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Mono<Void> exceptionResponse(ServerWebExchange exchange, String msg, int code) {
|
||||||
|
log.error("[第三方工具请求处理异常]请求路径:{}", exchange.getRequest().getPath());
|
||||||
|
return ServletUtils.webFluxResponseWriter(exchange.getResponse(), msg, code);
|
||||||
|
}
|
||||||
|
}
|
|
@ -204,6 +204,14 @@ public class PmsCiPipelinesController extends BaseController
|
||||||
return success(pmsCiPipelinesService.savePipelineYamlByGraphicJson(id, pmsCiPipelineBuildInputVo));
|
return success(pmsCiPipelinesService.savePipelineYamlByGraphicJson(id, pmsCiPipelineBuildInputVo));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping(value = "/{id}/savePipelineYamlNew")
|
||||||
|
@ApiOperation("流水线图形构建声明式yaml并保存")
|
||||||
|
public AjaxResult savePipelineYamlNew(@ApiParam(name = "id", value = "流水线id")@PathVariable Long id,
|
||||||
|
@RequestBody @Validated PmsCiPipelineBuildYamlInputVo pmsCiPipelineBuildInputVo)
|
||||||
|
{
|
||||||
|
return success(pmsCiPipelinesService.savePipelineYamlByGraphicJsonNew(id, pmsCiPipelineBuildInputVo));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 启动流水线执行记录任务
|
* 启动流水线执行记录任务
|
||||||
*/
|
*/
|
||||||
|
@ -251,4 +259,16 @@ public class PmsCiPipelinesController extends BaseController
|
||||||
pmsCiPipelinesService.getPipelineRunJobLogs(id, run, job, response);
|
pmsCiPipelinesService.getPipelineRunJobLogs(id, run, job, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流水线执行测试报告结果查询
|
||||||
|
*/
|
||||||
|
@GetMapping(value = "/{id}/actions/runs/{run}/results")
|
||||||
|
@ApiOperation("流水线执行测试报告结果查询")
|
||||||
|
public AjaxResult getPipelineRunResults(@ApiParam(name = "id", value = "流水线id")@PathVariable Long id,
|
||||||
|
@ApiParam(name = "run", value = "执行记录序号")@PathVariable String run)
|
||||||
|
{
|
||||||
|
return success(pmsCiPipelinesService.getPipelineRunResults(id, run));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,148 @@
|
||||||
|
package com.microservices.pms.pipeline.domain.vo;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class NodeActionVo {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private final Map<String, Object> on = new LinkedHashMap<>();
|
||||||
|
private final Map<String, Object> jobs = new LinkedHashMap<>();
|
||||||
|
@JsonIgnore
|
||||||
|
private Map<String, Object> params = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
public void buildName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void parse(List<Map<String, Object>> steps, PipelineVo.PipelineJson.Nodes node){
|
||||||
|
String nodeName = Optional.ofNullable(node.getName()).orElse("");
|
||||||
|
if (nodeName.startsWith("on-")) {
|
||||||
|
buildOn(node);
|
||||||
|
}else if (nodeName.startsWith("setup-")) {
|
||||||
|
buildSetupStep(steps, node);
|
||||||
|
}else if (Objects.equals(nodeName,"shell")) {
|
||||||
|
buildShellStep(steps, node);
|
||||||
|
}else if (StringUtils.isNotEmpty(nodeName)) {
|
||||||
|
buildOtherStep(steps, node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void buildOn(PipelineVo.PipelineJson.Nodes node) {
|
||||||
|
String nodeName = node.getName();
|
||||||
|
Map<String, Object> step = new LinkedHashMap<>();
|
||||||
|
List<PipelineVo.PipelineJson.Nodes.Inputs> inputs = getInputs(node);
|
||||||
|
|
||||||
|
if (Objects.equals(nodeName, "on-schedule")) {
|
||||||
|
inputs.forEach(e -> {
|
||||||
|
step.put(e.getName(), e.getValue());
|
||||||
|
});
|
||||||
|
this.on.put(nodeName.split("-")[1], step);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputs.forEach(e -> {
|
||||||
|
step.put(e.getName(), e.getValue().replace("\'","").split(","));
|
||||||
|
});
|
||||||
|
|
||||||
|
this.on.put(nodeName.split("-")[1], step);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void buildJobs(String jobKey, String jobName, List<Map<String, Object>> steps) {
|
||||||
|
if (steps.isEmpty()) return;
|
||||||
|
Map<String, Object> job = new LinkedHashMap<>();
|
||||||
|
job.put("runs-on", "ubuntu-latest");
|
||||||
|
job.put("name", jobName);
|
||||||
|
job.put("steps", steps);
|
||||||
|
this.jobs.put(jobKey+(this.jobs.size()), job);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void buildSetupStep(List<Map<String, Object>> steps, PipelineVo.PipelineJson.Nodes node) {
|
||||||
|
String nodeName = node.getName();
|
||||||
|
Map<String, Object> step = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
List<PipelineVo.PipelineJson.Nodes.Inputs> inputs = getInputs(node);
|
||||||
|
|
||||||
|
if (nodeName.startsWith("setup-java")) {
|
||||||
|
|
||||||
|
Map<String, String> name2Value = inputs.stream()
|
||||||
|
.collect(Collectors.toMap(e -> e.getName(), e -> e.getValue(), (o, n) -> n));
|
||||||
|
|
||||||
|
Map<String, Object> with = new LinkedHashMap<>();
|
||||||
|
if (name2Value.containsKey("download_url") && StringUtils.isNotBlank(name2Value.get("download_url"))) {
|
||||||
|
steps.add(Collections.singletonMap("run", "download_url=" + name2Value.get("download_url") + " && " + "wget -O $RUNNER_TEMP/java_package.tar.gz $download_url"));
|
||||||
|
with.put("distribution", "jdkfile");
|
||||||
|
with.put("jdkFile", "${{ runner.temp }}/java_package.tar.gz");
|
||||||
|
}
|
||||||
|
step.put("name", node.getLabel());
|
||||||
|
step.put("uses", node.getFull_name().trim());
|
||||||
|
if (!inputs.isEmpty()) {
|
||||||
|
|
||||||
|
inputs.forEach(i -> {
|
||||||
|
with.put(i.getName(), Optional.ofNullable(i.getValue()).orElse(""));
|
||||||
|
});
|
||||||
|
|
||||||
|
step.put("with", with);
|
||||||
|
}
|
||||||
|
steps.add(step);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
step.put("name", node.getLabel());
|
||||||
|
step.put("uses", node.getFull_name().trim());
|
||||||
|
if (!inputs.isEmpty()) {
|
||||||
|
Map<String, Object> with = new LinkedHashMap<>();
|
||||||
|
inputs.forEach(i -> {
|
||||||
|
with.put(i.getName(), Optional.ofNullable(i.getValue()).orElse(""));
|
||||||
|
});
|
||||||
|
|
||||||
|
step.put("with", with);
|
||||||
|
}
|
||||||
|
steps.add(step);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void buildShellStep(List<Map<String, Object>> steps, PipelineVo.PipelineJson.Nodes node) {
|
||||||
|
Map<String, Object> step = new LinkedHashMap<>();
|
||||||
|
step.put("name", node.getLabel());
|
||||||
|
List<PipelineVo.PipelineJson.Nodes.Inputs> inputs = Optional.ofNullable(node.getInputs()).orElse(new ArrayList<>());
|
||||||
|
inputs.forEach(i -> {
|
||||||
|
step.put(i.getName(), Optional.ofNullable(i.getValue()).orElse(""));
|
||||||
|
});
|
||||||
|
|
||||||
|
steps.add(step);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void buildOtherStep(List<Map<String, Object>> steps, PipelineVo.PipelineJson.Nodes node) {
|
||||||
|
Map<String, Object> step = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
List<PipelineVo.PipelineJson.Nodes.Inputs> inputs = getInputs(node);
|
||||||
|
|
||||||
|
step.put("name", node.getLabel());
|
||||||
|
step.put("uses", node.getFull_name().trim());
|
||||||
|
if (!inputs.isEmpty()) {
|
||||||
|
Map<String, Object> with = new LinkedHashMap<>();
|
||||||
|
inputs.forEach(i -> {
|
||||||
|
with.put(i.getName(), Optional.ofNullable(i.getValue()).orElse(""));
|
||||||
|
});
|
||||||
|
|
||||||
|
step.put("with", with);
|
||||||
|
}
|
||||||
|
steps.add(step);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<PipelineVo.PipelineJson.Nodes.Inputs> getInputs(PipelineVo.PipelineJson.Nodes node) {
|
||||||
|
return Optional.ofNullable(node.getInputs()).orElse(new ArrayList<>())
|
||||||
|
.stream()
|
||||||
|
.filter(e -> StringUtils.isNotBlank(e.getValue())).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,284 @@
|
||||||
|
package com.microservices.pms.pipeline.domain.vo;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PipelineVo implements Serializable {
|
||||||
|
private String pipelineName;
|
||||||
|
|
||||||
|
private PipelineJson pipelineJson;
|
||||||
|
|
||||||
|
public String getPipelineName() {
|
||||||
|
return this.pipelineName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPipelineName(String pipelineName) {
|
||||||
|
this.pipelineName = pipelineName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PipelineJson getPipelineJson() {
|
||||||
|
return this.pipelineJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPipelineJson(PipelineJson pipelineJson) {
|
||||||
|
this.pipelineJson = pipelineJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PipelineJson implements Serializable {
|
||||||
|
private List<Nodes> nodes;
|
||||||
|
|
||||||
|
private List<Edges> edges;
|
||||||
|
|
||||||
|
public List<Nodes> getNodes() {
|
||||||
|
return this.nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNodes(List<Nodes> nodes) {
|
||||||
|
this.nodes = nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Edges> getEdges() {
|
||||||
|
return this.edges;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEdges(List<Edges> edges) {
|
||||||
|
this.edges = edges;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Nodes implements Serializable {
|
||||||
|
private Integer action_node_types_id;
|
||||||
|
|
||||||
|
private String img;
|
||||||
|
|
||||||
|
private List<Inputs> inputs;
|
||||||
|
|
||||||
|
private String icon;
|
||||||
|
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
private String label;
|
||||||
|
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
private Integer sort_no;
|
||||||
|
|
||||||
|
private Integer use_count;
|
||||||
|
|
||||||
|
private String full_name;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private Integer x;
|
||||||
|
|
||||||
|
private Double y;
|
||||||
|
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
private Boolean isCluster;
|
||||||
|
|
||||||
|
private String yaml;
|
||||||
|
|
||||||
|
public Integer getAction_node_types_id() {
|
||||||
|
return this.action_node_types_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAction_node_types_id(Integer action_node_types_id) {
|
||||||
|
this.action_node_types_id = action_node_types_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getImg() {
|
||||||
|
return this.img;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImg(String img) {
|
||||||
|
this.img = img;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Inputs> getInputs() {
|
||||||
|
return this.inputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInputs(List<Inputs> inputs) {
|
||||||
|
this.inputs = inputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIcon() {
|
||||||
|
return this.icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIcon(String icon) {
|
||||||
|
this.icon = icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return this.description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLabel() {
|
||||||
|
return this.label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLabel(String label) {
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return this.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getSort_no() {
|
||||||
|
return this.sort_no;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSort_no(Integer sort_no) {
|
||||||
|
this.sort_no = sort_no;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getUse_count() {
|
||||||
|
return this.use_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUse_count(Integer use_count) {
|
||||||
|
this.use_count = use_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFull_name() {
|
||||||
|
return this.full_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFull_name(String full_name) {
|
||||||
|
this.full_name = full_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getX() {
|
||||||
|
return this.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setX(Integer x) {
|
||||||
|
this.x = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getY() {
|
||||||
|
return this.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setY(Double y) {
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getIsCluster() {
|
||||||
|
return this.isCluster;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsCluster(Boolean isCluster) {
|
||||||
|
this.isCluster = isCluster;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getYaml() {
|
||||||
|
return this.yaml;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setYaml(String yaml) {
|
||||||
|
this.yaml = yaml;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Inputs implements Serializable {
|
||||||
|
private Boolean is_required;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
private String input_type;
|
||||||
|
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getIs_required() {
|
||||||
|
return this.is_required;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIs_required(Boolean is_required) {
|
||||||
|
this.is_required = is_required;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getInput_type() {
|
||||||
|
return this.input_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInput_type(String input_type) {
|
||||||
|
this.input_type = input_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Edges implements Serializable {
|
||||||
|
private String source;
|
||||||
|
|
||||||
|
private String target;
|
||||||
|
|
||||||
|
public String getSource() {
|
||||||
|
return this.source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSource(String source) {
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTarget() {
|
||||||
|
return this.target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTarget(String target) {
|
||||||
|
this.target = target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -91,6 +91,8 @@ public interface IPmsCiPipelinesService
|
||||||
|
|
||||||
JSONObject savePipelineYamlByGraphicJson(Long id, PmsCiPipelineBuildYamlInputVo pmsCiPipelineBuildInputVo);
|
JSONObject savePipelineYamlByGraphicJson(Long id, PmsCiPipelineBuildYamlInputVo pmsCiPipelineBuildInputVo);
|
||||||
|
|
||||||
|
JSONObject savePipelineYamlByGraphicJsonNew(Long id, PmsCiPipelineBuildYamlInputVo pmsCiPipelineBuildInputVo);
|
||||||
|
|
||||||
JSONObject reRunPipelineRunsJob(Long id, String run, String job);
|
JSONObject reRunPipelineRunsJob(Long id, String run, String job);
|
||||||
|
|
||||||
JSONObject reRunAllJobsInPipelineRun(Long id, String run);
|
JSONObject reRunAllJobsInPipelineRun(Long id, String run);
|
||||||
|
@ -98,4 +100,6 @@ public interface IPmsCiPipelinesService
|
||||||
void getPipelineRunJobLogs(Long id, String run, String job, HttpServletResponse response);
|
void getPipelineRunJobLogs(Long id, String run, String job, HttpServletResponse response);
|
||||||
|
|
||||||
JSONObject runPipelineRunsJob(Long id, String workflow, String ref);
|
JSONObject runPipelineRunsJob(Long id, String workflow, String ref);
|
||||||
|
|
||||||
|
JSONObject getPipelineRunResults(Long id, String run);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
package com.microservices.pms.pipeline.service.impl;
|
package com.microservices.pms.pipeline.service.impl;
|
||||||
|
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.alibaba.fastjson2.JSONObject;
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.microservices.common.core.exception.ServiceException;
|
import com.microservices.common.core.exception.ServiceException;
|
||||||
import com.microservices.common.core.utils.DateUtils;
|
import com.microservices.common.core.utils.DateUtils;
|
||||||
import com.microservices.common.core.utils.StringUtils;
|
import com.microservices.common.core.utils.StringUtils;
|
||||||
|
@ -23,6 +28,7 @@ import com.microservices.pms.pipeline.domain.vo.*;
|
||||||
import com.microservices.pms.pipeline.service.IPmsCiPipelineGraphicsService;
|
import com.microservices.pms.pipeline.service.IPmsCiPipelineGraphicsService;
|
||||||
import com.microservices.pms.utils.PmsConstants;
|
import com.microservices.pms.utils.PmsConstants;
|
||||||
import com.microservices.pms.utils.PmsGitLinkRequestUrl;
|
import com.microservices.pms.utils.PmsGitLinkRequestUrl;
|
||||||
|
import com.microservices.pms.utils.YamlUtil;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
|
@ -363,6 +369,34 @@ public class PmsCiPipelinesServiceImpl implements IPmsCiPipelinesService {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JSONObject savePipelineYamlByGraphicJsonNew(Long id, PmsCiPipelineBuildYamlInputVo pmsCiPipelineBuildInputVo) {
|
||||||
|
PmsCiPipelines pmsCiPipelines = pmsCiPipelinesMapper.selectPmsCiPipelinesById(id);
|
||||||
|
if (pmsCiPipelines == null) {
|
||||||
|
throw new ServiceException("该项目流水线不存在(流水线ID[%s])", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
PipelineVo vo = JSON.parseObject(JSON.toJSONString(pmsCiPipelineBuildInputVo), PipelineVo.class);
|
||||||
|
List<PipelineVo.PipelineJson.Nodes> nodes = vo.getPipelineJson().getNodes();
|
||||||
|
String pipelineName = vo.getPipelineName();
|
||||||
|
NodeActionVo actionVo = new NodeActionVo();
|
||||||
|
actionVo.buildName(pipelineName);
|
||||||
|
nodes.forEach(s -> {
|
||||||
|
List<Map<String, Object>> steps = new ArrayList<>();
|
||||||
|
actionVo.parse(steps, s);
|
||||||
|
actionVo.buildJobs("job", s.getLabel()+String.format("[%s]",s.getId().trim()), steps);
|
||||||
|
});
|
||||||
|
String yaml = YamlUtil.toYaml(actionVo);
|
||||||
|
JSONObject result = new JSONObject();
|
||||||
|
result.put("pipeline_yaml", yaml);
|
||||||
|
return result;
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("流水线图形Json解析失败)", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JSONObject reRunPipelineRunsJob(Long id, String run, String job) {
|
public JSONObject reRunPipelineRunsJob(Long id, String run, String job) {
|
||||||
PmsCiPipelines pmsCiPipelines = pmsCiPipelinesMapper.selectPmsCiPipelinesById(id);
|
PmsCiPipelines pmsCiPipelines = pmsCiPipelinesMapper.selectPmsCiPipelinesById(id);
|
||||||
|
@ -414,6 +448,23 @@ public class PmsCiPipelinesServiceImpl implements IPmsCiPipelinesService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JSONObject getPipelineRunResults(Long id, String run) {
|
||||||
|
PmsCiPipelines pmsCiPipelines = pmsCiPipelinesMapper.selectPmsCiPipelinesById(id);
|
||||||
|
if (pmsCiPipelines == null) {
|
||||||
|
throw new ServiceException("该项目流水线不存在(流水线ID[%s])", id);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return gitLinkRequestHelper.doGet(PmsGitLinkRequestUrl.GET_PIPELINE_RUN_RESULTS(pmsCiPipelines.getOwner(),
|
||||||
|
pmsCiPipelines.getRepoIdentifier(),
|
||||||
|
run));
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error(e.getMessage());
|
||||||
|
throw new ServiceException("获取流水线运行结果信息失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void saveOrUpdateGraphicJsonToLocal(Long id, PmsCiPipelineBuildYamlInputVo pmsCiPipelineBuildInputVo) {
|
private void saveOrUpdateGraphicJsonToLocal(Long id, PmsCiPipelineBuildYamlInputVo pmsCiPipelineBuildInputVo) {
|
||||||
PmsCiPipelineGraphics existentialPmsCiPipelineGraphics = pmsCiPipelineGraphicsService.selectPmsCiPipelineGraphicsByPmsCiPipelineId(id);
|
PmsCiPipelineGraphics existentialPmsCiPipelineGraphics = pmsCiPipelineGraphicsService.selectPmsCiPipelineGraphicsByPmsCiPipelineId(id);
|
||||||
PmsCiPipelineGraphics pmsCiPipelineGraphics = new PmsCiPipelineGraphics();
|
PmsCiPipelineGraphics pmsCiPipelineGraphics = new PmsCiPipelineGraphics();
|
||||||
|
|
|
@ -362,4 +362,8 @@ public class PmsGitLinkRequestUrl extends GitLinkRequestUrl {
|
||||||
public static GitLinkRequestUrl SAVE_PIPELINE_YAML(String owner, String repoIdentifier) {
|
public static GitLinkRequestUrl SAVE_PIPELINE_YAML(String owner, String repoIdentifier) {
|
||||||
return getGitLinkRequestUrl(String.format("/api/v1/%s/%s/pipelines/save_yaml", owner, repoIdentifier));
|
return getGitLinkRequestUrl(String.format("/api/v1/%s/%s/pipelines/save_yaml", owner, repoIdentifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static GitLinkRequestUrl GET_PIPELINE_RUN_RESULTS(String owner, String repoIdentifier, String run){
|
||||||
|
return getGitLinkRequestUrl(String.format("/api/v1/%s/%s/pipelines/run_results.json?run_id=%s", owner, repoIdentifier, run));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
package com.microservices.pms.utils;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
|
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
||||||
|
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
|
||||||
|
import com.microservices.common.core.exception.ServiceException;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class YamlUtil {
|
||||||
|
/**
|
||||||
|
* 将yaml字符串转成类对象
|
||||||
|
*
|
||||||
|
* @param yamlStr 字符串
|
||||||
|
* @param clazz 目标类
|
||||||
|
* @param <T> 泛型
|
||||||
|
* @return 目标类
|
||||||
|
*/
|
||||||
|
public static <T> T toObject(String yamlStr, Class<T> clazz) {
|
||||||
|
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
|
||||||
|
mapper.findAndRegisterModules();
|
||||||
|
try {
|
||||||
|
return mapper.readValue(yamlStr, clazz);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
log.error("yaml文本解析成java对象错误:", e);
|
||||||
|
throw new ServiceException("yaml文本解析成java对象错误!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将类对象转yaml字符串
|
||||||
|
*
|
||||||
|
* @param object 对象
|
||||||
|
* @return yaml字符串
|
||||||
|
*/
|
||||||
|
public static String toYaml(Object object) {
|
||||||
|
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
|
||||||
|
mapper.findAndRegisterModules();
|
||||||
|
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
|
||||||
|
mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
|
||||||
|
mapper = new ObjectMapper(new YAMLFactory().disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER));
|
||||||
|
StringWriter stringWriter = new StringWriter();
|
||||||
|
try {
|
||||||
|
mapper.writeValue(stringWriter, object);
|
||||||
|
return stringWriter.toString();
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("Java对象解析成Yaml文本错误", e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue