基于feign原理实现hhtp调用

基于feign原理封装一个http

1.业务原因:公司有4个系统,领导要求将这4个平台资源整合到一个系统上
在这里插入图片描述

1.核心模块定义接口

新建一个maven项目 (只写Controller和service就行了一些其他的dto和vo根据自己业务去写,因公司项目只能提供方法思路)
Controller
在这里插入图片描述

@Controller
@RequestMapping("/xxx-sale/api")
public class XXXXXXApiController { //自己改名

    @Autowired
    private ISubSystemApiService subSystemApiService;

    private Logger logger = LoggerFactory.getLogger(SaleSubSystemApiController.class);

   
    @GetMapping("/seller")
    @ResponseBody
    public BaseResultSubSystem<List<SyncSellerVO>> getSellerListByUpdateTime(@RequestParam(value = "start") String start, @RequestParam(value = "end") String end,
                                                                             @RequestParam(value = "current") int current, @RequestParam(value = "size") int size) {
        try {
            BaseResultSubSystem result = subSystemApiService.getSellerListByUpdateTime(null, start, end, current, size);
            return result;
        } catch (Exception e) {
            logger.error("获取用户信息异常", e);
            return new ErrorResultSubSystem<>(500, "获取用户信息异常");
        }
    }
    /**
     * 校验账号密码是否匹配
     *
     * @return
     */
    @PostMapping("/checkUserPassword")
    @ResponseBody
    public BaseResultSubSystem<Boolean> checkUserPassword(@RequestBody CheckUserPasswordDTO dto) {
        try {
            return subSystemApiService.checkUserPassword(dto);
        } catch (Exception e) {
            logger.error("校验账号密码是否匹配异常", e);
            return new ErrorResultSubSystem<>(500, "校验账号密码是否匹配异常");
        }
    }
    
}

interface

public interface ISubSystemApiService {

    BaseResultSubSystem getSellerListByUpdateTime(String platform ,String start, String end, int current, int size);

    BaseResultSubSystem<Boolean> checkUserPassword(CheckUserPasswordDTO dto);
}

2.模块依赖

在这里将这个新建的maven模块install我成为核心模块,让其他主系统和子系统平台去依赖这个模块
1.子系统负责实现这个接口
2.主系统调用这个接口

3.标题写一个切面编程(主系统)

切面编程

@Component
@Aspect
@Slf4j
public class SubSystemApiAspect {

   /**
     * 定义切点 写自己的路径
     */
    @Pointcut("execution(public * com.xxx.xxx.core.service.ISubSystemApiService.* (..)) ")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint){
        try {
            String name = joinPoint.getSignature().getName();
            Object[] args = joinPoint.getArgs();
            //通过方法名获取 (path+请求方式)
            String[] method = SubApiChooser.CHOOSE_MAP.get(name);

            //获取平台 get请求的第一个参数 post请求的参数需继承基础类BaseApiDTO
            //这里是根据自己的业务去实现的,我这边对接子平台
            String platform = "get".equals(method[1])? (String) args[0]:((BaseApiDTO)args[0]).getPlatform();
            //获取参数
            String params = getParams(joinPoint, args, method[1]);

            log.info("远程调用 {}子系统 接口{} 参数:{}",args[0],joinPoint.getSignature().getName(),params);
            return SaleUtil.httpSubApi(PlatfromEnum.getByCode(platform), name, params);
        }catch (Exception e){
            e.printStackTrace();
            return new ErrorResultSubSystem(500, "系统繁忙,请稍后再试");
        }
    }

    private String getParams(ProceedingJoinPoint joinPoint, Object[] args, String httpMethod) {
        //get请求 封装Map 参数名和参数
        String params = null;
        if ("get".equals(httpMethod)){
            if (args.length>1){
                Object[] newArgs = deleteFirst(args);
                String[] parameterNames = ((CodeSignature) joinPoint.getSignature()).getParameterNames();
                Map<String,Object> getParams = new HashMap<>();
                for (int i = 0; i < newArgs.length; i++) {
                    getParams.put(parameterNames[i+1],newArgs[i]);
                }
                params = JSONUtil.toJsonStr(getParams);
            }
        }else {
            //Post请求 默认取第1个参数为Json对象
            if (args.length>0){
                params = JSONUtil.toJsonStr(args[0]);
            }
        }
        return params;
    }

    static Object[] deleteFirst(Object[] arr) {
        Object[] temp = new Object[arr.length - 1];
        System.arraycopy(arr, 1, temp, 0, temp.length);
        return temp;
    }
}

