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

 

posted @ 2018-07-26 18:04  taiyonghai  阅读(577)  评论(0编辑  收藏  举报