zoukankan      html  css  js  c++  java
  • IDEA项目搭建十三——服务消费端与生产端通信实现

    一、简介

    之前已经完成了EurekaClient的服务生产者和Feign的服务消费者模块的搭建,现在实现统一的通信约定

    (1) 统一Request结构

    (2) 统一Response结构

    (3) 统一Error通知

    二、代码

    1、创建统一请求对象ServiceRequest<>实际参数就是这个泛型,使用统一的构造进行创建便于对数据进行统一的加密传输

    import java.util.Date;
    
    /**
     * 客户端请求内容对象
     */
    public class ServiceRequest<T> {
    
        //region 属性
    
        /**
         * 请求唯一ID
         */
        private String requestID;
        /**
         * 请求时间
         */
        private Date requestTime;
        /**
         * 客户端代号
         */
        private String clientCode;
        /**
         * 请求签名
         */
        private String requestSign;
        /**
         * 请求参数
         */
        private T reqData;
    
        public String getRequestID() {
            return requestID;
        }
    
        public void setRequestID(String requestID) {
            this.requestID = requestID;
        }
    
        public Date getRequestTime() {
            return requestTime;
        }
    
        public void setRequestTime(Date requestTime) {
            this.requestTime = requestTime;
        }
    
        public String getClientCode() {
            return clientCode;
        }
    
        public void setClientCode(String clientCode) {
            this.clientCode = clientCode;
        }
    
        public String getRequestSign() {
            return requestSign;
        }
    
        public void setRequestSign(String requestSign) {
            this.requestSign = requestSign;
        }
    
        /**
         * 禁止使用此方法-不能删除内部自动取值需要使用
         * @return
         */
        @Deprecated
        public T getReqData() {
            return reqData;
        }
    
        /**
         * 禁止使用此方法-不能删除内部自动赋值需要使用
         * @param reqData
         */
        @Deprecated
        public void setReqData(T reqData) {
            this.reqData = reqData;
        }
    
        //endregion
    
        //设置类的无参构造为私有禁止外部实例化
        private ServiceRequest() {
        }
    
        /**
         * 创建请求对象
         * @param data
         */
        public ServiceRequest(T data) {
            //后期从此处增加加解密代码...
            this.reqData = data;
        }
    
        /**
         * 获取请求参数
         * @param req
         * @param <T>
         * @return
         */
        public static <T> T getRequestData(ServiceRequest<T> req) {
            //后期从此处增加加解密代码...
            T obj = req.getReqData();
            return obj;
        }
    }

    2、创建统一响应对象ServiceResponse<>实际响应就是这个泛型,使用统一的取值便于有需求时对响应数据进行统一的解密

    import com.google.common.base.Strings;
    import java.util.Date;
    
    /**
     * 服务端响应结果对象
     */
    public class ServiceResponse<T> {
    
        //region 属性
    
        /**
         * 请求唯一ID
         */
        private String requestID;
        /**
         * 响应代号
         * <p>
         * 000000 - 正确
         */
        private ServiceCodeMsgEnum resCodeMsg;
        /**
         * 响应时间
         */
        private Date resTime;
        /**
         * 响应结果
         */
        private T resData;
    
        public String getRequestID() {
            return requestID;
        }
    
        public void setRequestID(String requestID) {
            this.requestID = requestID;
        }
    
        public ServiceCodeMsgEnum getResCodeMsg() {
            return resCodeMsg;
        }
    
        public void setResCodeMsg(ServiceCodeMsgEnum resCodeMsg) {
            this.resCodeMsg = resCodeMsg;
        }
    
        public Date getResTime() {
            return resTime;
        }
    
        public void setResTime(Date resTime) {
            this.resTime = resTime;
        }
    
        /**
         * 禁止使用此方法-不能删除内部取值需要使用
         *
         * @return
         */
        @Deprecated
        public T getResData() {
            return resData;
        }
    
        /**
         * 禁止使用此方法-不能删除内部赋值需要使用
         *
         * @param resData
         */
        @Deprecated
        public void setResData(T resData) {
            this.resData = resData;
        }
    
        //endregion
    
        //设置类的无参构造为私有禁止外部实例化,只能通过下方静态方法创建
        private ServiceResponse() {
        }
    
        /**
         * 创建执行正确响应对象
         * @param data
         */
        public ServiceResponse(T data) {
            this.resCodeMsg = ServiceCodeMsgEnum.Success;
            this.resData = data;
        }
    
        /**
         * 创建执行错误响应对象
         * @param codeMsg
         */
        public ServiceResponse(ServiceCodeMsgEnum codeMsg) {
            this.resCodeMsg = codeMsg;
            this.resData = null;
        }
    
        /**
         * 获取响应CodeMsg(外部WebApi专用)
         *
         * @param res
         * @param <T>
         * @return ServiceCodeMsgEnum.Success为正确
         */
        private static <T> ServiceCodeMsgEnum getResponseCodeMsg(ServiceResponse<T> res) {
            return res.getResCodeMsg();
        }
    
        /**
         * 获取响应Msg(内部站点专用)
         *
         * @param res
         * @param <T>
         * @return null为正确
         */
        public static <T> String getResponseMsg(ServiceResponse<T> res) {
            return Strings.emptyToNull(res.getResCodeMsg().getMsg());
        }
    
        /**
         * 获取响应参数
         *
         * @param res
         * @param <T>
         * @return
         */
        public static <T> T getResponseData(ServiceResponse<T> res) {
            return res.getResData();
        }
    }

    3、创建统一响应结果枚举,这里可以统一控制响应CodeMsg的对应关系,使用也简单直观

    /**
     * 服务通信CodeMsg枚举
     */
    public enum ServiceCodeMsgEnum {
    
        Success("000000", null),
        Error("999999", "系统异常");
    
        //region
    
        private String code;
        private String msg;
    
        ServiceCodeMsgEnum(String code, String msg) {
            this.code = code;
            this.msg = msg;
        }
    
        public String getCode() {
            return code;
        }
    
        public void setCode(String code) {
            this.code = code;
        }
    
        public String getMsg() {
            return msg;
        }
    
        public void setMsg(String msg) {
            this.msg = msg;
        }
    
        //endregion
    }

    4、服务生产者这边,统一入参和返回值都是ServiceRequest和ServiceResponse,又可以根据泛型来识别到底是什么对象

    import com.google.gson.Gson;
    import com.ysl.ts.common.serviceModel.ServiceCodeMsgEnum;
    import com.ysl.ts.common.serviceModel.ServiceRequest;
    import com.ysl.ts.common.serviceModel.ServiceResponse;
    import com.ysl.ts.core.model.base.ts_base.SysUserModel;
    import com.ysl.ts.core.service.base.service.SysUserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import java.util.List;
    import java.util.Map;
    
    @Controller
    @RequestMapping("/api/sysUser")
    public class SysUserController extends BaseController {
        @Autowired
        SysUserService service;
    
        @ResponseBody
        @RequestMapping("/save")
        public ServiceResponse<Integer> save(@RequestBody ServiceRequest<SysUserModel> req) {
            try {
                //使用统一的方法获取请求Data
                SysUserModel agent = ServiceRequest.getRequestData(req);
                //调用Service
                int result = service.save(agent);
                //响应-成功
                return new ServiceResponse<>(result);
            } catch (Exception e) {
                //响应-错误
                return new ServiceResponse<>(ServiceCodeMsgEnum.Error);
            }
        }
    
        @ResponseBody
        @RequestMapping("/delete")
        public ServiceResponse<Integer> delete(@RequestBody ServiceRequest<Integer> req) {
            try {
                //获取请求Data
                int id = ServiceRequest.getRequestData(req);
                //调用Service
                int result = service.delete(id);
                //响应-成功
                return new ServiceResponse<>(result);
            } catch (Exception e) {
                //响应-错误
                return new ServiceResponse<>(ServiceCodeMsgEnum.Error);
            }
        }
    
        @ResponseBody
        @RequestMapping("/get")
        public ServiceResponse<SysUserModel> get(@RequestBody ServiceRequest<Integer> req) {
            try {
                //获取请求Data
                int id = ServiceRequest.getRequestData(req);
                //调用Service
                SysUserModel result = service.get(id);
                //响应-成功
                return new ServiceResponse<>(result);
            } catch (Exception e) {
                //响应-错误
                return new ServiceResponse<>(ServiceCodeMsgEnum.Error);
            }
        }
    
        @ResponseBody
        @RequestMapping("/list")
        public ServiceResponse<List<SysUserModel>> list(@RequestBody ServiceRequest<String> req) {
            try {
                //获取请求Data
                String search = ServiceRequest.getRequestData(req);
                //调用Service
                List<SysUserModel> result = service.list();
                //响应-成功
                return new ServiceResponse<>(result);
            } catch (Exception e) {
                //响应-错误
                return new ServiceResponse<>(ServiceCodeMsgEnum.Error);
            }
        }
    }

    5、服务消费者这边,使用了Feign所以需要一个接口来实现调用,我们直接传入ServiceRequest<>来做统一的请求对象,返回ServiceResponse<>来做统一的响应对象

    import com.ysl.ts.common.serviceModel.ServiceRequest;
    import com.ysl.ts.common.serviceModel.ServiceResponse;
    import com.ysl.ts.core.model.base.ts_base.SysUserModel;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.stereotype.Service;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import java.util.List;
    
    @Service
    @FeignClient("YSL-TS-Core-Service-Base")//服务生产者名称
    @RequestMapping("/api/sysUser")//服务路由
    public interface SysUserService {
    
        @RequestMapping("/save")
        ServiceResponse<Integer> save(@RequestBody ServiceRequest<SysUserModel> req);
    
        @RequestMapping("/delete")
        ServiceResponse<Integer> delete(@RequestBody ServiceRequest<Integer> req);
    
        @RequestMapping("/get")
        ServiceResponse<SysUserModel> get(@RequestBody ServiceRequest<Integer> req);
    
        @RequestMapping("/list")
        ServiceResponse<List<SysUserModel>> list(@RequestBody ServiceRequest<String> req);
    }

    6、获得基础数据实体后,在页面展现之前可能有些字段需要进行翻译,比如状态1:启用,0:禁用等等,这部分建立一个ModelEx类继承Model类,把其中需要翻译的字段写在ModelEx中,

    用以下转换类对实体值进行拷贝,然后页面接收这个ModelEx对象,这样默认可以使用父类中的属性,如果要显示翻译的就用子类中的属性即可

    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * Model 转换类
     *
     * @param <TModel>   Model类型对象
     * @param <TModelEx> ModelEx类型对象*/
    public abstract class AbstractModelConvertor<TModel, TModelEx extends TModel> {
    
        /**
         * 转换
         *
         * @param model   Model类型对象
         * @param modelEx ModelEx类型对象
         * @return
         */
        public TModelEx convert(TModel model, Class<TModelEx> modelEx) {
            TModelEx ex = new DeepClone().clone(model, modelEx);
            convertFields(ex);//填充翻译字段,需要子类重写
            return ex;
        }
    
        /**
         * 列表转换
         *
         * @param modelList Model类型对象列表
         * @param modelEx   ModelEx类型对象
         * @return
         */
        public List<TModelEx> convert(List<TModel> modelList, Class<TModelEx> modelEx) {
            List<TModelEx> list = new ArrayList<>();
    
            for (TModel tModel : modelList) {
                list.add(convert(tModel, modelEx));
            }
    
            return list;
        }
    
        /**
         * 字段转换接口
         *
         * @param modelEx
         */
        protected abstract void convertFields(TModelEx modelEx);
    }

    实际上就是使用Gson对实体做了一次序列化很简单

    import com.google.gson.Gson;
    
    /**
     * 深入拷贝
     */
    public class DeepClone {
    
        private Gson gson = new Gson();
    
        /**
         * 深拷贝
         *
         * @param t     源数据
         * @param clazz 目标类
         * @param <T>   源数据类型
         * @param <K>   目标类型
         * @return
         */
        public <T, K> K clone(T t, Class<K> clazz) {
            return gson.fromJson(gson.toJson(t), clazz);
        }
    }

     7、每一个实体都继承抽象基类,这样就可以直接使用转换方法了

    import com.ysl.ts.common.AbstractModelConvertor;
    import com.ysl.ts.core.model.base.ts_base.SysUserModel;
    import com.ysl.ts.core.model.base.ts_base.ex.SysUserModelEx;
    
    public class SysUserConvertor extends AbstractModelConvertor<SysUserModel, SysUserModelEx> {
        /**
         * 填充待翻译字段
         */
        @Override
        protected void convertFields(SysUserModelEx sysUserModelEx) {
    
        }
    }

    8、下面就是页面Controller的Action调用了

    import com.ysl.ts.common.serviceModel.ServiceRequest;
    import com.ysl.ts.common.serviceModel.ServiceResponse;
    import com.ysl.ts.core.model.base.ts_base.SysUserModel;
    import com.ysl.ts.core.model.base.ts_base.ex.SysUserModelEx;
    import com.ysl.ts.web.base.modelConvertors.ts_base.SysUserConvertor;
    import com.ysl.ts.web.base.service.SysUserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import java.util.ArrayList;
    import java.util.List;
    
    @Controller
    @Component("AdminSysUser")
    @RequestMapping("/sysUser")
    public class SysUserController extends BaseController {
        //自动注入Feign接口对象
        @Autowired
        SysUserService service;
    
        @RequestMapping("/save")
        public boolean save(SysUserModel agent) {
            boolean result = false;
            //创建ServiceRequest
            ServiceRequest<SysUserModel> req = new ServiceRequest<>(agent);
            //调用Service并获得ServiceResponse
            ServiceResponse<Integer> res = service.save(req);
            //解析获得Code
            String msg = ServiceResponse.getResponseMsg(res);
            //NULL代表响应正常
            if (null == msg)
            {
                //解析获得T类型
                result = ServiceResponse.getResponseData(res) > 0;
            }
            return result;
        }
    
        @RequestMapping("/delete")
        public boolean delete(int id){
            boolean result = false;
            //创建ServiceRequest
            ServiceRequest<Integer> req = new ServiceRequest<>(id);
            //调用Service并获得ServiceResponse
            ServiceResponse<Integer> res = service.delete(req);
            //解析获得Code
            String msg = ServiceResponse.getResponseMsg(res);
            //NULL代表响应正常
            if (null == msg)
            {
                //解析获得T类型
                result = ServiceResponse.getResponseData(res) > 0;
            }
            return result;
        }
    
        @ResponseBody
        @RequestMapping("/get")
        public SysUserModelEx get(Model model, int id) {
            SysUserModelEx result = new SysUserModelEx();
            //创建ServiceRequest
            ServiceRequest<Integer> req = new ServiceRequest<>(id);
            //调用Service并获得ServiceResponse
            ServiceResponse<SysUserModel> res = service.get(req);
            //解析获得Code
            String msg = ServiceResponse.getResponseMsg(res);
            //NULL代表响应正常
            if (null == msg)
            {
                //解析获得T类型
                SysUserModel tmp = ServiceResponse.getResponseData(res);
                //翻译所需字段
                result = new SysUserConvertor().convert(tmp, SysUserModelEx.class);
            }
            return result;
            //model.addAttribute("model", result);
            //return "/hotel/get";
        }
    
        //直接返回json不写页面了
        @ResponseBody
        @RequestMapping("/list")
        public List<SysUserModelEx> list(String search) {
            List<SysUserModelEx> result = new ArrayList<>();
            //创建ServiceRequest
            ServiceRequest<String> req = new ServiceRequest<>(search);
            //调用Service并获得ServiceResponse
            ServiceResponse<List<SysUserModel>> res = service.list(req);
            //解析获得Code
            String msg = ServiceResponse.getResponseMsg(res);
            //NULL代表响应正常
            if (null == msg)
            {
                //解析获得T类型
                List<SysUserModel> tmp = ServiceResponse.getResponseData(res);
                //翻译所需字段
                result = new SysUserConvertor().convert(tmp, SysUserModelEx.class);
            }
            return result;
        }
    }

    111

  • 相关阅读:
    python之路day10命名空间和作用域、函数嵌套,作用域链、闭包
    20200719 千锤百炼软工人第十四天
    20200720 千锤百炼软工人第十五 天
    JavaScript中基本数据类型和引用数据类型的区别
    js 经纬度坐标格式化 度分秒转度 度转度分秒
    export ,export default 和 import 区别 以及用法
    10月28日学习日志
    10月20日学习日志
    10月29日学习日志
    10月27日学习日志
  • 原文地址:https://www.cnblogs.com/taiyonghai/p/9373444.html
Copyright © 2011-2022 走看看