http调用工具类

@Slf4j
@Component
public class HttpUtil {
	
	//这里是我4个子平台系统的标识
    private static String A_URL;
    private static String B_URL;
    private static String C_URL;
    private static String D_URL;

    public static String getBUrl() {
        return B_URL;
    }

    @Value("${seller.api.url.b}")
    public void setbUrl(String BUrl) {
        B_URL= BUrl;
    }

    public static String getDUrl() {
        return D_URL;
    }

    @Value("${seller.api.url.d}")
    public void setDUrl(String DUrl) {
        D_URL= DUrl;
    }

    public static String getAUrl() {
        return A_URL;
    }
    @Value("${seller.api.url.a}")
    public void setDyUrl(String AUrl) {
        A_URL= AUrl;
    }

    public static String getCUrl() {
        return C_URL ;
    }
    @Value("${seller.api.url.c}")
    public void setKsUrl(String CUrl) {
        C_URL = CUrl;
    }

    /**
     * 请求子系统接口通用方法
     * @param platfromEnum 平台枚举
     * @param path 接口路径 如 "/aaa/ccc"
     * @param httpMethod 接口类型 get  post
     * @param getParams get请求时请求参数
     * @param postBody  post请求时请求参数(json字符)
     * @return
     */
    public static BaseResultSubSystem httpSubApi(PlatfromEnum platfromEnum, String path, String httpMethod, Map<String, Object> getParams, String postBody) {

        try {
            String url = getSubApiUrl(path, platfromEnum);
            String result;

            if ("get".equals(httpMethod)) {
                result = HttpUtil.get(url, getParams, 30000);
            } else {
                HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
                factory.setConnectTimeout(30000);
                RestTemplate restTemplate = new RestTemplate(factory);
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                HttpEntity<String> entity = new HttpEntity<>(postBody, headers);
                ResponseEntity<String> r = restTemplate.postForEntity(url, entity, String.class);
                result = r.getBody();
            }
            JSONObject jsonObject = JSONUtil.parseObj(result);
            if (jsonObject.getInt("code", 0) == 200) {
                return JSONUtil.toBean(result, SuccessResultSubSystem.class);
            } else {
                return new ErrorResultSubSystem(500, jsonObject.getStr("message", "子系统接口调用异常"));
            }
        } catch (Exception e) {
            log.error("子系统接口调用异常 信息:{}", e.getMessage());
            return new ErrorResultSubSystem(500, "子系统接口调用异常");
        }

    }

    /**
     *  根据方法名 请求子系统接口通用方法 V2
     * @param platfromEnum 平台枚举
     * @param methodName 方法名称 如 "getUserRole"
     * @param params 请求时请求参数 json
     * @return
     */
    public static BaseResultSubSystem httpSubApi(PlatfromEnum platfromEnum, String methodName, String params) {
        //通过方法名获取 path+请求方式
        String[] method = SubApiChooser.CHOOSE_MAP.get(methodName);
        Map<String, Object> getParams = null;
        if ("get".equals(method[1])){
            getParams = JSONUtil.toBean(params,Map.class);
            params=null;
        }
        return httpSubApi(platfromEnum,method[0],method[1],getParams,params);
    }

    /**
     * 拼接接口地址
     * @param path
     * @param platfromEnum
     * @return
     */
    private static String getSubApiUrl(String path, PlatfromEnum platfromEnum){

        String url = "";

        String code = platfromEnum.getCode();

        switch (code) {
            case "B" :
                url = B_URL + path;
                break;
            case "D" :
                url = D_URL + path;
                break;
            case "A" :
                url = A_URL + path;
                break;
            case "C" :
                url = C_URL + path;
                break;
            default:
                break;
        }

        return url;
    }

}

/**
 * @description 归属平台
 */
@AllArgsConstructor
@Getter
public enum PlatfromEnum {

