IDEA項目搭建十三——服務消費端與生產端通訊實現

1、簡介

以前已經完成了EurekaClient的服務生產者和Feign的服務消費者模塊的搭建,如今實現統一的通訊約定java

(1) 統一Request結構web

(2) 統一Response結構spring

(3) 統一Error通知json

2、代碼

一、建立統一請求對象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;
    }
}

二、建立統一響應對象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();
    }
}

三、建立統一響應結果枚舉,這裏能夠統一控制響應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
}

四、服務生產者這邊,統一入參和返回值都是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);
        }
    }
}

五、服務消費者這邊,使用了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);
}

六、得到基礎數據實體後,在頁面展示以前可能有些字段須要進行翻譯,好比狀態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對實體作了一次序列化很簡單api

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);
    }
}

 七、每個實體都繼承抽象基類,這樣就能夠直接使用轉換方法了

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) {

    }
}

八、下面就是頁面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;
    }
}

111app

相關文章
相關標籤/搜索