    D("D", "XXX","8021","dLoginName"),
    A("A", "XXX","9021","aLoginName"),
    C("C", "XXX","8021","cLoginName"),
    B("B", "XXX","7021","bLoginName"),
    ;

    private final String code;
    private final String msg;
    private final String post;
    private final String filed;
    public static PlatfromEnum getByCode(String code) {
        if(StrUtil.isBlank(code)){
            return null;
        }
        for (PlatfromEnum platformEnum : PlatfromEnum.values()) {
            if (platformEnum.getCode().equals(code)) {
                return platformEnum;
            }
        }
        return null;
    }
    private static Map<String, PlatfromEnum> cache;

    static {
        cache = Arrays.stream(PlatfromEnum.values()).collect(Collectors.toMap(PlatfromEnum::getCode, Function.identity()));

    }

    public static PlatfromEnum of(String code) {
        return cache.get(code);
    }
}

4.核心的一步获取接口(主系统)

获取子系统平台的接口请求地址和参数名和方法名

import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @Author Lgc
 * @Date 2023/3/22 16:48
 * @Description 子系统接口信息选择器
 * @Version 1.0
 **/
@Component
@Slf4j
public class SubApiChooser {
    @Resource
    private RequestMappingHandlerMapping handlerMapping;


    /**
     * 方法选择器
     * 通过方法名获取 (path+请求方式)
     * key:方法名  value: 请求方式 路径 例:getUserRole -> ["/yeion-xxx/api/user/role","get"]
     */
    public static final Map<String, String[]> CHOOSE_MAP = new ConcurrentHashMap<>();
    /**
     * 注册 XXXXApiController控制器的API信息
     */
    @PostConstruct
    public void registerApiUrlPath() {
        SaleSubSystemApiController bean = SpringUtils.getBean(SaleSubSystemApiController.class);
        System.out.println(bean);
        //获取XXXXXXController的所有方法
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods().entrySet().stream()
                .filter(h-> SaleSubSystemApiController.class.equals(h.getValue().getBeanType())).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

        for (RequestMappingInfo info :handlerMethods.keySet()){
            //获取每个方法的映射路径
            Set<String> urlSet = info.getPatternsCondition().getPatterns();
            //获取每个方法的请求方式 get post
            Set<RequestMethod> methods = info.getMethodsCondition().getMethods();
            HandlerMethod handlerMethod = handlerMethods.get(info);
            //获取每个方法名
            String name = handlerMethod.getMethod().getName();
            for (String path : urlSet) {
                for (RequestMethod method : methods) {
                    CHOOSE_MAP.put(name, new String[]{path, method.name().toLowerCase()});
                }
            }
        }
        log.info("XXXXXXApiController的API信息注册,方法数量:{} | 注册数量:{}",handlerMethods.size(),CHOOSE_MAP.size());
    }
}

5使用方法(主系统调用)

    @SneakyThrows
    @Override
    public Boolean bindPlatform(List<PlatformReq> platformReqList)  {

        Assert.isTrue(platformReqList.size() > 0, "数据不存在");
        for (PlatformReq platformReq : platformReqList) {
            CheckUserPasswordDTO dto = new CheckUserPasswordDTO();
            dto.setPlatform(platformReq.getSource());
            dto.setLoginName(platformReq.getAccount());
            dto.setPassword(platformReq.getPwd());
            BaseResultSubSystem<Boolean> result = subSystemApiService.checkUserPassword(dto); //通过调用core模块的接口,自动走切面编程捕获
            Assert.isTrue(result.getCode() == 200, "绑定失败,请检查["+platformReq.getSource()+"] "+result.getMessage());
            Assert.isTrue(result.getData(), "绑定失败,请检查["+platformReq.getSource()+"]账号密码是否输入错误");
        }

        SysUser sysUser = new BaseController().getLoginUser().getUser();
        CompletableFuture[] futures = new CompletableFuture[2];
        futures[0] = CompletableFuture.runAsync(() -> updateCrmUserCustomer(platformReqList, sysUser));
        futures[1] = CompletableFuture.runAsync(() -> updateSysUserById(platformReqList, sysUser));
        CompletableFuture.allOf(futures).get();

        return true;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值